roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = []; //config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         this.addColumn(config[i]);
7316         
7317     }
7318
7319     /**
7320      * The width of columns which have no width specified (defaults to 100)
7321      * @type Number
7322      */
7323     this.defaultWidth = 100;
7324
7325     /**
7326      * Default sortable of columns which have no sortable specified (defaults to false)
7327      * @type Boolean
7328      */
7329     this.defaultSortable = false;
7330
7331     this.addEvents({
7332         /**
7333              * @event widthchange
7334              * Fires when the width of a column changes.
7335              * @param {ColumnModel} this
7336              * @param {Number} columnIndex The column index
7337              * @param {Number} newWidth The new width
7338              */
7339             "widthchange": true,
7340         /**
7341              * @event headerchange
7342              * Fires when the text of a header changes.
7343              * @param {ColumnModel} this
7344              * @param {Number} columnIndex The column index
7345              * @param {Number} newText The new header text
7346              */
7347             "headerchange": true,
7348         /**
7349              * @event hiddenchange
7350              * Fires when a column is hidden or "unhidden".
7351              * @param {ColumnModel} this
7352              * @param {Number} columnIndex The column index
7353              * @param {Boolean} hidden true if hidden, false otherwise
7354              */
7355             "hiddenchange": true,
7356             /**
7357          * @event columnmoved
7358          * Fires when a column is moved.
7359          * @param {ColumnModel} this
7360          * @param {Number} oldIndex
7361          * @param {Number} newIndex
7362          */
7363         "columnmoved" : true,
7364         /**
7365          * @event columlockchange
7366          * Fires when a column's locked state is changed
7367          * @param {ColumnModel} this
7368          * @param {Number} colIndex
7369          * @param {Boolean} locked true if locked
7370          */
7371         "columnlockchange" : true
7372     });
7373     Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 };
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376     /**
7377      * @cfg {String} header The header text to display in the Grid view.
7378      */
7379     /**
7380      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382      * specified, the column's index is used as an index into the Record's data Array.
7383      */
7384     /**
7385      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7387      */
7388     /**
7389      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390      * Defaults to the value of the {@link #defaultSortable} property.
7391      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7392      */
7393     /**
7394      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7395      */
7396     /**
7397      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7398      */
7399     /**
7400      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7401      */
7402     /**
7403      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7404      */
7405     /**
7406      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7410      */
7411        /**
7412      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7413      */
7414     /**
7415      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7416      */
7417     /**
7418      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7419      */
7420     /**
7421      * @cfg {String} cursor (Optional)
7422      */
7423     /**
7424      * @cfg {String} tooltip (Optional)
7425      */
7426     /**
7427      * @cfg {Number} xs (Optional)
7428      */
7429     /**
7430      * @cfg {Number} sm (Optional)
7431      */
7432     /**
7433      * @cfg {Number} md (Optional)
7434      */
7435     /**
7436      * @cfg {Number} lg (Optional)
7437      */
7438     /**
7439      * Returns the id of the column at the specified index.
7440      * @param {Number} index The column index
7441      * @return {String} the id
7442      */
7443     getColumnId : function(index){
7444         return this.config[index].id;
7445     },
7446
7447     /**
7448      * Returns the column for a specified id.
7449      * @param {String} id The column id
7450      * @return {Object} the column
7451      */
7452     getColumnById : function(id){
7453         return this.lookup[id];
7454     },
7455
7456     
7457     /**
7458      * Returns the column Object for a specified dataIndex.
7459      * @param {String} dataIndex The column dataIndex
7460      * @return {Object|Boolean} the column or false if not found
7461      */
7462     getColumnByDataIndex: function(dataIndex){
7463         var index = this.findColumnIndex(dataIndex);
7464         return index > -1 ? this.config[index] : false;
7465     },
7466     
7467     /**
7468      * Returns the index for a specified column id.
7469      * @param {String} id The column id
7470      * @return {Number} the index, or -1 if not found
7471      */
7472     getIndexById : function(id){
7473         for(var i = 0, len = this.config.length; i < len; i++){
7474             if(this.config[i].id == id){
7475                 return i;
7476             }
7477         }
7478         return -1;
7479     },
7480     
7481     /**
7482      * Returns the index for a specified column dataIndex.
7483      * @param {String} dataIndex The column dataIndex
7484      * @return {Number} the index, or -1 if not found
7485      */
7486     
7487     findColumnIndex : function(dataIndex){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].dataIndex == dataIndex){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     
7497     moveColumn : function(oldIndex, newIndex){
7498         var c = this.config[oldIndex];
7499         this.config.splice(oldIndex, 1);
7500         this.config.splice(newIndex, 0, c);
7501         this.dataMap = null;
7502         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7503     },
7504
7505     isLocked : function(colIndex){
7506         return this.config[colIndex].locked === true;
7507     },
7508
7509     setLocked : function(colIndex, value, suppressEvent){
7510         if(this.isLocked(colIndex) == value){
7511             return;
7512         }
7513         this.config[colIndex].locked = value;
7514         if(!suppressEvent){
7515             this.fireEvent("columnlockchange", this, colIndex, value);
7516         }
7517     },
7518
7519     getTotalLockedWidth : function(){
7520         var totalWidth = 0;
7521         for(var i = 0; i < this.config.length; i++){
7522             if(this.isLocked(i) && !this.isHidden(i)){
7523                 this.totalWidth += this.getColumnWidth(i);
7524             }
7525         }
7526         return totalWidth;
7527     },
7528
7529     getLockedCount : function(){
7530         for(var i = 0, len = this.config.length; i < len; i++){
7531             if(!this.isLocked(i)){
7532                 return i;
7533             }
7534         }
7535         
7536         return this.config.length;
7537     },
7538
7539     /**
7540      * Returns the number of columns.
7541      * @return {Number}
7542      */
7543     getColumnCount : function(visibleOnly){
7544         if(visibleOnly === true){
7545             var c = 0;
7546             for(var i = 0, len = this.config.length; i < len; i++){
7547                 if(!this.isHidden(i)){
7548                     c++;
7549                 }
7550             }
7551             return c;
7552         }
7553         return this.config.length;
7554     },
7555
7556     /**
7557      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558      * @param {Function} fn
7559      * @param {Object} scope (optional)
7560      * @return {Array} result
7561      */
7562     getColumnsBy : function(fn, scope){
7563         var r = [];
7564         for(var i = 0, len = this.config.length; i < len; i++){
7565             var c = this.config[i];
7566             if(fn.call(scope||this, c, i) === true){
7567                 r[r.length] = c;
7568             }
7569         }
7570         return r;
7571     },
7572
7573     /**
7574      * Returns true if the specified column is sortable.
7575      * @param {Number} col The column index
7576      * @return {Boolean}
7577      */
7578     isSortable : function(col){
7579         if(typeof this.config[col].sortable == "undefined"){
7580             return this.defaultSortable;
7581         }
7582         return this.config[col].sortable;
7583     },
7584
7585     /**
7586      * Returns the rendering (formatting) function defined for the column.
7587      * @param {Number} col The column index.
7588      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589      */
7590     getRenderer : function(col){
7591         if(!this.config[col].renderer){
7592             return Roo.grid.ColumnModel.defaultRenderer;
7593         }
7594         return this.config[col].renderer;
7595     },
7596
7597     /**
7598      * Sets the rendering (formatting) function for a column.
7599      * @param {Number} col The column index
7600      * @param {Function} fn The function to use to process the cell's raw data
7601      * to return HTML markup for the grid view. The render function is called with
7602      * the following parameters:<ul>
7603      * <li>Data value.</li>
7604      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605      * <li>css A CSS style string to apply to the table cell.</li>
7606      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608      * <li>Row index</li>
7609      * <li>Column index</li>
7610      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611      */
7612     setRenderer : function(col, fn){
7613         this.config[col].renderer = fn;
7614     },
7615
7616     /**
7617      * Returns the width for the specified column.
7618      * @param {Number} col The column index
7619      * @return {Number}
7620      */
7621     getColumnWidth : function(col){
7622         return this.config[col].width * 1 || this.defaultWidth;
7623     },
7624
7625     /**
7626      * Sets the width for a column.
7627      * @param {Number} col The column index
7628      * @param {Number} width The new width
7629      */
7630     setColumnWidth : function(col, width, suppressEvent){
7631         this.config[col].width = width;
7632         this.totalWidth = null;
7633         if(!suppressEvent){
7634              this.fireEvent("widthchange", this, col, width);
7635         }
7636     },
7637
7638     /**
7639      * Returns the total width of all columns.
7640      * @param {Boolean} includeHidden True to include hidden column widths
7641      * @return {Number}
7642      */
7643     getTotalWidth : function(includeHidden){
7644         if(!this.totalWidth){
7645             this.totalWidth = 0;
7646             for(var i = 0, len = this.config.length; i < len; i++){
7647                 if(includeHidden || !this.isHidden(i)){
7648                     this.totalWidth += this.getColumnWidth(i);
7649                 }
7650             }
7651         }
7652         return this.totalWidth;
7653     },
7654
7655     /**
7656      * Returns the header for the specified column.
7657      * @param {Number} col The column index
7658      * @return {String}
7659      */
7660     getColumnHeader : function(col){
7661         return this.config[col].header;
7662     },
7663
7664     /**
7665      * Sets the header for a column.
7666      * @param {Number} col The column index
7667      * @param {String} header The new header
7668      */
7669     setColumnHeader : function(col, header){
7670         this.config[col].header = header;
7671         this.fireEvent("headerchange", this, col, header);
7672     },
7673
7674     /**
7675      * Returns the tooltip for the specified column.
7676      * @param {Number} col The column index
7677      * @return {String}
7678      */
7679     getColumnTooltip : function(col){
7680             return this.config[col].tooltip;
7681     },
7682     /**
7683      * Sets the tooltip for a column.
7684      * @param {Number} col The column index
7685      * @param {String} tooltip The new tooltip
7686      */
7687     setColumnTooltip : function(col, tooltip){
7688             this.config[col].tooltip = tooltip;
7689     },
7690
7691     /**
7692      * Returns the dataIndex for the specified column.
7693      * @param {Number} col The column index
7694      * @return {Number}
7695      */
7696     getDataIndex : function(col){
7697         return this.config[col].dataIndex;
7698     },
7699
7700     /**
7701      * Sets the dataIndex for a column.
7702      * @param {Number} col The column index
7703      * @param {Number} dataIndex The new dataIndex
7704      */
7705     setDataIndex : function(col, dataIndex){
7706         this.config[col].dataIndex = dataIndex;
7707     },
7708
7709     
7710     
7711     /**
7712      * Returns true if the cell is editable.
7713      * @param {Number} colIndex The column index
7714      * @param {Number} rowIndex The row index - this is nto actually used..?
7715      * @return {Boolean}
7716      */
7717     isCellEditable : function(colIndex, rowIndex){
7718         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7719     },
7720
7721     /**
7722      * Returns the editor defined for the cell/column.
7723      * return false or null to disable editing.
7724      * @param {Number} colIndex The column index
7725      * @param {Number} rowIndex The row index
7726      * @return {Object}
7727      */
7728     getCellEditor : function(colIndex, rowIndex){
7729         return this.config[colIndex].editor;
7730     },
7731
7732     /**
7733      * Sets if a column is editable.
7734      * @param {Number} col The column index
7735      * @param {Boolean} editable True if the column is editable
7736      */
7737     setEditable : function(col, editable){
7738         this.config[col].editable = editable;
7739     },
7740
7741
7742     /**
7743      * Returns true if the column is hidden.
7744      * @param {Number} colIndex The column index
7745      * @return {Boolean}
7746      */
7747     isHidden : function(colIndex){
7748         return this.config[colIndex].hidden;
7749     },
7750
7751
7752     /**
7753      * Returns true if the column width cannot be changed
7754      */
7755     isFixed : function(colIndex){
7756         return this.config[colIndex].fixed;
7757     },
7758
7759     /**
7760      * Returns true if the column can be resized
7761      * @return {Boolean}
7762      */
7763     isResizable : function(colIndex){
7764         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7765     },
7766     /**
7767      * Sets if a column is hidden.
7768      * @param {Number} colIndex The column index
7769      * @param {Boolean} hidden True if the column is hidden
7770      */
7771     setHidden : function(colIndex, hidden){
7772         this.config[colIndex].hidden = hidden;
7773         this.totalWidth = null;
7774         this.fireEvent("hiddenchange", this, colIndex, hidden);
7775     },
7776
7777     /**
7778      * Sets the editor for a column.
7779      * @param {Number} col The column index
7780      * @param {Object} editor The editor object
7781      */
7782     setEditor : function(col, editor){
7783         this.config[col].editor = editor;
7784     },
7785     /**
7786      * Add a column (experimental...) - defaults to adding to the end..
7787      * @param {Object} config 
7788     */
7789     addColumn : function(c)
7790     {
7791     
7792         var i = this.config.length;
7793         this.config[i] = c;
7794         
7795         if(typeof c.dataIndex == "undefined"){
7796             c.dataIndex = i;
7797         }
7798         if(typeof c.renderer == "string"){
7799             c.renderer = Roo.util.Format[c.renderer];
7800         }
7801         if(typeof c.id == "undefined"){
7802             c.id = Roo.id();
7803         }
7804         if(c.editor && c.editor.xtype){
7805             c.editor  = Roo.factory(c.editor, Roo.grid);
7806         }
7807         if(c.editor && c.editor.isFormField){
7808             c.editor = new Roo.grid.GridEditor(c.editor);
7809         }
7810         this.lookup[c.id] = c;
7811     }
7812     
7813 });
7814
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7816 {
7817     if(typeof value == "object") {
7818         return value;
7819     }
7820         if(typeof value == "string" && value.length < 1){
7821             return "&#160;";
7822         }
7823     
7824         return String.format("{0}", value);
7825 };
7826
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7829 /*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839  
7840 /**
7841  * @class Roo.LoadMask
7842  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7843  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7845  * element's UpdateManager load indicator and will be destroyed after the initial load.
7846  * @constructor
7847  * Create a new LoadMask
7848  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849  * @param {Object} config The config object
7850  */
7851 Roo.LoadMask = function(el, config){
7852     this.el = Roo.get(el);
7853     Roo.apply(this, config);
7854     if(this.store){
7855         this.store.on('beforeload', this.onBeforeLoad, this);
7856         this.store.on('load', this.onLoad, this);
7857         this.store.on('loadexception', this.onLoadException, this);
7858         this.removeMask = false;
7859     }else{
7860         var um = this.el.getUpdateManager();
7861         um.showLoadIndicator = false; // disable the default indicator
7862         um.on('beforeupdate', this.onBeforeLoad, this);
7863         um.on('update', this.onLoad, this);
7864         um.on('failure', this.onLoad, this);
7865         this.removeMask = true;
7866     }
7867 };
7868
7869 Roo.LoadMask.prototype = {
7870     /**
7871      * @cfg {Boolean} removeMask
7872      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7874      */
7875     /**
7876      * @cfg {String} msg
7877      * The text to display in a centered loading message box (defaults to 'Loading...')
7878      */
7879     msg : 'Loading...',
7880     /**
7881      * @cfg {String} msgCls
7882      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7883      */
7884     msgCls : 'x-mask-loading',
7885
7886     /**
7887      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7888      * @type Boolean
7889      */
7890     disabled: false,
7891
7892     /**
7893      * Disables the mask to prevent it from being displayed
7894      */
7895     disable : function(){
7896        this.disabled = true;
7897     },
7898
7899     /**
7900      * Enables the mask so that it can be displayed
7901      */
7902     enable : function(){
7903         this.disabled = false;
7904     },
7905     
7906     onLoadException : function()
7907     {
7908         Roo.log(arguments);
7909         
7910         if (typeof(arguments[3]) != 'undefined') {
7911             Roo.MessageBox.alert("Error loading",arguments[3]);
7912         } 
7913         /*
7914         try {
7915             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7917             }   
7918         } catch(e) {
7919             
7920         }
7921         */
7922     
7923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7924     },
7925     // private
7926     onLoad : function()
7927     {
7928         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7929     },
7930
7931     // private
7932     onBeforeLoad : function(){
7933         if(!this.disabled){
7934             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7935         }
7936     },
7937
7938     // private
7939     destroy : function(){
7940         if(this.store){
7941             this.store.un('beforeload', this.onBeforeLoad, this);
7942             this.store.un('load', this.onLoad, this);
7943             this.store.un('loadexception', this.onLoadException, this);
7944         }else{
7945             var um = this.el.getUpdateManager();
7946             um.un('beforeupdate', this.onBeforeLoad, this);
7947             um.un('update', this.onLoad, this);
7948             um.un('failure', this.onLoad, this);
7949         }
7950     }
7951 };/*
7952  * - LGPL
7953  *
7954  * table
7955  * 
7956  */
7957
7958 /**
7959  * @class Roo.bootstrap.Table
7960  * @extends Roo.bootstrap.Component
7961  * Bootstrap Table class
7962  * @cfg {String} cls table class
7963  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964  * @cfg {String} bgcolor Specifies the background color for a table
7965  * @cfg {Number} border Specifies whether the table cells should have borders or not
7966  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967  * @cfg {Number} cellspacing Specifies the space between cells
7968  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970  * @cfg {String} sortable Specifies that the table should be sortable
7971  * @cfg {String} summary Specifies a summary of the content of a table
7972  * @cfg {Number} width Specifies the width of a table
7973  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7974  * 
7975  * @cfg {boolean} striped Should the rows be alternative striped
7976  * @cfg {boolean} bordered Add borders to the table
7977  * @cfg {boolean} hover Add hover highlighting
7978  * @cfg {boolean} condensed Format condensed
7979  * @cfg {boolean} responsive Format condensed
7980  * @cfg {Boolean} loadMask (true|false) default false
7981  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983  * @cfg {Boolean} rowSelection (true|false) default false
7984  * @cfg {Boolean} cellSelection (true|false) default false
7985  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7987  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7988  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7989  
7990  * 
7991  * @constructor
7992  * Create a new Table
7993  * @param {Object} config The config object
7994  */
7995
7996 Roo.bootstrap.Table = function(config){
7997     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7998     
7999   
8000     
8001     // BC...
8002     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8006     
8007     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8008     if (this.sm) {
8009         this.sm.grid = this;
8010         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011         this.sm = this.selModel;
8012         this.sm.xmodule = this.xmodule || false;
8013     }
8014     
8015     if (this.cm && typeof(this.cm.config) == 'undefined') {
8016         this.colModel = new Roo.grid.ColumnModel(this.cm);
8017         this.cm = this.colModel;
8018         this.cm.xmodule = this.xmodule || false;
8019     }
8020     if (this.store) {
8021         this.store= Roo.factory(this.store, Roo.data);
8022         this.ds = this.store;
8023         this.ds.xmodule = this.xmodule || false;
8024          
8025     }
8026     if (this.footer && this.store) {
8027         this.footer.dataSource = this.ds;
8028         this.footer = Roo.factory(this.footer);
8029     }
8030     
8031     /** @private */
8032     this.addEvents({
8033         /**
8034          * @event cellclick
8035          * Fires when a cell is clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Number} columnIndex
8040          * @param {Roo.EventObject} e
8041          */
8042         "cellclick" : true,
8043         /**
8044          * @event celldblclick
8045          * Fires when a cell is double clicked
8046          * @param {Roo.bootstrap.Table} this
8047          * @param {Roo.Element} el
8048          * @param {Number} rowIndex
8049          * @param {Number} columnIndex
8050          * @param {Roo.EventObject} e
8051          */
8052         "celldblclick" : true,
8053         /**
8054          * @event rowclick
8055          * Fires when a row is clicked
8056          * @param {Roo.bootstrap.Table} this
8057          * @param {Roo.Element} el
8058          * @param {Number} rowIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "rowclick" : true,
8062         /**
8063          * @event rowdblclick
8064          * Fires when a row is double clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Roo.Element} el
8067          * @param {Number} rowIndex
8068          * @param {Roo.EventObject} e
8069          */
8070         "rowdblclick" : true,
8071         /**
8072          * @event mouseover
8073          * Fires when a mouseover occur
8074          * @param {Roo.bootstrap.Table} this
8075          * @param {Roo.Element} el
8076          * @param {Number} rowIndex
8077          * @param {Number} columnIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "mouseover" : true,
8081         /**
8082          * @event mouseout
8083          * Fires when a mouseout occur
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Roo.Element} el
8086          * @param {Number} rowIndex
8087          * @param {Number} columnIndex
8088          * @param {Roo.EventObject} e
8089          */
8090         "mouseout" : true,
8091         /**
8092          * @event rowclass
8093          * Fires when a row is rendered, so you can change add a style to it.
8094          * @param {Roo.bootstrap.Table} this
8095          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8096          */
8097         'rowclass' : true,
8098           /**
8099          * @event rowsrendered
8100          * Fires when all the  rows have been rendered
8101          * @param {Roo.bootstrap.Table} this
8102          */
8103         'rowsrendered' : true,
8104         /**
8105          * @event contextmenu
8106          * The raw contextmenu event for the entire grid.
8107          * @param {Roo.EventObject} e
8108          */
8109         "contextmenu" : true,
8110         /**
8111          * @event rowcontextmenu
8112          * Fires when a row is right clicked
8113          * @param {Roo.bootstrap.Table} this
8114          * @param {Number} rowIndex
8115          * @param {Roo.EventObject} e
8116          */
8117         "rowcontextmenu" : true,
8118         /**
8119          * @event cellcontextmenu
8120          * Fires when a cell is right clicked
8121          * @param {Roo.bootstrap.Table} this
8122          * @param {Number} rowIndex
8123          * @param {Number} cellIndex
8124          * @param {Roo.EventObject} e
8125          */
8126          "cellcontextmenu" : true,
8127          /**
8128          * @event headercontextmenu
8129          * Fires when a header is right clicked
8130          * @param {Roo.bootstrap.Table} this
8131          * @param {Number} columnIndex
8132          * @param {Roo.EventObject} e
8133          */
8134         "headercontextmenu" : true
8135     });
8136 };
8137
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8139     
8140     cls: false,
8141     align: false,
8142     bgcolor: false,
8143     border: false,
8144     cellpadding: false,
8145     cellspacing: false,
8146     frame: false,
8147     rules: false,
8148     sortable: false,
8149     summary: false,
8150     width: false,
8151     striped : false,
8152     scrollBody : false,
8153     bordered: false,
8154     hover:  false,
8155     condensed : false,
8156     responsive : false,
8157     sm : false,
8158     cm : false,
8159     store : false,
8160     loadMask : false,
8161     footerShow : true,
8162     headerShow : true,
8163   
8164     rowSelection : false,
8165     cellSelection : false,
8166     layout : false,
8167     
8168     // Roo.Element - the tbody
8169     mainBody: false,
8170     // Roo.Element - thead element
8171     mainHead: false,
8172     
8173     container: false, // used by gridpanel...
8174     
8175     lazyLoad : false,
8176     
8177     CSS : Roo.util.CSS,
8178     
8179     auto_hide_footer : false,
8180     
8181     getAutoCreate : function()
8182     {
8183         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8184         
8185         cfg = {
8186             tag: 'table',
8187             cls : 'table',
8188             cn : []
8189         };
8190         if (this.scrollBody) {
8191             cfg.cls += ' table-body-fixed';
8192         }    
8193         if (this.striped) {
8194             cfg.cls += ' table-striped';
8195         }
8196         
8197         if (this.hover) {
8198             cfg.cls += ' table-hover';
8199         }
8200         if (this.bordered) {
8201             cfg.cls += ' table-bordered';
8202         }
8203         if (this.condensed) {
8204             cfg.cls += ' table-condensed';
8205         }
8206         if (this.responsive) {
8207             cfg.cls += ' table-responsive';
8208         }
8209         
8210         if (this.cls) {
8211             cfg.cls+=  ' ' +this.cls;
8212         }
8213         
8214         // this lot should be simplifed...
8215         var _t = this;
8216         var cp = [
8217             'align',
8218             'bgcolor',
8219             'border',
8220             'cellpadding',
8221             'cellspacing',
8222             'frame',
8223             'rules',
8224             'sortable',
8225             'summary',
8226             'width'
8227         ].forEach(function(k) {
8228             if (_t[k]) {
8229                 cfg[k] = _t[k];
8230             }
8231         });
8232         
8233         
8234         if (this.layout) {
8235             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8236         }
8237         
8238         if(this.store || this.cm){
8239             if(this.headerShow){
8240                 cfg.cn.push(this.renderHeader());
8241             }
8242             
8243             cfg.cn.push(this.renderBody());
8244             
8245             if(this.footerShow){
8246                 cfg.cn.push(this.renderFooter());
8247             }
8248             // where does this come from?
8249             //cfg.cls+=  ' TableGrid';
8250         }
8251         
8252         return { cn : [ cfg ] };
8253     },
8254     
8255     initEvents : function()
8256     {   
8257         if(!this.store || !this.cm){
8258             return;
8259         }
8260         if (this.selModel) {
8261             this.selModel.initEvents();
8262         }
8263         
8264         
8265         //Roo.log('initEvents with ds!!!!');
8266         
8267         this.mainBody = this.el.select('tbody', true).first();
8268         this.mainHead = this.el.select('thead', true).first();
8269         this.mainFoot = this.el.select('tfoot', true).first();
8270         
8271         
8272         
8273         var _this = this;
8274         
8275         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8276             e.on('click', _this.sort, _this);
8277         });
8278         
8279         this.mainBody.on("click", this.onClick, this);
8280         this.mainBody.on("dblclick", this.onDblClick, this);
8281         
8282         // why is this done????? = it breaks dialogs??
8283         //this.parent().el.setStyle('position', 'relative');
8284         
8285         
8286         if (this.footer) {
8287             this.footer.parentId = this.id;
8288             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8289             
8290             if(this.lazyLoad){
8291                 this.el.select('tfoot tr td').first().addClass('hide');
8292             }
8293         } 
8294         
8295         if(this.loadMask) {
8296             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8297         }
8298         
8299         this.store.on('load', this.onLoad, this);
8300         this.store.on('beforeload', this.onBeforeLoad, this);
8301         this.store.on('update', this.onUpdate, this);
8302         this.store.on('add', this.onAdd, this);
8303         this.store.on("clear", this.clear, this);
8304         
8305         this.el.on("contextmenu", this.onContextMenu, this);
8306         
8307         this.mainBody.on('scroll', this.onBodyScroll, this);
8308         
8309         this.cm.on("headerchange", this.onHeaderChange, this);
8310         
8311         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8312         
8313     },
8314     
8315     onContextMenu : function(e, t)
8316     {
8317         this.processEvent("contextmenu", e);
8318     },
8319     
8320     processEvent : function(name, e)
8321     {
8322         if (name != 'touchstart' ) {
8323             this.fireEvent(name, e);    
8324         }
8325         
8326         var t = e.getTarget();
8327         
8328         var cell = Roo.get(t);
8329         
8330         if(!cell){
8331             return;
8332         }
8333         
8334         if(cell.findParent('tfoot', false, true)){
8335             return;
8336         }
8337         
8338         if(cell.findParent('thead', false, true)){
8339             
8340             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8341                 cell = Roo.get(t).findParent('th', false, true);
8342                 if (!cell) {
8343                     Roo.log("failed to find th in thead?");
8344                     Roo.log(e.getTarget());
8345                     return;
8346                 }
8347             }
8348             
8349             var cellIndex = cell.dom.cellIndex;
8350             
8351             var ename = name == 'touchstart' ? 'click' : name;
8352             this.fireEvent("header" + ename, this, cellIndex, e);
8353             
8354             return;
8355         }
8356         
8357         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8358             cell = Roo.get(t).findParent('td', false, true);
8359             if (!cell) {
8360                 Roo.log("failed to find th in tbody?");
8361                 Roo.log(e.getTarget());
8362                 return;
8363             }
8364         }
8365         
8366         var row = cell.findParent('tr', false, true);
8367         var cellIndex = cell.dom.cellIndex;
8368         var rowIndex = row.dom.rowIndex - 1;
8369         
8370         if(row !== false){
8371             
8372             this.fireEvent("row" + name, this, rowIndex, e);
8373             
8374             if(cell !== false){
8375             
8376                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8377             }
8378         }
8379         
8380     },
8381     
8382     onMouseover : function(e, el)
8383     {
8384         var cell = Roo.get(el);
8385         
8386         if(!cell){
8387             return;
8388         }
8389         
8390         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8391             cell = cell.findParent('td', false, true);
8392         }
8393         
8394         var row = cell.findParent('tr', false, true);
8395         var cellIndex = cell.dom.cellIndex;
8396         var rowIndex = row.dom.rowIndex - 1; // start from 0
8397         
8398         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8399         
8400     },
8401     
8402     onMouseout : function(e, el)
8403     {
8404         var cell = Roo.get(el);
8405         
8406         if(!cell){
8407             return;
8408         }
8409         
8410         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8411             cell = cell.findParent('td', false, true);
8412         }
8413         
8414         var row = cell.findParent('tr', false, true);
8415         var cellIndex = cell.dom.cellIndex;
8416         var rowIndex = row.dom.rowIndex - 1; // start from 0
8417         
8418         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8419         
8420     },
8421     
8422     onClick : function(e, el)
8423     {
8424         var cell = Roo.get(el);
8425         
8426         if(!cell || (!this.cellSelection && !this.rowSelection)){
8427             return;
8428         }
8429         
8430         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8431             cell = cell.findParent('td', false, true);
8432         }
8433         
8434         if(!cell || typeof(cell) == 'undefined'){
8435             return;
8436         }
8437         
8438         var row = cell.findParent('tr', false, true);
8439         
8440         if(!row || typeof(row) == 'undefined'){
8441             return;
8442         }
8443         
8444         var cellIndex = cell.dom.cellIndex;
8445         var rowIndex = this.getRowIndex(row);
8446         
8447         // why??? - should these not be based on SelectionModel?
8448         if(this.cellSelection){
8449             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8450         }
8451         
8452         if(this.rowSelection){
8453             this.fireEvent('rowclick', this, row, rowIndex, e);
8454         }
8455         
8456         
8457     },
8458         
8459     onDblClick : function(e,el)
8460     {
8461         var cell = Roo.get(el);
8462         
8463         if(!cell || (!this.cellSelection && !this.rowSelection)){
8464             return;
8465         }
8466         
8467         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8468             cell = cell.findParent('td', false, true);
8469         }
8470         
8471         if(!cell || typeof(cell) == 'undefined'){
8472             return;
8473         }
8474         
8475         var row = cell.findParent('tr', false, true);
8476         
8477         if(!row || typeof(row) == 'undefined'){
8478             return;
8479         }
8480         
8481         var cellIndex = cell.dom.cellIndex;
8482         var rowIndex = this.getRowIndex(row);
8483         
8484         if(this.cellSelection){
8485             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8486         }
8487         
8488         if(this.rowSelection){
8489             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8490         }
8491     },
8492     
8493     sort : function(e,el)
8494     {
8495         var col = Roo.get(el);
8496         
8497         if(!col.hasClass('sortable')){
8498             return;
8499         }
8500         
8501         var sort = col.attr('sort');
8502         var dir = 'ASC';
8503         
8504         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8505             dir = 'DESC';
8506         }
8507         
8508         this.store.sortInfo = {field : sort, direction : dir};
8509         
8510         if (this.footer) {
8511             Roo.log("calling footer first");
8512             this.footer.onClick('first');
8513         } else {
8514         
8515             this.store.load({ params : { start : 0 } });
8516         }
8517     },
8518     
8519     renderHeader : function()
8520     {
8521         var header = {
8522             tag: 'thead',
8523             cn : []
8524         };
8525         
8526         var cm = this.cm;
8527         this.totalWidth = 0;
8528         
8529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530             
8531             var config = cm.config[i];
8532             
8533             var c = {
8534                 tag: 'th',
8535                 cls : 'x-hcol-' + i,
8536                 style : '',
8537                 html: cm.getColumnHeader(i)
8538             };
8539             
8540             var hh = '';
8541             
8542             if(typeof(config.sortable) != 'undefined' && config.sortable){
8543                 c.cls = 'sortable';
8544                 c.html = '<i class="fa"></i>' + c.html;
8545             }
8546             
8547             // could use BS4 hidden-..-down 
8548             
8549             if(typeof(config.lgHeader) != 'undefined'){
8550                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8551             }
8552             
8553             if(typeof(config.mdHeader) != 'undefined'){
8554                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8555             }
8556             
8557             if(typeof(config.smHeader) != 'undefined'){
8558                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8559             }
8560             
8561             if(typeof(config.xsHeader) != 'undefined'){
8562                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8563             }
8564             
8565             if(hh.length){
8566                 c.html = hh;
8567             }
8568             
8569             if(typeof(config.tooltip) != 'undefined'){
8570                 c.tooltip = config.tooltip;
8571             }
8572             
8573             if(typeof(config.colspan) != 'undefined'){
8574                 c.colspan = config.colspan;
8575             }
8576             
8577             if(typeof(config.hidden) != 'undefined' && config.hidden){
8578                 c.style += ' display:none;';
8579             }
8580             
8581             if(typeof(config.dataIndex) != 'undefined'){
8582                 c.sort = config.dataIndex;
8583             }
8584             
8585            
8586             
8587             if(typeof(config.align) != 'undefined' && config.align.length){
8588                 c.style += ' text-align:' + config.align + ';';
8589             }
8590             
8591             if(typeof(config.width) != 'undefined'){
8592                 c.style += ' width:' + config.width + 'px;';
8593                 this.totalWidth += config.width;
8594             } else {
8595                 this.totalWidth += 100; // assume minimum of 100 per column?
8596             }
8597             
8598             if(typeof(config.cls) != 'undefined'){
8599                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8600             }
8601             
8602             ['xs','sm','md','lg'].map(function(size){
8603                 
8604                 if(typeof(config[size]) == 'undefined'){
8605                     return;
8606                 }
8607                  
8608                 if (!config[size]) { // 0 = hidden
8609                     // BS 4 '0' is treated as hide that column and below.
8610                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8611                     return;
8612                 }
8613                 
8614                 c.cls += ' col-' + size + '-' + config[size] + (
8615                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8616                 );
8617                 
8618                 
8619             });
8620             
8621             header.cn.push(c)
8622         }
8623         
8624         return header;
8625     },
8626     
8627     renderBody : function()
8628     {
8629         var body = {
8630             tag: 'tbody',
8631             cn : [
8632                 {
8633                     tag: 'tr',
8634                     cn : [
8635                         {
8636                             tag : 'td',
8637                             colspan :  this.cm.getColumnCount()
8638                         }
8639                     ]
8640                 }
8641             ]
8642         };
8643         
8644         return body;
8645     },
8646     
8647     renderFooter : function()
8648     {
8649         var footer = {
8650             tag: 'tfoot',
8651             cn : [
8652                 {
8653                     tag: 'tr',
8654                     cn : [
8655                         {
8656                             tag : 'td',
8657                             colspan :  this.cm.getColumnCount()
8658                         }
8659                     ]
8660                 }
8661             ]
8662         };
8663         
8664         return footer;
8665     },
8666     
8667     
8668     
8669     onLoad : function()
8670     {
8671 //        Roo.log('ds onload');
8672         this.clear();
8673         
8674         var _this = this;
8675         var cm = this.cm;
8676         var ds = this.store;
8677         
8678         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8679             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8680             if (_this.store.sortInfo) {
8681                     
8682                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8683                     e.select('i', true).addClass(['fa-arrow-up']);
8684                 }
8685                 
8686                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8687                     e.select('i', true).addClass(['fa-arrow-down']);
8688                 }
8689             }
8690         });
8691         
8692         var tbody =  this.mainBody;
8693               
8694         if(ds.getCount() > 0){
8695             ds.data.each(function(d,rowIndex){
8696                 var row =  this.renderRow(cm, ds, rowIndex);
8697                 
8698                 tbody.createChild(row);
8699                 
8700                 var _this = this;
8701                 
8702                 if(row.cellObjects.length){
8703                     Roo.each(row.cellObjects, function(r){
8704                         _this.renderCellObject(r);
8705                     })
8706                 }
8707                 
8708             }, this);
8709         }
8710         
8711         var tfoot = this.el.select('tfoot', true).first();
8712         
8713         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8714             
8715             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8716             
8717             var total = this.ds.getTotalCount();
8718             
8719             if(this.footer.pageSize < total){
8720                 this.mainFoot.show();
8721             }
8722         }
8723         
8724         Roo.each(this.el.select('tbody td', true).elements, function(e){
8725             e.on('mouseover', _this.onMouseover, _this);
8726         });
8727         
8728         Roo.each(this.el.select('tbody td', true).elements, function(e){
8729             e.on('mouseout', _this.onMouseout, _this);
8730         });
8731         this.fireEvent('rowsrendered', this);
8732         
8733         this.autoSize();
8734     },
8735     
8736     
8737     onUpdate : function(ds,record)
8738     {
8739         this.refreshRow(record);
8740         this.autoSize();
8741     },
8742     
8743     onRemove : function(ds, record, index, isUpdate){
8744         if(isUpdate !== true){
8745             this.fireEvent("beforerowremoved", this, index, record);
8746         }
8747         var bt = this.mainBody.dom;
8748         
8749         var rows = this.el.select('tbody > tr', true).elements;
8750         
8751         if(typeof(rows[index]) != 'undefined'){
8752             bt.removeChild(rows[index].dom);
8753         }
8754         
8755 //        if(bt.rows[index]){
8756 //            bt.removeChild(bt.rows[index]);
8757 //        }
8758         
8759         if(isUpdate !== true){
8760             //this.stripeRows(index);
8761             //this.syncRowHeights(index, index);
8762             //this.layout();
8763             this.fireEvent("rowremoved", this, index, record);
8764         }
8765     },
8766     
8767     onAdd : function(ds, records, rowIndex)
8768     {
8769         //Roo.log('on Add called');
8770         // - note this does not handle multiple adding very well..
8771         var bt = this.mainBody.dom;
8772         for (var i =0 ; i < records.length;i++) {
8773             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8774             //Roo.log(records[i]);
8775             //Roo.log(this.store.getAt(rowIndex+i));
8776             this.insertRow(this.store, rowIndex + i, false);
8777             return;
8778         }
8779         
8780     },
8781     
8782     
8783     refreshRow : function(record){
8784         var ds = this.store, index;
8785         if(typeof record == 'number'){
8786             index = record;
8787             record = ds.getAt(index);
8788         }else{
8789             index = ds.indexOf(record);
8790             if (index < 0) {
8791                 return; // should not happen - but seems to 
8792             }
8793         }
8794         this.insertRow(ds, index, true);
8795         this.autoSize();
8796         this.onRemove(ds, record, index+1, true);
8797         this.autoSize();
8798         //this.syncRowHeights(index, index);
8799         //this.layout();
8800         this.fireEvent("rowupdated", this, index, record);
8801     },
8802     
8803     insertRow : function(dm, rowIndex, isUpdate){
8804         
8805         if(!isUpdate){
8806             this.fireEvent("beforerowsinserted", this, rowIndex);
8807         }
8808             //var s = this.getScrollState();
8809         var row = this.renderRow(this.cm, this.store, rowIndex);
8810         // insert before rowIndex..
8811         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8812         
8813         var _this = this;
8814                 
8815         if(row.cellObjects.length){
8816             Roo.each(row.cellObjects, function(r){
8817                 _this.renderCellObject(r);
8818             })
8819         }
8820             
8821         if(!isUpdate){
8822             this.fireEvent("rowsinserted", this, rowIndex);
8823             //this.syncRowHeights(firstRow, lastRow);
8824             //this.stripeRows(firstRow);
8825             //this.layout();
8826         }
8827         
8828     },
8829     
8830     
8831     getRowDom : function(rowIndex)
8832     {
8833         var rows = this.el.select('tbody > tr', true).elements;
8834         
8835         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8836         
8837     },
8838     // returns the object tree for a tr..
8839   
8840     
8841     renderRow : function(cm, ds, rowIndex) 
8842     {
8843         var d = ds.getAt(rowIndex);
8844         
8845         var row = {
8846             tag : 'tr',
8847             cls : 'x-row-' + rowIndex,
8848             cn : []
8849         };
8850             
8851         var cellObjects = [];
8852         
8853         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8854             var config = cm.config[i];
8855             
8856             var renderer = cm.getRenderer(i);
8857             var value = '';
8858             var id = false;
8859             
8860             if(typeof(renderer) !== 'undefined'){
8861                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8862             }
8863             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8864             // and are rendered into the cells after the row is rendered - using the id for the element.
8865             
8866             if(typeof(value) === 'object'){
8867                 id = Roo.id();
8868                 cellObjects.push({
8869                     container : id,
8870                     cfg : value 
8871                 })
8872             }
8873             
8874             var rowcfg = {
8875                 record: d,
8876                 rowIndex : rowIndex,
8877                 colIndex : i,
8878                 rowClass : ''
8879             };
8880
8881             this.fireEvent('rowclass', this, rowcfg);
8882             
8883             var td = {
8884                 tag: 'td',
8885                 cls : rowcfg.rowClass + ' x-col-' + i,
8886                 style: '',
8887                 html: (typeof(value) === 'object') ? '' : value
8888             };
8889             
8890             if (id) {
8891                 td.id = id;
8892             }
8893             
8894             if(typeof(config.colspan) != 'undefined'){
8895                 td.colspan = config.colspan;
8896             }
8897             
8898             if(typeof(config.hidden) != 'undefined' && config.hidden){
8899                 td.style += ' display:none;';
8900             }
8901             
8902             if(typeof(config.align) != 'undefined' && config.align.length){
8903                 td.style += ' text-align:' + config.align + ';';
8904             }
8905             if(typeof(config.valign) != 'undefined' && config.valign.length){
8906                 td.style += ' vertical-align:' + config.valign + ';';
8907             }
8908             
8909             if(typeof(config.width) != 'undefined'){
8910                 td.style += ' width:' +  config.width + 'px;';
8911             }
8912             
8913             if(typeof(config.cursor) != 'undefined'){
8914                 td.style += ' cursor:' +  config.cursor + ';';
8915             }
8916             
8917             if(typeof(config.cls) != 'undefined'){
8918                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8919             }
8920             
8921             ['xs','sm','md','lg'].map(function(size){
8922                 
8923                 if(typeof(config[size]) == 'undefined'){
8924                     return;
8925                 }
8926                 
8927                 
8928                   
8929                 if (!config[size]) { // 0 = hidden
8930                     // BS 4 '0' is treated as hide that column and below.
8931                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8932                     return;
8933                 }
8934                 
8935                 td.cls += ' col-' + size + '-' + config[size] + (
8936                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8937                 );
8938                  
8939
8940             });
8941             
8942             row.cn.push(td);
8943            
8944         }
8945         
8946         row.cellObjects = cellObjects;
8947         
8948         return row;
8949           
8950     },
8951     
8952     
8953     
8954     onBeforeLoad : function()
8955     {
8956         
8957     },
8958      /**
8959      * Remove all rows
8960      */
8961     clear : function()
8962     {
8963         this.el.select('tbody', true).first().dom.innerHTML = '';
8964     },
8965     /**
8966      * Show or hide a row.
8967      * @param {Number} rowIndex to show or hide
8968      * @param {Boolean} state hide
8969      */
8970     setRowVisibility : function(rowIndex, state)
8971     {
8972         var bt = this.mainBody.dom;
8973         
8974         var rows = this.el.select('tbody > tr', true).elements;
8975         
8976         if(typeof(rows[rowIndex]) == 'undefined'){
8977             return;
8978         }
8979         rows[rowIndex].dom.style.display = state ? '' : 'none';
8980     },
8981     
8982     
8983     getSelectionModel : function(){
8984         if(!this.selModel){
8985             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8986         }
8987         return this.selModel;
8988     },
8989     /*
8990      * Render the Roo.bootstrap object from renderder
8991      */
8992     renderCellObject : function(r)
8993     {
8994         var _this = this;
8995         
8996         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8997         
8998         var t = r.cfg.render(r.container);
8999         
9000         if(r.cfg.cn){
9001             Roo.each(r.cfg.cn, function(c){
9002                 var child = {
9003                     container: t.getChildContainer(),
9004                     cfg: c
9005                 };
9006                 _this.renderCellObject(child);
9007             })
9008         }
9009     },
9010     
9011     getRowIndex : function(row)
9012     {
9013         var rowIndex = -1;
9014         
9015         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9016             if(el != row){
9017                 return;
9018             }
9019             
9020             rowIndex = index;
9021         });
9022         
9023         return rowIndex;
9024     },
9025      /**
9026      * Returns the grid's underlying element = used by panel.Grid
9027      * @return {Element} The element
9028      */
9029     getGridEl : function(){
9030         return this.el;
9031     },
9032      /**
9033      * Forces a resize - used by panel.Grid
9034      * @return {Element} The element
9035      */
9036     autoSize : function()
9037     {
9038         //var ctr = Roo.get(this.container.dom.parentElement);
9039         var ctr = Roo.get(this.el.dom);
9040         
9041         var thd = this.getGridEl().select('thead',true).first();
9042         var tbd = this.getGridEl().select('tbody', true).first();
9043         var tfd = this.getGridEl().select('tfoot', true).first();
9044         
9045         var cw = ctr.getWidth();
9046         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9047         
9048         if (tbd) {
9049             
9050             tbd.setWidth(ctr.getWidth());
9051             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9052             // this needs fixing for various usage - currently only hydra job advers I think..
9053             //tdb.setHeight(
9054             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9055             //); 
9056             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9057             cw -= barsize;
9058         }
9059         cw = Math.max(cw, this.totalWidth);
9060         this.getGridEl().select('tbody tr',true).setWidth(cw);
9061         
9062         // resize 'expandable coloumn?
9063         
9064         return; // we doe not have a view in this design..
9065         
9066     },
9067     onBodyScroll: function()
9068     {
9069         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9070         if(this.mainHead){
9071             this.mainHead.setStyle({
9072                 'position' : 'relative',
9073                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9074             });
9075         }
9076         
9077         if(this.lazyLoad){
9078             
9079             var scrollHeight = this.mainBody.dom.scrollHeight;
9080             
9081             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9082             
9083             var height = this.mainBody.getHeight();
9084             
9085             if(scrollHeight - height == scrollTop) {
9086                 
9087                 var total = this.ds.getTotalCount();
9088                 
9089                 if(this.footer.cursor + this.footer.pageSize < total){
9090                     
9091                     this.footer.ds.load({
9092                         params : {
9093                             start : this.footer.cursor + this.footer.pageSize,
9094                             limit : this.footer.pageSize
9095                         },
9096                         add : true
9097                     });
9098                 }
9099             }
9100             
9101         }
9102     },
9103     
9104     onHeaderChange : function()
9105     {
9106         var header = this.renderHeader();
9107         var table = this.el.select('table', true).first();
9108         
9109         this.mainHead.remove();
9110         this.mainHead = table.createChild(header, this.mainBody, false);
9111         
9112         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9113             e.on('click', this.sort, this);
9114         }, this);
9115         
9116         
9117     },
9118     
9119     onHiddenChange : function(colModel, colIndex, hidden)
9120     {
9121         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9122         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9123         
9124         this.CSS.updateRule(thSelector, "display", "");
9125         this.CSS.updateRule(tdSelector, "display", "");
9126         
9127         if(hidden){
9128             this.CSS.updateRule(thSelector, "display", "none");
9129             this.CSS.updateRule(tdSelector, "display", "none");
9130         }
9131         
9132         this.onHeaderChange();
9133         this.onLoad();
9134     },
9135     
9136     setColumnWidth: function(col_index, width)
9137     {
9138         // width = "md-2 xs-2..."
9139         if(!this.colModel.config[col_index]) {
9140             return;
9141         }
9142         
9143         var w = width.split(" ");
9144         
9145         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9146         
9147         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9148         
9149         
9150         for(var j = 0; j < w.length; j++) {
9151             
9152             if(!w[j]) {
9153                 continue;
9154             }
9155             
9156             var size_cls = w[j].split("-");
9157             
9158             if(!Number.isInteger(size_cls[1] * 1)) {
9159                 continue;
9160             }
9161             
9162             if(!this.colModel.config[col_index][size_cls[0]]) {
9163                 continue;
9164             }
9165             
9166             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9167                 continue;
9168             }
9169             
9170             h_row[0].classList.replace(
9171                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9172                 "col-"+size_cls[0]+"-"+size_cls[1]
9173             );
9174             
9175             for(var i = 0; i < rows.length; i++) {
9176                 
9177                 var size_cls = w[j].split("-");
9178                 
9179                 if(!Number.isInteger(size_cls[1] * 1)) {
9180                     continue;
9181                 }
9182                 
9183                 if(!this.colModel.config[col_index][size_cls[0]]) {
9184                     continue;
9185                 }
9186                 
9187                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9188                     continue;
9189                 }
9190                 
9191                 rows[i].classList.replace(
9192                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9193                     "col-"+size_cls[0]+"-"+size_cls[1]
9194                 );
9195             }
9196             
9197             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9198         }
9199     }
9200 });
9201
9202  
9203
9204  /*
9205  * - LGPL
9206  *
9207  * table cell
9208  * 
9209  */
9210
9211 /**
9212  * @class Roo.bootstrap.TableCell
9213  * @extends Roo.bootstrap.Component
9214  * Bootstrap TableCell class
9215  * @cfg {String} html cell contain text
9216  * @cfg {String} cls cell class
9217  * @cfg {String} tag cell tag (td|th) default td
9218  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9219  * @cfg {String} align Aligns the content in a cell
9220  * @cfg {String} axis Categorizes cells
9221  * @cfg {String} bgcolor Specifies the background color of a cell
9222  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9223  * @cfg {Number} colspan Specifies the number of columns a cell should span
9224  * @cfg {String} headers Specifies one or more header cells a cell is related to
9225  * @cfg {Number} height Sets the height of a cell
9226  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9227  * @cfg {Number} rowspan Sets the number of rows a cell should span
9228  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9229  * @cfg {String} valign Vertical aligns the content in a cell
9230  * @cfg {Number} width Specifies the width of a cell
9231  * 
9232  * @constructor
9233  * Create a new TableCell
9234  * @param {Object} config The config object
9235  */
9236
9237 Roo.bootstrap.TableCell = function(config){
9238     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9239 };
9240
9241 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9242     
9243     html: false,
9244     cls: false,
9245     tag: false,
9246     abbr: false,
9247     align: false,
9248     axis: false,
9249     bgcolor: false,
9250     charoff: false,
9251     colspan: false,
9252     headers: false,
9253     height: false,
9254     nowrap: false,
9255     rowspan: false,
9256     scope: false,
9257     valign: false,
9258     width: false,
9259     
9260     
9261     getAutoCreate : function(){
9262         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9263         
9264         cfg = {
9265             tag: 'td'
9266         };
9267         
9268         if(this.tag){
9269             cfg.tag = this.tag;
9270         }
9271         
9272         if (this.html) {
9273             cfg.html=this.html
9274         }
9275         if (this.cls) {
9276             cfg.cls=this.cls
9277         }
9278         if (this.abbr) {
9279             cfg.abbr=this.abbr
9280         }
9281         if (this.align) {
9282             cfg.align=this.align
9283         }
9284         if (this.axis) {
9285             cfg.axis=this.axis
9286         }
9287         if (this.bgcolor) {
9288             cfg.bgcolor=this.bgcolor
9289         }
9290         if (this.charoff) {
9291             cfg.charoff=this.charoff
9292         }
9293         if (this.colspan) {
9294             cfg.colspan=this.colspan
9295         }
9296         if (this.headers) {
9297             cfg.headers=this.headers
9298         }
9299         if (this.height) {
9300             cfg.height=this.height
9301         }
9302         if (this.nowrap) {
9303             cfg.nowrap=this.nowrap
9304         }
9305         if (this.rowspan) {
9306             cfg.rowspan=this.rowspan
9307         }
9308         if (this.scope) {
9309             cfg.scope=this.scope
9310         }
9311         if (this.valign) {
9312             cfg.valign=this.valign
9313         }
9314         if (this.width) {
9315             cfg.width=this.width
9316         }
9317         
9318         
9319         return cfg;
9320     }
9321    
9322 });
9323
9324  
9325
9326  /*
9327  * - LGPL
9328  *
9329  * table row
9330  * 
9331  */
9332
9333 /**
9334  * @class Roo.bootstrap.TableRow
9335  * @extends Roo.bootstrap.Component
9336  * Bootstrap TableRow class
9337  * @cfg {String} cls row class
9338  * @cfg {String} align Aligns the content in a table row
9339  * @cfg {String} bgcolor Specifies a background color for a table row
9340  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9341  * @cfg {String} valign Vertical aligns the content in a table row
9342  * 
9343  * @constructor
9344  * Create a new TableRow
9345  * @param {Object} config The config object
9346  */
9347
9348 Roo.bootstrap.TableRow = function(config){
9349     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9350 };
9351
9352 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9353     
9354     cls: false,
9355     align: false,
9356     bgcolor: false,
9357     charoff: false,
9358     valign: false,
9359     
9360     getAutoCreate : function(){
9361         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9362         
9363         cfg = {
9364             tag: 'tr'
9365         };
9366             
9367         if(this.cls){
9368             cfg.cls = this.cls;
9369         }
9370         if(this.align){
9371             cfg.align = this.align;
9372         }
9373         if(this.bgcolor){
9374             cfg.bgcolor = this.bgcolor;
9375         }
9376         if(this.charoff){
9377             cfg.charoff = this.charoff;
9378         }
9379         if(this.valign){
9380             cfg.valign = this.valign;
9381         }
9382         
9383         return cfg;
9384     }
9385    
9386 });
9387
9388  
9389
9390  /*
9391  * - LGPL
9392  *
9393  * table body
9394  * 
9395  */
9396
9397 /**
9398  * @class Roo.bootstrap.TableBody
9399  * @extends Roo.bootstrap.Component
9400  * Bootstrap TableBody class
9401  * @cfg {String} cls element class
9402  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9403  * @cfg {String} align Aligns the content inside the element
9404  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9405  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9406  * 
9407  * @constructor
9408  * Create a new TableBody
9409  * @param {Object} config The config object
9410  */
9411
9412 Roo.bootstrap.TableBody = function(config){
9413     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9414 };
9415
9416 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9417     
9418     cls: false,
9419     tag: false,
9420     align: false,
9421     charoff: false,
9422     valign: false,
9423     
9424     getAutoCreate : function(){
9425         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9426         
9427         cfg = {
9428             tag: 'tbody'
9429         };
9430             
9431         if (this.cls) {
9432             cfg.cls=this.cls
9433         }
9434         if(this.tag){
9435             cfg.tag = this.tag;
9436         }
9437         
9438         if(this.align){
9439             cfg.align = this.align;
9440         }
9441         if(this.charoff){
9442             cfg.charoff = this.charoff;
9443         }
9444         if(this.valign){
9445             cfg.valign = this.valign;
9446         }
9447         
9448         return cfg;
9449     }
9450     
9451     
9452 //    initEvents : function()
9453 //    {
9454 //        
9455 //        if(!this.store){
9456 //            return;
9457 //        }
9458 //        
9459 //        this.store = Roo.factory(this.store, Roo.data);
9460 //        this.store.on('load', this.onLoad, this);
9461 //        
9462 //        this.store.load();
9463 //        
9464 //    },
9465 //    
9466 //    onLoad: function () 
9467 //    {   
9468 //        this.fireEvent('load', this);
9469 //    }
9470 //    
9471 //   
9472 });
9473
9474  
9475
9476  /*
9477  * Based on:
9478  * Ext JS Library 1.1.1
9479  * Copyright(c) 2006-2007, Ext JS, LLC.
9480  *
9481  * Originally Released Under LGPL - original licence link has changed is not relivant.
9482  *
9483  * Fork - LGPL
9484  * <script type="text/javascript">
9485  */
9486
9487 // as we use this in bootstrap.
9488 Roo.namespace('Roo.form');
9489  /**
9490  * @class Roo.form.Action
9491  * Internal Class used to handle form actions
9492  * @constructor
9493  * @param {Roo.form.BasicForm} el The form element or its id
9494  * @param {Object} config Configuration options
9495  */
9496
9497  
9498  
9499 // define the action interface
9500 Roo.form.Action = function(form, options){
9501     this.form = form;
9502     this.options = options || {};
9503 };
9504 /**
9505  * Client Validation Failed
9506  * @const 
9507  */
9508 Roo.form.Action.CLIENT_INVALID = 'client';
9509 /**
9510  * Server Validation Failed
9511  * @const 
9512  */
9513 Roo.form.Action.SERVER_INVALID = 'server';
9514  /**
9515  * Connect to Server Failed
9516  * @const 
9517  */
9518 Roo.form.Action.CONNECT_FAILURE = 'connect';
9519 /**
9520  * Reading Data from Server Failed
9521  * @const 
9522  */
9523 Roo.form.Action.LOAD_FAILURE = 'load';
9524
9525 Roo.form.Action.prototype = {
9526     type : 'default',
9527     failureType : undefined,
9528     response : undefined,
9529     result : undefined,
9530
9531     // interface method
9532     run : function(options){
9533
9534     },
9535
9536     // interface method
9537     success : function(response){
9538
9539     },
9540
9541     // interface method
9542     handleResponse : function(response){
9543
9544     },
9545
9546     // default connection failure
9547     failure : function(response){
9548         
9549         this.response = response;
9550         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9551         this.form.afterAction(this, false);
9552     },
9553
9554     processResponse : function(response){
9555         this.response = response;
9556         if(!response.responseText){
9557             return true;
9558         }
9559         this.result = this.handleResponse(response);
9560         return this.result;
9561     },
9562
9563     // utility functions used internally
9564     getUrl : function(appendParams){
9565         var url = this.options.url || this.form.url || this.form.el.dom.action;
9566         if(appendParams){
9567             var p = this.getParams();
9568             if(p){
9569                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9570             }
9571         }
9572         return url;
9573     },
9574
9575     getMethod : function(){
9576         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9577     },
9578
9579     getParams : function(){
9580         var bp = this.form.baseParams;
9581         var p = this.options.params;
9582         if(p){
9583             if(typeof p == "object"){
9584                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9585             }else if(typeof p == 'string' && bp){
9586                 p += '&' + Roo.urlEncode(bp);
9587             }
9588         }else if(bp){
9589             p = Roo.urlEncode(bp);
9590         }
9591         return p;
9592     },
9593
9594     createCallback : function(){
9595         return {
9596             success: this.success,
9597             failure: this.failure,
9598             scope: this,
9599             timeout: (this.form.timeout*1000),
9600             upload: this.form.fileUpload ? this.success : undefined
9601         };
9602     }
9603 };
9604
9605 Roo.form.Action.Submit = function(form, options){
9606     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9607 };
9608
9609 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9610     type : 'submit',
9611
9612     haveProgress : false,
9613     uploadComplete : false,
9614     
9615     // uploadProgress indicator.
9616     uploadProgress : function()
9617     {
9618         if (!this.form.progressUrl) {
9619             return;
9620         }
9621         
9622         if (!this.haveProgress) {
9623             Roo.MessageBox.progress("Uploading", "Uploading");
9624         }
9625         if (this.uploadComplete) {
9626            Roo.MessageBox.hide();
9627            return;
9628         }
9629         
9630         this.haveProgress = true;
9631    
9632         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9633         
9634         var c = new Roo.data.Connection();
9635         c.request({
9636             url : this.form.progressUrl,
9637             params: {
9638                 id : uid
9639             },
9640             method: 'GET',
9641             success : function(req){
9642                //console.log(data);
9643                 var rdata = false;
9644                 var edata;
9645                 try  {
9646                    rdata = Roo.decode(req.responseText)
9647                 } catch (e) {
9648                     Roo.log("Invalid data from server..");
9649                     Roo.log(edata);
9650                     return;
9651                 }
9652                 if (!rdata || !rdata.success) {
9653                     Roo.log(rdata);
9654                     Roo.MessageBox.alert(Roo.encode(rdata));
9655                     return;
9656                 }
9657                 var data = rdata.data;
9658                 
9659                 if (this.uploadComplete) {
9660                    Roo.MessageBox.hide();
9661                    return;
9662                 }
9663                    
9664                 if (data){
9665                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9666                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9667                     );
9668                 }
9669                 this.uploadProgress.defer(2000,this);
9670             },
9671        
9672             failure: function(data) {
9673                 Roo.log('progress url failed ');
9674                 Roo.log(data);
9675             },
9676             scope : this
9677         });
9678            
9679     },
9680     
9681     
9682     run : function()
9683     {
9684         // run get Values on the form, so it syncs any secondary forms.
9685         this.form.getValues();
9686         
9687         var o = this.options;
9688         var method = this.getMethod();
9689         var isPost = method == 'POST';
9690         if(o.clientValidation === false || this.form.isValid()){
9691             
9692             if (this.form.progressUrl) {
9693                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9694                     (new Date() * 1) + '' + Math.random());
9695                     
9696             } 
9697             
9698             
9699             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9700                 form:this.form.el.dom,
9701                 url:this.getUrl(!isPost),
9702                 method: method,
9703                 params:isPost ? this.getParams() : null,
9704                 isUpload: this.form.fileUpload,
9705                 formData : this.form.formData
9706             }));
9707             
9708             this.uploadProgress();
9709
9710         }else if (o.clientValidation !== false){ // client validation failed
9711             this.failureType = Roo.form.Action.CLIENT_INVALID;
9712             this.form.afterAction(this, false);
9713         }
9714     },
9715
9716     success : function(response)
9717     {
9718         this.uploadComplete= true;
9719         if (this.haveProgress) {
9720             Roo.MessageBox.hide();
9721         }
9722         
9723         
9724         var result = this.processResponse(response);
9725         if(result === true || result.success){
9726             this.form.afterAction(this, true);
9727             return;
9728         }
9729         if(result.errors){
9730             this.form.markInvalid(result.errors);
9731             this.failureType = Roo.form.Action.SERVER_INVALID;
9732         }
9733         this.form.afterAction(this, false);
9734     },
9735     failure : function(response)
9736     {
9737         this.uploadComplete= true;
9738         if (this.haveProgress) {
9739             Roo.MessageBox.hide();
9740         }
9741         
9742         this.response = response;
9743         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9744         this.form.afterAction(this, false);
9745     },
9746     
9747     handleResponse : function(response){
9748         if(this.form.errorReader){
9749             var rs = this.form.errorReader.read(response);
9750             var errors = [];
9751             if(rs.records){
9752                 for(var i = 0, len = rs.records.length; i < len; i++) {
9753                     var r = rs.records[i];
9754                     errors[i] = r.data;
9755                 }
9756             }
9757             if(errors.length < 1){
9758                 errors = null;
9759             }
9760             return {
9761                 success : rs.success,
9762                 errors : errors
9763             };
9764         }
9765         var ret = false;
9766         try {
9767             ret = Roo.decode(response.responseText);
9768         } catch (e) {
9769             ret = {
9770                 success: false,
9771                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9772                 errors : []
9773             };
9774         }
9775         return ret;
9776         
9777     }
9778 });
9779
9780
9781 Roo.form.Action.Load = function(form, options){
9782     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9783     this.reader = this.form.reader;
9784 };
9785
9786 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9787     type : 'load',
9788
9789     run : function(){
9790         
9791         Roo.Ajax.request(Roo.apply(
9792                 this.createCallback(), {
9793                     method:this.getMethod(),
9794                     url:this.getUrl(false),
9795                     params:this.getParams()
9796         }));
9797     },
9798
9799     success : function(response){
9800         
9801         var result = this.processResponse(response);
9802         if(result === true || !result.success || !result.data){
9803             this.failureType = Roo.form.Action.LOAD_FAILURE;
9804             this.form.afterAction(this, false);
9805             return;
9806         }
9807         this.form.clearInvalid();
9808         this.form.setValues(result.data);
9809         this.form.afterAction(this, true);
9810     },
9811
9812     handleResponse : function(response){
9813         if(this.form.reader){
9814             var rs = this.form.reader.read(response);
9815             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9816             return {
9817                 success : rs.success,
9818                 data : data
9819             };
9820         }
9821         return Roo.decode(response.responseText);
9822     }
9823 });
9824
9825 Roo.form.Action.ACTION_TYPES = {
9826     'load' : Roo.form.Action.Load,
9827     'submit' : Roo.form.Action.Submit
9828 };/*
9829  * - LGPL
9830  *
9831  * form
9832  *
9833  */
9834
9835 /**
9836  * @class Roo.bootstrap.Form
9837  * @extends Roo.bootstrap.Component
9838  * Bootstrap Form class
9839  * @cfg {String} method  GET | POST (default POST)
9840  * @cfg {String} labelAlign top | left (default top)
9841  * @cfg {String} align left  | right - for navbars
9842  * @cfg {Boolean} loadMask load mask when submit (default true)
9843
9844  *
9845  * @constructor
9846  * Create a new Form
9847  * @param {Object} config The config object
9848  */
9849
9850
9851 Roo.bootstrap.Form = function(config){
9852     
9853     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9854     
9855     Roo.bootstrap.Form.popover.apply();
9856     
9857     this.addEvents({
9858         /**
9859          * @event clientvalidation
9860          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9861          * @param {Form} this
9862          * @param {Boolean} valid true if the form has passed client-side validation
9863          */
9864         clientvalidation: true,
9865         /**
9866          * @event beforeaction
9867          * Fires before any action is performed. Return false to cancel the action.
9868          * @param {Form} this
9869          * @param {Action} action The action to be performed
9870          */
9871         beforeaction: true,
9872         /**
9873          * @event actionfailed
9874          * Fires when an action fails.
9875          * @param {Form} this
9876          * @param {Action} action The action that failed
9877          */
9878         actionfailed : true,
9879         /**
9880          * @event actioncomplete
9881          * Fires when an action is completed.
9882          * @param {Form} this
9883          * @param {Action} action The action that completed
9884          */
9885         actioncomplete : true
9886     });
9887 };
9888
9889 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9890
9891      /**
9892      * @cfg {String} method
9893      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9894      */
9895     method : 'POST',
9896     /**
9897      * @cfg {String} url
9898      * The URL to use for form actions if one isn't supplied in the action options.
9899      */
9900     /**
9901      * @cfg {Boolean} fileUpload
9902      * Set to true if this form is a file upload.
9903      */
9904
9905     /**
9906      * @cfg {Object} baseParams
9907      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9908      */
9909
9910     /**
9911      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9912      */
9913     timeout: 30,
9914     /**
9915      * @cfg {Sting} align (left|right) for navbar forms
9916      */
9917     align : 'left',
9918
9919     // private
9920     activeAction : null,
9921
9922     /**
9923      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9924      * element by passing it or its id or mask the form itself by passing in true.
9925      * @type Mixed
9926      */
9927     waitMsgTarget : false,
9928
9929     loadMask : true,
9930     
9931     /**
9932      * @cfg {Boolean} errorMask (true|false) default false
9933      */
9934     errorMask : false,
9935     
9936     /**
9937      * @cfg {Number} maskOffset Default 100
9938      */
9939     maskOffset : 100,
9940     
9941     /**
9942      * @cfg {Boolean} maskBody
9943      */
9944     maskBody : false,
9945
9946     getAutoCreate : function(){
9947
9948         var cfg = {
9949             tag: 'form',
9950             method : this.method || 'POST',
9951             id : this.id || Roo.id(),
9952             cls : ''
9953         };
9954         if (this.parent().xtype.match(/^Nav/)) {
9955             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9956
9957         }
9958
9959         if (this.labelAlign == 'left' ) {
9960             cfg.cls += ' form-horizontal';
9961         }
9962
9963
9964         return cfg;
9965     },
9966     initEvents : function()
9967     {
9968         this.el.on('submit', this.onSubmit, this);
9969         // this was added as random key presses on the form where triggering form submit.
9970         this.el.on('keypress', function(e) {
9971             if (e.getCharCode() != 13) {
9972                 return true;
9973             }
9974             // we might need to allow it for textareas.. and some other items.
9975             // check e.getTarget().
9976
9977             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9978                 return true;
9979             }
9980
9981             Roo.log("keypress blocked");
9982
9983             e.preventDefault();
9984             return false;
9985         });
9986         
9987     },
9988     // private
9989     onSubmit : function(e){
9990         e.stopEvent();
9991     },
9992
9993      /**
9994      * Returns true if client-side validation on the form is successful.
9995      * @return Boolean
9996      */
9997     isValid : function(){
9998         var items = this.getItems();
9999         var valid = true;
10000         var target = false;
10001         
10002         items.each(function(f){
10003             
10004             if(f.validate()){
10005                 return;
10006             }
10007             
10008             Roo.log('invalid field: ' + f.name);
10009             
10010             valid = false;
10011
10012             if(!target && f.el.isVisible(true)){
10013                 target = f;
10014             }
10015            
10016         });
10017         
10018         if(this.errorMask && !valid){
10019             Roo.bootstrap.Form.popover.mask(this, target);
10020         }
10021         
10022         return valid;
10023     },
10024     
10025     /**
10026      * Returns true if any fields in this form have changed since their original load.
10027      * @return Boolean
10028      */
10029     isDirty : function(){
10030         var dirty = false;
10031         var items = this.getItems();
10032         items.each(function(f){
10033            if(f.isDirty()){
10034                dirty = true;
10035                return false;
10036            }
10037            return true;
10038         });
10039         return dirty;
10040     },
10041      /**
10042      * Performs a predefined action (submit or load) or custom actions you define on this form.
10043      * @param {String} actionName The name of the action type
10044      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10045      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10046      * accept other config options):
10047      * <pre>
10048 Property          Type             Description
10049 ----------------  ---------------  ----------------------------------------------------------------------------------
10050 url               String           The url for the action (defaults to the form's url)
10051 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10052 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10053 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10054                                    validate the form on the client (defaults to false)
10055      * </pre>
10056      * @return {BasicForm} this
10057      */
10058     doAction : function(action, options){
10059         if(typeof action == 'string'){
10060             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10061         }
10062         if(this.fireEvent('beforeaction', this, action) !== false){
10063             this.beforeAction(action);
10064             action.run.defer(100, action);
10065         }
10066         return this;
10067     },
10068
10069     // private
10070     beforeAction : function(action){
10071         var o = action.options;
10072         
10073         if(this.loadMask){
10074             
10075             if(this.maskBody){
10076                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10077             } else {
10078                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10079             }
10080         }
10081         // not really supported yet.. ??
10082
10083         //if(this.waitMsgTarget === true){
10084         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10085         //}else if(this.waitMsgTarget){
10086         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10087         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10088         //}else {
10089         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10090        // }
10091
10092     },
10093
10094     // private
10095     afterAction : function(action, success){
10096         this.activeAction = null;
10097         var o = action.options;
10098
10099         if(this.loadMask){
10100             
10101             if(this.maskBody){
10102                 Roo.get(document.body).unmask();
10103             } else {
10104                 this.el.unmask();
10105             }
10106         }
10107         
10108         //if(this.waitMsgTarget === true){
10109 //            this.el.unmask();
10110         //}else if(this.waitMsgTarget){
10111         //    this.waitMsgTarget.unmask();
10112         //}else{
10113         //    Roo.MessageBox.updateProgress(1);
10114         //    Roo.MessageBox.hide();
10115        // }
10116         //
10117         if(success){
10118             if(o.reset){
10119                 this.reset();
10120             }
10121             Roo.callback(o.success, o.scope, [this, action]);
10122             this.fireEvent('actioncomplete', this, action);
10123
10124         }else{
10125
10126             // failure condition..
10127             // we have a scenario where updates need confirming.
10128             // eg. if a locking scenario exists..
10129             // we look for { errors : { needs_confirm : true }} in the response.
10130             if (
10131                 (typeof(action.result) != 'undefined')  &&
10132                 (typeof(action.result.errors) != 'undefined')  &&
10133                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10134            ){
10135                 var _t = this;
10136                 Roo.log("not supported yet");
10137                  /*
10138
10139                 Roo.MessageBox.confirm(
10140                     "Change requires confirmation",
10141                     action.result.errorMsg,
10142                     function(r) {
10143                         if (r != 'yes') {
10144                             return;
10145                         }
10146                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10147                     }
10148
10149                 );
10150                 */
10151
10152
10153                 return;
10154             }
10155
10156             Roo.callback(o.failure, o.scope, [this, action]);
10157             // show an error message if no failed handler is set..
10158             if (!this.hasListener('actionfailed')) {
10159                 Roo.log("need to add dialog support");
10160                 /*
10161                 Roo.MessageBox.alert("Error",
10162                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10163                         action.result.errorMsg :
10164                         "Saving Failed, please check your entries or try again"
10165                 );
10166                 */
10167             }
10168
10169             this.fireEvent('actionfailed', this, action);
10170         }
10171
10172     },
10173     /**
10174      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10175      * @param {String} id The value to search for
10176      * @return Field
10177      */
10178     findField : function(id){
10179         var items = this.getItems();
10180         var field = items.get(id);
10181         if(!field){
10182              items.each(function(f){
10183                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10184                     field = f;
10185                     return false;
10186                 }
10187                 return true;
10188             });
10189         }
10190         return field || null;
10191     },
10192      /**
10193      * Mark fields in this form invalid in bulk.
10194      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10195      * @return {BasicForm} this
10196      */
10197     markInvalid : function(errors){
10198         if(errors instanceof Array){
10199             for(var i = 0, len = errors.length; i < len; i++){
10200                 var fieldError = errors[i];
10201                 var f = this.findField(fieldError.id);
10202                 if(f){
10203                     f.markInvalid(fieldError.msg);
10204                 }
10205             }
10206         }else{
10207             var field, id;
10208             for(id in errors){
10209                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10210                     field.markInvalid(errors[id]);
10211                 }
10212             }
10213         }
10214         //Roo.each(this.childForms || [], function (f) {
10215         //    f.markInvalid(errors);
10216         //});
10217
10218         return this;
10219     },
10220
10221     /**
10222      * Set values for fields in this form in bulk.
10223      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10224      * @return {BasicForm} this
10225      */
10226     setValues : function(values){
10227         if(values instanceof Array){ // array of objects
10228             for(var i = 0, len = values.length; i < len; i++){
10229                 var v = values[i];
10230                 var f = this.findField(v.id);
10231                 if(f){
10232                     f.setValue(v.value);
10233                     if(this.trackResetOnLoad){
10234                         f.originalValue = f.getValue();
10235                     }
10236                 }
10237             }
10238         }else{ // object hash
10239             var field, id;
10240             for(id in values){
10241                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10242
10243                     if (field.setFromData &&
10244                         field.valueField &&
10245                         field.displayField &&
10246                         // combos' with local stores can
10247                         // be queried via setValue()
10248                         // to set their value..
10249                         (field.store && !field.store.isLocal)
10250                         ) {
10251                         // it's a combo
10252                         var sd = { };
10253                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10254                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10255                         field.setFromData(sd);
10256
10257                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10258                         
10259                         field.setFromData(values);
10260                         
10261                     } else {
10262                         field.setValue(values[id]);
10263                     }
10264
10265
10266                     if(this.trackResetOnLoad){
10267                         field.originalValue = field.getValue();
10268                     }
10269                 }
10270             }
10271         }
10272
10273         //Roo.each(this.childForms || [], function (f) {
10274         //    f.setValues(values);
10275         //});
10276
10277         return this;
10278     },
10279
10280     /**
10281      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10282      * they are returned as an array.
10283      * @param {Boolean} asString
10284      * @return {Object}
10285      */
10286     getValues : function(asString){
10287         //if (this.childForms) {
10288             // copy values from the child forms
10289         //    Roo.each(this.childForms, function (f) {
10290         //        this.setValues(f.getValues());
10291         //    }, this);
10292         //}
10293
10294
10295
10296         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10297         if(asString === true){
10298             return fs;
10299         }
10300         return Roo.urlDecode(fs);
10301     },
10302
10303     /**
10304      * Returns the fields in this form as an object with key/value pairs.
10305      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10306      * @return {Object}
10307      */
10308     getFieldValues : function(with_hidden)
10309     {
10310         var items = this.getItems();
10311         var ret = {};
10312         items.each(function(f){
10313             
10314             if (!f.getName()) {
10315                 return;
10316             }
10317             
10318             var v = f.getValue();
10319             
10320             if (f.inputType =='radio') {
10321                 if (typeof(ret[f.getName()]) == 'undefined') {
10322                     ret[f.getName()] = ''; // empty..
10323                 }
10324
10325                 if (!f.el.dom.checked) {
10326                     return;
10327
10328                 }
10329                 v = f.el.dom.value;
10330
10331             }
10332             
10333             if(f.xtype == 'MoneyField'){
10334                 ret[f.currencyName] = f.getCurrency();
10335             }
10336
10337             // not sure if this supported any more..
10338             if ((typeof(v) == 'object') && f.getRawValue) {
10339                 v = f.getRawValue() ; // dates..
10340             }
10341             // combo boxes where name != hiddenName...
10342             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10343                 ret[f.name] = f.getRawValue();
10344             }
10345             ret[f.getName()] = v;
10346         });
10347
10348         return ret;
10349     },
10350
10351     /**
10352      * Clears all invalid messages in this form.
10353      * @return {BasicForm} this
10354      */
10355     clearInvalid : function(){
10356         var items = this.getItems();
10357
10358         items.each(function(f){
10359            f.clearInvalid();
10360         });
10361
10362         return this;
10363     },
10364
10365     /**
10366      * Resets this form.
10367      * @return {BasicForm} this
10368      */
10369     reset : function(){
10370         var items = this.getItems();
10371         items.each(function(f){
10372             f.reset();
10373         });
10374
10375         Roo.each(this.childForms || [], function (f) {
10376             f.reset();
10377         });
10378
10379
10380         return this;
10381     },
10382     
10383     getItems : function()
10384     {
10385         var r=new Roo.util.MixedCollection(false, function(o){
10386             return o.id || (o.id = Roo.id());
10387         });
10388         var iter = function(el) {
10389             if (el.inputEl) {
10390                 r.add(el);
10391             }
10392             if (!el.items) {
10393                 return;
10394             }
10395             Roo.each(el.items,function(e) {
10396                 iter(e);
10397             });
10398         };
10399
10400         iter(this);
10401         return r;
10402     },
10403     
10404     hideFields : function(items)
10405     {
10406         Roo.each(items, function(i){
10407             
10408             var f = this.findField(i);
10409             
10410             if(!f){
10411                 return;
10412             }
10413             
10414             f.hide();
10415             
10416         }, this);
10417     },
10418     
10419     showFields : function(items)
10420     {
10421         Roo.each(items, function(i){
10422             
10423             var f = this.findField(i);
10424             
10425             if(!f){
10426                 return;
10427             }
10428             
10429             f.show();
10430             
10431         }, this);
10432     }
10433
10434 });
10435
10436 Roo.apply(Roo.bootstrap.Form, {
10437     
10438     popover : {
10439         
10440         padding : 5,
10441         
10442         isApplied : false,
10443         
10444         isMasked : false,
10445         
10446         form : false,
10447         
10448         target : false,
10449         
10450         toolTip : false,
10451         
10452         intervalID : false,
10453         
10454         maskEl : false,
10455         
10456         apply : function()
10457         {
10458             if(this.isApplied){
10459                 return;
10460             }
10461             
10462             this.maskEl = {
10463                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10464                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10465                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10466                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10467             };
10468             
10469             this.maskEl.top.enableDisplayMode("block");
10470             this.maskEl.left.enableDisplayMode("block");
10471             this.maskEl.bottom.enableDisplayMode("block");
10472             this.maskEl.right.enableDisplayMode("block");
10473             
10474             this.toolTip = new Roo.bootstrap.Tooltip({
10475                 cls : 'roo-form-error-popover',
10476                 alignment : {
10477                     'left' : ['r-l', [-2,0], 'right'],
10478                     'right' : ['l-r', [2,0], 'left'],
10479                     'bottom' : ['tl-bl', [0,2], 'top'],
10480                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10481                 }
10482             });
10483             
10484             this.toolTip.render(Roo.get(document.body));
10485
10486             this.toolTip.el.enableDisplayMode("block");
10487             
10488             Roo.get(document.body).on('click', function(){
10489                 this.unmask();
10490             }, this);
10491             
10492             Roo.get(document.body).on('touchstart', function(){
10493                 this.unmask();
10494             }, this);
10495             
10496             this.isApplied = true
10497         },
10498         
10499         mask : function(form, target)
10500         {
10501             this.form = form;
10502             
10503             this.target = target;
10504             
10505             if(!this.form.errorMask || !target.el){
10506                 return;
10507             }
10508             
10509             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10510             
10511             Roo.log(scrollable);
10512             
10513             var ot = this.target.el.calcOffsetsTo(scrollable);
10514             
10515             var scrollTo = ot[1] - this.form.maskOffset;
10516             
10517             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10518             
10519             scrollable.scrollTo('top', scrollTo);
10520             
10521             var box = this.target.el.getBox();
10522             Roo.log(box);
10523             var zIndex = Roo.bootstrap.Modal.zIndex++;
10524
10525             
10526             this.maskEl.top.setStyle('position', 'absolute');
10527             this.maskEl.top.setStyle('z-index', zIndex);
10528             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10529             this.maskEl.top.setLeft(0);
10530             this.maskEl.top.setTop(0);
10531             this.maskEl.top.show();
10532             
10533             this.maskEl.left.setStyle('position', 'absolute');
10534             this.maskEl.left.setStyle('z-index', zIndex);
10535             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10536             this.maskEl.left.setLeft(0);
10537             this.maskEl.left.setTop(box.y - this.padding);
10538             this.maskEl.left.show();
10539
10540             this.maskEl.bottom.setStyle('position', 'absolute');
10541             this.maskEl.bottom.setStyle('z-index', zIndex);
10542             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10543             this.maskEl.bottom.setLeft(0);
10544             this.maskEl.bottom.setTop(box.bottom + this.padding);
10545             this.maskEl.bottom.show();
10546
10547             this.maskEl.right.setStyle('position', 'absolute');
10548             this.maskEl.right.setStyle('z-index', zIndex);
10549             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10550             this.maskEl.right.setLeft(box.right + this.padding);
10551             this.maskEl.right.setTop(box.y - this.padding);
10552             this.maskEl.right.show();
10553
10554             this.toolTip.bindEl = this.target.el;
10555
10556             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10557
10558             var tip = this.target.blankText;
10559
10560             if(this.target.getValue() !== '' ) {
10561                 
10562                 if (this.target.invalidText.length) {
10563                     tip = this.target.invalidText;
10564                 } else if (this.target.regexText.length){
10565                     tip = this.target.regexText;
10566                 }
10567             }
10568
10569             this.toolTip.show(tip);
10570
10571             this.intervalID = window.setInterval(function() {
10572                 Roo.bootstrap.Form.popover.unmask();
10573             }, 10000);
10574
10575             window.onwheel = function(){ return false;};
10576             
10577             (function(){ this.isMasked = true; }).defer(500, this);
10578             
10579         },
10580         
10581         unmask : function()
10582         {
10583             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10584                 return;
10585             }
10586             
10587             this.maskEl.top.setStyle('position', 'absolute');
10588             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10589             this.maskEl.top.hide();
10590
10591             this.maskEl.left.setStyle('position', 'absolute');
10592             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10593             this.maskEl.left.hide();
10594
10595             this.maskEl.bottom.setStyle('position', 'absolute');
10596             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10597             this.maskEl.bottom.hide();
10598
10599             this.maskEl.right.setStyle('position', 'absolute');
10600             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10601             this.maskEl.right.hide();
10602             
10603             this.toolTip.hide();
10604             
10605             this.toolTip.el.hide();
10606             
10607             window.onwheel = function(){ return true;};
10608             
10609             if(this.intervalID){
10610                 window.clearInterval(this.intervalID);
10611                 this.intervalID = false;
10612             }
10613             
10614             this.isMasked = false;
10615             
10616         }
10617         
10618     }
10619     
10620 });
10621
10622 /*
10623  * Based on:
10624  * Ext JS Library 1.1.1
10625  * Copyright(c) 2006-2007, Ext JS, LLC.
10626  *
10627  * Originally Released Under LGPL - original licence link has changed is not relivant.
10628  *
10629  * Fork - LGPL
10630  * <script type="text/javascript">
10631  */
10632 /**
10633  * @class Roo.form.VTypes
10634  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10635  * @singleton
10636  */
10637 Roo.form.VTypes = function(){
10638     // closure these in so they are only created once.
10639     var alpha = /^[a-zA-Z_]+$/;
10640     var alphanum = /^[a-zA-Z0-9_]+$/;
10641     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10642     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10643
10644     // All these messages and functions are configurable
10645     return {
10646         /**
10647          * The function used to validate email addresses
10648          * @param {String} value The email address
10649          */
10650         'email' : function(v){
10651             return email.test(v);
10652         },
10653         /**
10654          * The error text to display when the email validation function returns false
10655          * @type String
10656          */
10657         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10658         /**
10659          * The keystroke filter mask to be applied on email input
10660          * @type RegExp
10661          */
10662         'emailMask' : /[a-z0-9_\.\-@]/i,
10663
10664         /**
10665          * The function used to validate URLs
10666          * @param {String} value The URL
10667          */
10668         'url' : function(v){
10669             return url.test(v);
10670         },
10671         /**
10672          * The error text to display when the url validation function returns false
10673          * @type String
10674          */
10675         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10676         
10677         /**
10678          * The function used to validate alpha values
10679          * @param {String} value The value
10680          */
10681         'alpha' : function(v){
10682             return alpha.test(v);
10683         },
10684         /**
10685          * The error text to display when the alpha validation function returns false
10686          * @type String
10687          */
10688         'alphaText' : 'This field should only contain letters and _',
10689         /**
10690          * The keystroke filter mask to be applied on alpha input
10691          * @type RegExp
10692          */
10693         'alphaMask' : /[a-z_]/i,
10694
10695         /**
10696          * The function used to validate alphanumeric values
10697          * @param {String} value The value
10698          */
10699         'alphanum' : function(v){
10700             return alphanum.test(v);
10701         },
10702         /**
10703          * The error text to display when the alphanumeric validation function returns false
10704          * @type String
10705          */
10706         'alphanumText' : 'This field should only contain letters, numbers and _',
10707         /**
10708          * The keystroke filter mask to be applied on alphanumeric input
10709          * @type RegExp
10710          */
10711         'alphanumMask' : /[a-z0-9_]/i
10712     };
10713 }();/*
10714  * - LGPL
10715  *
10716  * Input
10717  * 
10718  */
10719
10720 /**
10721  * @class Roo.bootstrap.Input
10722  * @extends Roo.bootstrap.Component
10723  * Bootstrap Input class
10724  * @cfg {Boolean} disabled is it disabled
10725  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10726  * @cfg {String} name name of the input
10727  * @cfg {string} fieldLabel - the label associated
10728  * @cfg {string} placeholder - placeholder to put in text.
10729  * @cfg {string}  before - input group add on before
10730  * @cfg {string} after - input group add on after
10731  * @cfg {string} size - (lg|sm) or leave empty..
10732  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10733  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10734  * @cfg {Number} md colspan out of 12 for computer-sized screens
10735  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10736  * @cfg {string} value default value of the input
10737  * @cfg {Number} labelWidth set the width of label 
10738  * @cfg {Number} labellg set the width of label (1-12)
10739  * @cfg {Number} labelmd set the width of label (1-12)
10740  * @cfg {Number} labelsm set the width of label (1-12)
10741  * @cfg {Number} labelxs set the width of label (1-12)
10742  * @cfg {String} labelAlign (top|left)
10743  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10744  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10745  * @cfg {String} indicatorpos (left|right) default left
10746  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10747  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10748  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10749
10750  * @cfg {String} align (left|center|right) Default left
10751  * @cfg {Boolean} forceFeedback (true|false) Default false
10752  * 
10753  * @constructor
10754  * Create a new Input
10755  * @param {Object} config The config object
10756  */
10757
10758 Roo.bootstrap.Input = function(config){
10759     
10760     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10761     
10762     this.addEvents({
10763         /**
10764          * @event focus
10765          * Fires when this field receives input focus.
10766          * @param {Roo.form.Field} this
10767          */
10768         focus : true,
10769         /**
10770          * @event blur
10771          * Fires when this field loses input focus.
10772          * @param {Roo.form.Field} this
10773          */
10774         blur : true,
10775         /**
10776          * @event specialkey
10777          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10778          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10779          * @param {Roo.form.Field} this
10780          * @param {Roo.EventObject} e The event object
10781          */
10782         specialkey : true,
10783         /**
10784          * @event change
10785          * Fires just before the field blurs if the field value has changed.
10786          * @param {Roo.form.Field} this
10787          * @param {Mixed} newValue The new value
10788          * @param {Mixed} oldValue The original value
10789          */
10790         change : true,
10791         /**
10792          * @event invalid
10793          * Fires after the field has been marked as invalid.
10794          * @param {Roo.form.Field} this
10795          * @param {String} msg The validation message
10796          */
10797         invalid : true,
10798         /**
10799          * @event valid
10800          * Fires after the field has been validated with no errors.
10801          * @param {Roo.form.Field} this
10802          */
10803         valid : true,
10804          /**
10805          * @event keyup
10806          * Fires after the key up
10807          * @param {Roo.form.Field} this
10808          * @param {Roo.EventObject}  e The event Object
10809          */
10810         keyup : true,
10811         /**
10812          * @event paste
10813          * Fires after the user pastes into input
10814          * @param {Roo.form.Field} this
10815          * @param {Roo.EventObject}  e The event Object
10816          */
10817         paste : true
10818     });
10819 };
10820
10821 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10822      /**
10823      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10824       automatic validation (defaults to "keyup").
10825      */
10826     validationEvent : "keyup",
10827      /**
10828      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10829      */
10830     validateOnBlur : true,
10831     /**
10832      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10833      */
10834     validationDelay : 250,
10835      /**
10836      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10837      */
10838     focusClass : "x-form-focus",  // not needed???
10839     
10840        
10841     /**
10842      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10843      */
10844     invalidClass : "has-warning",
10845     
10846     /**
10847      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10848      */
10849     validClass : "has-success",
10850     
10851     /**
10852      * @cfg {Boolean} hasFeedback (true|false) default true
10853      */
10854     hasFeedback : true,
10855     
10856     /**
10857      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10858      */
10859     invalidFeedbackClass : "glyphicon-warning-sign",
10860     
10861     /**
10862      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10863      */
10864     validFeedbackClass : "glyphicon-ok",
10865     
10866     /**
10867      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10868      */
10869     selectOnFocus : false,
10870     
10871      /**
10872      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10873      */
10874     maskRe : null,
10875        /**
10876      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10877      */
10878     vtype : null,
10879     
10880       /**
10881      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10882      */
10883     disableKeyFilter : false,
10884     
10885        /**
10886      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10887      */
10888     disabled : false,
10889      /**
10890      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10891      */
10892     allowBlank : true,
10893     /**
10894      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10895      */
10896     blankText : "Please complete this mandatory field",
10897     
10898      /**
10899      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10900      */
10901     minLength : 0,
10902     /**
10903      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10904      */
10905     maxLength : Number.MAX_VALUE,
10906     /**
10907      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10908      */
10909     minLengthText : "The minimum length for this field is {0}",
10910     /**
10911      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10912      */
10913     maxLengthText : "The maximum length for this field is {0}",
10914   
10915     
10916     /**
10917      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10918      * If available, this function will be called only after the basic validators all return true, and will be passed the
10919      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10920      */
10921     validator : null,
10922     /**
10923      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10924      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10925      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10926      */
10927     regex : null,
10928     /**
10929      * @cfg {String} regexText -- Depricated - use Invalid Text
10930      */
10931     regexText : "",
10932     
10933     /**
10934      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10935      */
10936     invalidText : "",
10937     
10938     
10939     
10940     autocomplete: false,
10941     
10942     
10943     fieldLabel : '',
10944     inputType : 'text',
10945     
10946     name : false,
10947     placeholder: false,
10948     before : false,
10949     after : false,
10950     size : false,
10951     hasFocus : false,
10952     preventMark: false,
10953     isFormField : true,
10954     value : '',
10955     labelWidth : 2,
10956     labelAlign : false,
10957     readOnly : false,
10958     align : false,
10959     formatedValue : false,
10960     forceFeedback : false,
10961     
10962     indicatorpos : 'left',
10963     
10964     labellg : 0,
10965     labelmd : 0,
10966     labelsm : 0,
10967     labelxs : 0,
10968     
10969     capture : '',
10970     accept : '',
10971     
10972     parentLabelAlign : function()
10973     {
10974         var parent = this;
10975         while (parent.parent()) {
10976             parent = parent.parent();
10977             if (typeof(parent.labelAlign) !='undefined') {
10978                 return parent.labelAlign;
10979             }
10980         }
10981         return 'left';
10982         
10983     },
10984     
10985     getAutoCreate : function()
10986     {
10987         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10988         
10989         var id = Roo.id();
10990         
10991         var cfg = {};
10992         
10993         if(this.inputType != 'hidden'){
10994             cfg.cls = 'form-group' //input-group
10995         }
10996         
10997         var input =  {
10998             tag: 'input',
10999             id : id,
11000             type : this.inputType,
11001             value : this.value,
11002             cls : 'form-control',
11003             placeholder : this.placeholder || '',
11004             autocomplete : this.autocomplete || 'new-password'
11005         };
11006         if (this.inputType == 'file') {
11007             input.style = 'overflow:hidden'; // why not in CSS?
11008         }
11009         
11010         if(this.capture.length){
11011             input.capture = this.capture;
11012         }
11013         
11014         if(this.accept.length){
11015             input.accept = this.accept + "/*";
11016         }
11017         
11018         if(this.align){
11019             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11020         }
11021         
11022         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11023             input.maxLength = this.maxLength;
11024         }
11025         
11026         if (this.disabled) {
11027             input.disabled=true;
11028         }
11029         
11030         if (this.readOnly) {
11031             input.readonly=true;
11032         }
11033         
11034         if (this.name) {
11035             input.name = this.name;
11036         }
11037         
11038         if (this.size) {
11039             input.cls += ' input-' + this.size;
11040         }
11041         
11042         var settings=this;
11043         ['xs','sm','md','lg'].map(function(size){
11044             if (settings[size]) {
11045                 cfg.cls += ' col-' + size + '-' + settings[size];
11046             }
11047         });
11048         
11049         var inputblock = input;
11050         
11051         var feedback = {
11052             tag: 'span',
11053             cls: 'glyphicon form-control-feedback'
11054         };
11055             
11056         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11057             
11058             inputblock = {
11059                 cls : 'has-feedback',
11060                 cn :  [
11061                     input,
11062                     feedback
11063                 ] 
11064             };  
11065         }
11066         
11067         if (this.before || this.after) {
11068             
11069             inputblock = {
11070                 cls : 'input-group',
11071                 cn :  [] 
11072             };
11073             
11074             if (this.before && typeof(this.before) == 'string') {
11075                 
11076                 inputblock.cn.push({
11077                     tag :'span',
11078                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11079                     html : this.before
11080                 });
11081             }
11082             if (this.before && typeof(this.before) == 'object') {
11083                 this.before = Roo.factory(this.before);
11084                 
11085                 inputblock.cn.push({
11086                     tag :'span',
11087                     cls : 'roo-input-before input-group-prepend   input-group-' +
11088                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11089                 });
11090             }
11091             
11092             inputblock.cn.push(input);
11093             
11094             if (this.after && typeof(this.after) == 'string') {
11095                 inputblock.cn.push({
11096                     tag :'span',
11097                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11098                     html : this.after
11099                 });
11100             }
11101             if (this.after && typeof(this.after) == 'object') {
11102                 this.after = Roo.factory(this.after);
11103                 
11104                 inputblock.cn.push({
11105                     tag :'span',
11106                     cls : 'roo-input-after input-group-append  input-group-' +
11107                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11108                 });
11109             }
11110             
11111             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11112                 inputblock.cls += ' has-feedback';
11113                 inputblock.cn.push(feedback);
11114             }
11115         };
11116         var indicator = {
11117             tag : 'i',
11118             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11119             tooltip : 'This field is required'
11120         };
11121         if (this.allowBlank ) {
11122             indicator.style = this.allowBlank ? ' display:none' : '';
11123         }
11124         if (align ==='left' && this.fieldLabel.length) {
11125             
11126             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11127             
11128             cfg.cn = [
11129                 indicator,
11130                 {
11131                     tag: 'label',
11132                     'for' :  id,
11133                     cls : 'control-label col-form-label',
11134                     html : this.fieldLabel
11135
11136                 },
11137                 {
11138                     cls : "", 
11139                     cn: [
11140                         inputblock
11141                     ]
11142                 }
11143             ];
11144             
11145             var labelCfg = cfg.cn[1];
11146             var contentCfg = cfg.cn[2];
11147             
11148             if(this.indicatorpos == 'right'){
11149                 cfg.cn = [
11150                     {
11151                         tag: 'label',
11152                         'for' :  id,
11153                         cls : 'control-label col-form-label',
11154                         cn : [
11155                             {
11156                                 tag : 'span',
11157                                 html : this.fieldLabel
11158                             },
11159                             indicator
11160                         ]
11161                     },
11162                     {
11163                         cls : "",
11164                         cn: [
11165                             inputblock
11166                         ]
11167                     }
11168
11169                 ];
11170                 
11171                 labelCfg = cfg.cn[0];
11172                 contentCfg = cfg.cn[1];
11173             
11174             }
11175             
11176             if(this.labelWidth > 12){
11177                 labelCfg.style = "width: " + this.labelWidth + 'px';
11178             }
11179             
11180             if(this.labelWidth < 13 && this.labelmd == 0){
11181                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11182             }
11183             
11184             if(this.labellg > 0){
11185                 labelCfg.cls += ' col-lg-' + this.labellg;
11186                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11187             }
11188             
11189             if(this.labelmd > 0){
11190                 labelCfg.cls += ' col-md-' + this.labelmd;
11191                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11192             }
11193             
11194             if(this.labelsm > 0){
11195                 labelCfg.cls += ' col-sm-' + this.labelsm;
11196                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11197             }
11198             
11199             if(this.labelxs > 0){
11200                 labelCfg.cls += ' col-xs-' + this.labelxs;
11201                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11202             }
11203             
11204             
11205         } else if ( this.fieldLabel.length) {
11206                 
11207             
11208             
11209             cfg.cn = [
11210                 {
11211                     tag : 'i',
11212                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11213                     tooltip : 'This field is required',
11214                     style : this.allowBlank ? ' display:none' : '' 
11215                 },
11216                 {
11217                     tag: 'label',
11218                    //cls : 'input-group-addon',
11219                     html : this.fieldLabel
11220
11221                 },
11222
11223                inputblock
11224
11225            ];
11226            
11227            if(this.indicatorpos == 'right'){
11228        
11229                 cfg.cn = [
11230                     {
11231                         tag: 'label',
11232                        //cls : 'input-group-addon',
11233                         html : this.fieldLabel
11234
11235                     },
11236                     {
11237                         tag : 'i',
11238                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11239                         tooltip : 'This field is required',
11240                         style : this.allowBlank ? ' display:none' : '' 
11241                     },
11242
11243                    inputblock
11244
11245                ];
11246
11247             }
11248
11249         } else {
11250             
11251             cfg.cn = [
11252
11253                     inputblock
11254
11255             ];
11256                 
11257                 
11258         };
11259         
11260         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11261            cfg.cls += ' navbar-form';
11262         }
11263         
11264         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11265             // on BS4 we do this only if not form 
11266             cfg.cls += ' navbar-form';
11267             cfg.tag = 'li';
11268         }
11269         
11270         return cfg;
11271         
11272     },
11273     /**
11274      * return the real input element.
11275      */
11276     inputEl: function ()
11277     {
11278         return this.el.select('input.form-control',true).first();
11279     },
11280     
11281     tooltipEl : function()
11282     {
11283         return this.inputEl();
11284     },
11285     
11286     indicatorEl : function()
11287     {
11288         if (Roo.bootstrap.version == 4) {
11289             return false; // not enabled in v4 yet.
11290         }
11291         
11292         var indicator = this.el.select('i.roo-required-indicator',true).first();
11293         
11294         if(!indicator){
11295             return false;
11296         }
11297         
11298         return indicator;
11299         
11300     },
11301     
11302     setDisabled : function(v)
11303     {
11304         var i  = this.inputEl().dom;
11305         if (!v) {
11306             i.removeAttribute('disabled');
11307             return;
11308             
11309         }
11310         i.setAttribute('disabled','true');
11311     },
11312     initEvents : function()
11313     {
11314           
11315         this.inputEl().on("keydown" , this.fireKey,  this);
11316         this.inputEl().on("focus", this.onFocus,  this);
11317         this.inputEl().on("blur", this.onBlur,  this);
11318         
11319         this.inputEl().relayEvent('keyup', this);
11320         this.inputEl().relayEvent('paste', this);
11321         
11322         this.indicator = this.indicatorEl();
11323         
11324         if(this.indicator){
11325             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11326         }
11327  
11328         // reference to original value for reset
11329         this.originalValue = this.getValue();
11330         //Roo.form.TextField.superclass.initEvents.call(this);
11331         if(this.validationEvent == 'keyup'){
11332             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11333             this.inputEl().on('keyup', this.filterValidation, this);
11334         }
11335         else if(this.validationEvent !== false){
11336             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11337         }
11338         
11339         if(this.selectOnFocus){
11340             this.on("focus", this.preFocus, this);
11341             
11342         }
11343         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11344             this.inputEl().on("keypress", this.filterKeys, this);
11345         } else {
11346             this.inputEl().relayEvent('keypress', this);
11347         }
11348        /* if(this.grow){
11349             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11350             this.el.on("click", this.autoSize,  this);
11351         }
11352         */
11353         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11354             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11355         }
11356         
11357         if (typeof(this.before) == 'object') {
11358             this.before.render(this.el.select('.roo-input-before',true).first());
11359         }
11360         if (typeof(this.after) == 'object') {
11361             this.after.render(this.el.select('.roo-input-after',true).first());
11362         }
11363         
11364         this.inputEl().on('change', this.onChange, this);
11365         
11366     },
11367     filterValidation : function(e){
11368         if(!e.isNavKeyPress()){
11369             this.validationTask.delay(this.validationDelay);
11370         }
11371     },
11372      /**
11373      * Validates the field value
11374      * @return {Boolean} True if the value is valid, else false
11375      */
11376     validate : function(){
11377         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11378         if(this.disabled || this.validateValue(this.getRawValue())){
11379             this.markValid();
11380             return true;
11381         }
11382         
11383         this.markInvalid();
11384         return false;
11385     },
11386     
11387     
11388     /**
11389      * Validates a value according to the field's validation rules and marks the field as invalid
11390      * if the validation fails
11391      * @param {Mixed} value The value to validate
11392      * @return {Boolean} True if the value is valid, else false
11393      */
11394     validateValue : function(value)
11395     {
11396         if(this.getVisibilityEl().hasClass('hidden')){
11397             return true;
11398         }
11399         
11400         if(value.length < 1)  { // if it's blank
11401             if(this.allowBlank){
11402                 return true;
11403             }
11404             return false;
11405         }
11406         
11407         if(value.length < this.minLength){
11408             return false;
11409         }
11410         if(value.length > this.maxLength){
11411             return false;
11412         }
11413         if(this.vtype){
11414             var vt = Roo.form.VTypes;
11415             if(!vt[this.vtype](value, this)){
11416                 return false;
11417             }
11418         }
11419         if(typeof this.validator == "function"){
11420             var msg = this.validator(value);
11421             if(msg !== true){
11422                 return false;
11423             }
11424             if (typeof(msg) == 'string') {
11425                 this.invalidText = msg;
11426             }
11427         }
11428         
11429         if(this.regex && !this.regex.test(value)){
11430             return false;
11431         }
11432         
11433         return true;
11434     },
11435     
11436      // private
11437     fireKey : function(e){
11438         //Roo.log('field ' + e.getKey());
11439         if(e.isNavKeyPress()){
11440             this.fireEvent("specialkey", this, e);
11441         }
11442     },
11443     focus : function (selectText){
11444         if(this.rendered){
11445             this.inputEl().focus();
11446             if(selectText === true){
11447                 this.inputEl().dom.select();
11448             }
11449         }
11450         return this;
11451     } ,
11452     
11453     onFocus : function(){
11454         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11455            // this.el.addClass(this.focusClass);
11456         }
11457         if(!this.hasFocus){
11458             this.hasFocus = true;
11459             this.startValue = this.getValue();
11460             this.fireEvent("focus", this);
11461         }
11462     },
11463     
11464     beforeBlur : Roo.emptyFn,
11465
11466     
11467     // private
11468     onBlur : function(){
11469         this.beforeBlur();
11470         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11471             //this.el.removeClass(this.focusClass);
11472         }
11473         this.hasFocus = false;
11474         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11475             this.validate();
11476         }
11477         var v = this.getValue();
11478         if(String(v) !== String(this.startValue)){
11479             this.fireEvent('change', this, v, this.startValue);
11480         }
11481         this.fireEvent("blur", this);
11482     },
11483     
11484     onChange : function(e)
11485     {
11486         var v = this.getValue();
11487         if(String(v) !== String(this.startValue)){
11488             this.fireEvent('change', this, v, this.startValue);
11489         }
11490         
11491     },
11492     
11493     /**
11494      * Resets the current field value to the originally loaded value and clears any validation messages
11495      */
11496     reset : function(){
11497         this.setValue(this.originalValue);
11498         this.validate();
11499     },
11500      /**
11501      * Returns the name of the field
11502      * @return {Mixed} name The name field
11503      */
11504     getName: function(){
11505         return this.name;
11506     },
11507      /**
11508      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11509      * @return {Mixed} value The field value
11510      */
11511     getValue : function(){
11512         
11513         var v = this.inputEl().getValue();
11514         
11515         return v;
11516     },
11517     /**
11518      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11519      * @return {Mixed} value The field value
11520      */
11521     getRawValue : function(){
11522         var v = this.inputEl().getValue();
11523         
11524         return v;
11525     },
11526     
11527     /**
11528      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11529      * @param {Mixed} value The value to set
11530      */
11531     setRawValue : function(v){
11532         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11533     },
11534     
11535     selectText : function(start, end){
11536         var v = this.getRawValue();
11537         if(v.length > 0){
11538             start = start === undefined ? 0 : start;
11539             end = end === undefined ? v.length : end;
11540             var d = this.inputEl().dom;
11541             if(d.setSelectionRange){
11542                 d.setSelectionRange(start, end);
11543             }else if(d.createTextRange){
11544                 var range = d.createTextRange();
11545                 range.moveStart("character", start);
11546                 range.moveEnd("character", v.length-end);
11547                 range.select();
11548             }
11549         }
11550     },
11551     
11552     /**
11553      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11554      * @param {Mixed} value The value to set
11555      */
11556     setValue : function(v){
11557         this.value = v;
11558         if(this.rendered){
11559             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11560             this.validate();
11561         }
11562     },
11563     
11564     /*
11565     processValue : function(value){
11566         if(this.stripCharsRe){
11567             var newValue = value.replace(this.stripCharsRe, '');
11568             if(newValue !== value){
11569                 this.setRawValue(newValue);
11570                 return newValue;
11571             }
11572         }
11573         return value;
11574     },
11575   */
11576     preFocus : function(){
11577         
11578         if(this.selectOnFocus){
11579             this.inputEl().dom.select();
11580         }
11581     },
11582     filterKeys : function(e){
11583         var k = e.getKey();
11584         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11585             return;
11586         }
11587         var c = e.getCharCode(), cc = String.fromCharCode(c);
11588         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11589             return;
11590         }
11591         if(!this.maskRe.test(cc)){
11592             e.stopEvent();
11593         }
11594     },
11595      /**
11596      * Clear any invalid styles/messages for this field
11597      */
11598     clearInvalid : function(){
11599         
11600         if(!this.el || this.preventMark){ // not rendered
11601             return;
11602         }
11603         
11604         
11605         this.el.removeClass([this.invalidClass, 'is-invalid']);
11606         
11607         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11608             
11609             var feedback = this.el.select('.form-control-feedback', true).first();
11610             
11611             if(feedback){
11612                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11613             }
11614             
11615         }
11616         
11617         if(this.indicator){
11618             this.indicator.removeClass('visible');
11619             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11620         }
11621         
11622         this.fireEvent('valid', this);
11623     },
11624     
11625      /**
11626      * Mark this field as valid
11627      */
11628     markValid : function()
11629     {
11630         if(!this.el  || this.preventMark){ // not rendered...
11631             return;
11632         }
11633         
11634         this.el.removeClass([this.invalidClass, this.validClass]);
11635         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11636
11637         var feedback = this.el.select('.form-control-feedback', true).first();
11638             
11639         if(feedback){
11640             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11641         }
11642         
11643         if(this.indicator){
11644             this.indicator.removeClass('visible');
11645             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11646         }
11647         
11648         if(this.disabled){
11649             return;
11650         }
11651         
11652            
11653         if(this.allowBlank && !this.getRawValue().length){
11654             return;
11655         }
11656         if (Roo.bootstrap.version == 3) {
11657             this.el.addClass(this.validClass);
11658         } else {
11659             this.inputEl().addClass('is-valid');
11660         }
11661
11662         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11663             
11664             var feedback = this.el.select('.form-control-feedback', true).first();
11665             
11666             if(feedback){
11667                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11668                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11669             }
11670             
11671         }
11672         
11673         this.fireEvent('valid', this);
11674     },
11675     
11676      /**
11677      * Mark this field as invalid
11678      * @param {String} msg The validation message
11679      */
11680     markInvalid : function(msg)
11681     {
11682         if(!this.el  || this.preventMark){ // not rendered
11683             return;
11684         }
11685         
11686         this.el.removeClass([this.invalidClass, this.validClass]);
11687         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11688         
11689         var feedback = this.el.select('.form-control-feedback', true).first();
11690             
11691         if(feedback){
11692             this.el.select('.form-control-feedback', true).first().removeClass(
11693                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11694         }
11695
11696         if(this.disabled){
11697             return;
11698         }
11699         
11700         if(this.allowBlank && !this.getRawValue().length){
11701             return;
11702         }
11703         
11704         if(this.indicator){
11705             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11706             this.indicator.addClass('visible');
11707         }
11708         if (Roo.bootstrap.version == 3) {
11709             this.el.addClass(this.invalidClass);
11710         } else {
11711             this.inputEl().addClass('is-invalid');
11712         }
11713         
11714         
11715         
11716         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11717             
11718             var feedback = this.el.select('.form-control-feedback', true).first();
11719             
11720             if(feedback){
11721                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11722                 
11723                 if(this.getValue().length || this.forceFeedback){
11724                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11725                 }
11726                 
11727             }
11728             
11729         }
11730         
11731         this.fireEvent('invalid', this, msg);
11732     },
11733     // private
11734     SafariOnKeyDown : function(event)
11735     {
11736         // this is a workaround for a password hang bug on chrome/ webkit.
11737         if (this.inputEl().dom.type != 'password') {
11738             return;
11739         }
11740         
11741         var isSelectAll = false;
11742         
11743         if(this.inputEl().dom.selectionEnd > 0){
11744             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11745         }
11746         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11747             event.preventDefault();
11748             this.setValue('');
11749             return;
11750         }
11751         
11752         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11753             
11754             event.preventDefault();
11755             // this is very hacky as keydown always get's upper case.
11756             //
11757             var cc = String.fromCharCode(event.getCharCode());
11758             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11759             
11760         }
11761     },
11762     adjustWidth : function(tag, w){
11763         tag = tag.toLowerCase();
11764         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11765             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11766                 if(tag == 'input'){
11767                     return w + 2;
11768                 }
11769                 if(tag == 'textarea'){
11770                     return w-2;
11771                 }
11772             }else if(Roo.isOpera){
11773                 if(tag == 'input'){
11774                     return w + 2;
11775                 }
11776                 if(tag == 'textarea'){
11777                     return w-2;
11778                 }
11779             }
11780         }
11781         return w;
11782     },
11783     
11784     setFieldLabel : function(v)
11785     {
11786         if(!this.rendered){
11787             return;
11788         }
11789         
11790         if(this.indicatorEl()){
11791             var ar = this.el.select('label > span',true);
11792             
11793             if (ar.elements.length) {
11794                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11795                 this.fieldLabel = v;
11796                 return;
11797             }
11798             
11799             var br = this.el.select('label',true);
11800             
11801             if(br.elements.length) {
11802                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11803                 this.fieldLabel = v;
11804                 return;
11805             }
11806             
11807             Roo.log('Cannot Found any of label > span || label in input');
11808             return;
11809         }
11810         
11811         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11812         this.fieldLabel = v;
11813         
11814         
11815     }
11816 });
11817
11818  
11819 /*
11820  * - LGPL
11821  *
11822  * Input
11823  * 
11824  */
11825
11826 /**
11827  * @class Roo.bootstrap.TextArea
11828  * @extends Roo.bootstrap.Input
11829  * Bootstrap TextArea class
11830  * @cfg {Number} cols Specifies the visible width of a text area
11831  * @cfg {Number} rows Specifies the visible number of lines in a text area
11832  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11833  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11834  * @cfg {string} html text
11835  * 
11836  * @constructor
11837  * Create a new TextArea
11838  * @param {Object} config The config object
11839  */
11840
11841 Roo.bootstrap.TextArea = function(config){
11842     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11843    
11844 };
11845
11846 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11847      
11848     cols : false,
11849     rows : 5,
11850     readOnly : false,
11851     warp : 'soft',
11852     resize : false,
11853     value: false,
11854     html: false,
11855     
11856     getAutoCreate : function(){
11857         
11858         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11859         
11860         var id = Roo.id();
11861         
11862         var cfg = {};
11863         
11864         if(this.inputType != 'hidden'){
11865             cfg.cls = 'form-group' //input-group
11866         }
11867         
11868         var input =  {
11869             tag: 'textarea',
11870             id : id,
11871             warp : this.warp,
11872             rows : this.rows,
11873             value : this.value || '',
11874             html: this.html || '',
11875             cls : 'form-control',
11876             placeholder : this.placeholder || '' 
11877             
11878         };
11879         
11880         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11881             input.maxLength = this.maxLength;
11882         }
11883         
11884         if(this.resize){
11885             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11886         }
11887         
11888         if(this.cols){
11889             input.cols = this.cols;
11890         }
11891         
11892         if (this.readOnly) {
11893             input.readonly = true;
11894         }
11895         
11896         if (this.name) {
11897             input.name = this.name;
11898         }
11899         
11900         if (this.size) {
11901             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11902         }
11903         
11904         var settings=this;
11905         ['xs','sm','md','lg'].map(function(size){
11906             if (settings[size]) {
11907                 cfg.cls += ' col-' + size + '-' + settings[size];
11908             }
11909         });
11910         
11911         var inputblock = input;
11912         
11913         if(this.hasFeedback && !this.allowBlank){
11914             
11915             var feedback = {
11916                 tag: 'span',
11917                 cls: 'glyphicon form-control-feedback'
11918             };
11919
11920             inputblock = {
11921                 cls : 'has-feedback',
11922                 cn :  [
11923                     input,
11924                     feedback
11925                 ] 
11926             };  
11927         }
11928         
11929         
11930         if (this.before || this.after) {
11931             
11932             inputblock = {
11933                 cls : 'input-group',
11934                 cn :  [] 
11935             };
11936             if (this.before) {
11937                 inputblock.cn.push({
11938                     tag :'span',
11939                     cls : 'input-group-addon',
11940                     html : this.before
11941                 });
11942             }
11943             
11944             inputblock.cn.push(input);
11945             
11946             if(this.hasFeedback && !this.allowBlank){
11947                 inputblock.cls += ' has-feedback';
11948                 inputblock.cn.push(feedback);
11949             }
11950             
11951             if (this.after) {
11952                 inputblock.cn.push({
11953                     tag :'span',
11954                     cls : 'input-group-addon',
11955                     html : this.after
11956                 });
11957             }
11958             
11959         }
11960         
11961         if (align ==='left' && this.fieldLabel.length) {
11962             cfg.cn = [
11963                 {
11964                     tag: 'label',
11965                     'for' :  id,
11966                     cls : 'control-label',
11967                     html : this.fieldLabel
11968                 },
11969                 {
11970                     cls : "",
11971                     cn: [
11972                         inputblock
11973                     ]
11974                 }
11975
11976             ];
11977             
11978             if(this.labelWidth > 12){
11979                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11980             }
11981
11982             if(this.labelWidth < 13 && this.labelmd == 0){
11983                 this.labelmd = this.labelWidth;
11984             }
11985
11986             if(this.labellg > 0){
11987                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11988                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11989             }
11990
11991             if(this.labelmd > 0){
11992                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11993                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11994             }
11995
11996             if(this.labelsm > 0){
11997                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11998                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11999             }
12000
12001             if(this.labelxs > 0){
12002                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12003                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12004             }
12005             
12006         } else if ( this.fieldLabel.length) {
12007             cfg.cn = [
12008
12009                {
12010                    tag: 'label',
12011                    //cls : 'input-group-addon',
12012                    html : this.fieldLabel
12013
12014                },
12015
12016                inputblock
12017
12018            ];
12019
12020         } else {
12021
12022             cfg.cn = [
12023
12024                 inputblock
12025
12026             ];
12027                 
12028         }
12029         
12030         if (this.disabled) {
12031             input.disabled=true;
12032         }
12033         
12034         return cfg;
12035         
12036     },
12037     /**
12038      * return the real textarea element.
12039      */
12040     inputEl: function ()
12041     {
12042         return this.el.select('textarea.form-control',true).first();
12043     },
12044     
12045     /**
12046      * Clear any invalid styles/messages for this field
12047      */
12048     clearInvalid : function()
12049     {
12050         
12051         if(!this.el || this.preventMark){ // not rendered
12052             return;
12053         }
12054         
12055         var label = this.el.select('label', true).first();
12056         var icon = this.el.select('i.fa-star', true).first();
12057         
12058         if(label && icon){
12059             icon.remove();
12060         }
12061         this.el.removeClass( this.validClass);
12062         this.inputEl().removeClass('is-invalid');
12063          
12064         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12065             
12066             var feedback = this.el.select('.form-control-feedback', true).first();
12067             
12068             if(feedback){
12069                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12070             }
12071             
12072         }
12073         
12074         this.fireEvent('valid', this);
12075     },
12076     
12077      /**
12078      * Mark this field as valid
12079      */
12080     markValid : function()
12081     {
12082         if(!this.el  || this.preventMark){ // not rendered
12083             return;
12084         }
12085         
12086         this.el.removeClass([this.invalidClass, this.validClass]);
12087         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12088         
12089         var feedback = this.el.select('.form-control-feedback', true).first();
12090             
12091         if(feedback){
12092             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12093         }
12094
12095         if(this.disabled || this.allowBlank){
12096             return;
12097         }
12098         
12099         var label = this.el.select('label', true).first();
12100         var icon = this.el.select('i.fa-star', true).first();
12101         
12102         if(label && icon){
12103             icon.remove();
12104         }
12105         if (Roo.bootstrap.version == 3) {
12106             this.el.addClass(this.validClass);
12107         } else {
12108             this.inputEl().addClass('is-valid');
12109         }
12110         
12111         
12112         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12113             
12114             var feedback = this.el.select('.form-control-feedback', true).first();
12115             
12116             if(feedback){
12117                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12118                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12119             }
12120             
12121         }
12122         
12123         this.fireEvent('valid', this);
12124     },
12125     
12126      /**
12127      * Mark this field as invalid
12128      * @param {String} msg The validation message
12129      */
12130     markInvalid : function(msg)
12131     {
12132         if(!this.el  || this.preventMark){ // not rendered
12133             return;
12134         }
12135         
12136         this.el.removeClass([this.invalidClass, this.validClass]);
12137         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12138         
12139         var feedback = this.el.select('.form-control-feedback', true).first();
12140             
12141         if(feedback){
12142             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12143         }
12144
12145         if(this.disabled || this.allowBlank){
12146             return;
12147         }
12148         
12149         var label = this.el.select('label', true).first();
12150         var icon = this.el.select('i.fa-star', true).first();
12151         
12152         if(!this.getValue().length && label && !icon){
12153             this.el.createChild({
12154                 tag : 'i',
12155                 cls : 'text-danger fa fa-lg fa-star',
12156                 tooltip : 'This field is required',
12157                 style : 'margin-right:5px;'
12158             }, label, true);
12159         }
12160         
12161         if (Roo.bootstrap.version == 3) {
12162             this.el.addClass(this.invalidClass);
12163         } else {
12164             this.inputEl().addClass('is-invalid');
12165         }
12166         
12167         // fixme ... this may be depricated need to test..
12168         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12169             
12170             var feedback = this.el.select('.form-control-feedback', true).first();
12171             
12172             if(feedback){
12173                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12174                 
12175                 if(this.getValue().length || this.forceFeedback){
12176                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12177                 }
12178                 
12179             }
12180             
12181         }
12182         
12183         this.fireEvent('invalid', this, msg);
12184     }
12185 });
12186
12187  
12188 /*
12189  * - LGPL
12190  *
12191  * trigger field - base class for combo..
12192  * 
12193  */
12194  
12195 /**
12196  * @class Roo.bootstrap.TriggerField
12197  * @extends Roo.bootstrap.Input
12198  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12199  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12200  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12201  * for which you can provide a custom implementation.  For example:
12202  * <pre><code>
12203 var trigger = new Roo.bootstrap.TriggerField();
12204 trigger.onTriggerClick = myTriggerFn;
12205 trigger.applyTo('my-field');
12206 </code></pre>
12207  *
12208  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12209  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12210  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12211  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12212  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12213
12214  * @constructor
12215  * Create a new TriggerField.
12216  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12217  * to the base TextField)
12218  */
12219 Roo.bootstrap.TriggerField = function(config){
12220     this.mimicing = false;
12221     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12222 };
12223
12224 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12225     /**
12226      * @cfg {String} triggerClass A CSS class to apply to the trigger
12227      */
12228      /**
12229      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12230      */
12231     hideTrigger:false,
12232
12233     /**
12234      * @cfg {Boolean} removable (true|false) special filter default false
12235      */
12236     removable : false,
12237     
12238     /** @cfg {Boolean} grow @hide */
12239     /** @cfg {Number} growMin @hide */
12240     /** @cfg {Number} growMax @hide */
12241
12242     /**
12243      * @hide 
12244      * @method
12245      */
12246     autoSize: Roo.emptyFn,
12247     // private
12248     monitorTab : true,
12249     // private
12250     deferHeight : true,
12251
12252     
12253     actionMode : 'wrap',
12254     
12255     caret : false,
12256     
12257     
12258     getAutoCreate : function(){
12259        
12260         var align = this.labelAlign || this.parentLabelAlign();
12261         
12262         var id = Roo.id();
12263         
12264         var cfg = {
12265             cls: 'form-group' //input-group
12266         };
12267         
12268         
12269         var input =  {
12270             tag: 'input',
12271             id : id,
12272             type : this.inputType,
12273             cls : 'form-control',
12274             autocomplete: 'new-password',
12275             placeholder : this.placeholder || '' 
12276             
12277         };
12278         if (this.name) {
12279             input.name = this.name;
12280         }
12281         if (this.size) {
12282             input.cls += ' input-' + this.size;
12283         }
12284         
12285         if (this.disabled) {
12286             input.disabled=true;
12287         }
12288         
12289         var inputblock = input;
12290         
12291         if(this.hasFeedback && !this.allowBlank){
12292             
12293             var feedback = {
12294                 tag: 'span',
12295                 cls: 'glyphicon form-control-feedback'
12296             };
12297             
12298             if(this.removable && !this.editable  ){
12299                 inputblock = {
12300                     cls : 'has-feedback',
12301                     cn :  [
12302                         inputblock,
12303                         {
12304                             tag: 'button',
12305                             html : 'x',
12306                             cls : 'roo-combo-removable-btn close'
12307                         },
12308                         feedback
12309                     ] 
12310                 };
12311             } else {
12312                 inputblock = {
12313                     cls : 'has-feedback',
12314                     cn :  [
12315                         inputblock,
12316                         feedback
12317                     ] 
12318                 };
12319             }
12320
12321         } else {
12322             if(this.removable && !this.editable ){
12323                 inputblock = {
12324                     cls : 'roo-removable',
12325                     cn :  [
12326                         inputblock,
12327                         {
12328                             tag: 'button',
12329                             html : 'x',
12330                             cls : 'roo-combo-removable-btn close'
12331                         }
12332                     ] 
12333                 };
12334             }
12335         }
12336         
12337         if (this.before || this.after) {
12338             
12339             inputblock = {
12340                 cls : 'input-group',
12341                 cn :  [] 
12342             };
12343             if (this.before) {
12344                 inputblock.cn.push({
12345                     tag :'span',
12346                     cls : 'input-group-addon input-group-prepend input-group-text',
12347                     html : this.before
12348                 });
12349             }
12350             
12351             inputblock.cn.push(input);
12352             
12353             if(this.hasFeedback && !this.allowBlank){
12354                 inputblock.cls += ' has-feedback';
12355                 inputblock.cn.push(feedback);
12356             }
12357             
12358             if (this.after) {
12359                 inputblock.cn.push({
12360                     tag :'span',
12361                     cls : 'input-group-addon input-group-append input-group-text',
12362                     html : this.after
12363                 });
12364             }
12365             
12366         };
12367         
12368       
12369         
12370         var ibwrap = inputblock;
12371         
12372         if(this.multiple){
12373             ibwrap = {
12374                 tag: 'ul',
12375                 cls: 'roo-select2-choices',
12376                 cn:[
12377                     {
12378                         tag: 'li',
12379                         cls: 'roo-select2-search-field',
12380                         cn: [
12381
12382                             inputblock
12383                         ]
12384                     }
12385                 ]
12386             };
12387                 
12388         }
12389         
12390         var combobox = {
12391             cls: 'roo-select2-container input-group',
12392             cn: [
12393                  {
12394                     tag: 'input',
12395                     type : 'hidden',
12396                     cls: 'form-hidden-field'
12397                 },
12398                 ibwrap
12399             ]
12400         };
12401         
12402         if(!this.multiple && this.showToggleBtn){
12403             
12404             var caret = {
12405                         tag: 'span',
12406                         cls: 'caret'
12407              };
12408             if (this.caret != false) {
12409                 caret = {
12410                      tag: 'i',
12411                      cls: 'fa fa-' + this.caret
12412                 };
12413                 
12414             }
12415             
12416             combobox.cn.push({
12417                 tag :'span',
12418                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12419                 cn : [
12420                     Roo.bootstrap.version == 3 ? caret : '',
12421                     {
12422                         tag: 'span',
12423                         cls: 'combobox-clear',
12424                         cn  : [
12425                             {
12426                                 tag : 'i',
12427                                 cls: 'icon-remove'
12428                             }
12429                         ]
12430                     }
12431                 ]
12432
12433             })
12434         }
12435         
12436         if(this.multiple){
12437             combobox.cls += ' roo-select2-container-multi';
12438         }
12439          var indicator = {
12440             tag : 'i',
12441             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12442             tooltip : 'This field is required'
12443         };
12444         if (Roo.bootstrap.version == 4) {
12445             indicator = {
12446                 tag : 'i',
12447                 style : 'display:none'
12448             };
12449         }
12450         
12451         
12452         if (align ==='left' && this.fieldLabel.length) {
12453             
12454             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12455
12456             cfg.cn = [
12457                 indicator,
12458                 {
12459                     tag: 'label',
12460                     'for' :  id,
12461                     cls : 'control-label',
12462                     html : this.fieldLabel
12463
12464                 },
12465                 {
12466                     cls : "", 
12467                     cn: [
12468                         combobox
12469                     ]
12470                 }
12471
12472             ];
12473             
12474             var labelCfg = cfg.cn[1];
12475             var contentCfg = cfg.cn[2];
12476             
12477             if(this.indicatorpos == 'right'){
12478                 cfg.cn = [
12479                     {
12480                         tag: 'label',
12481                         'for' :  id,
12482                         cls : 'control-label',
12483                         cn : [
12484                             {
12485                                 tag : 'span',
12486                                 html : this.fieldLabel
12487                             },
12488                             indicator
12489                         ]
12490                     },
12491                     {
12492                         cls : "", 
12493                         cn: [
12494                             combobox
12495                         ]
12496                     }
12497
12498                 ];
12499                 
12500                 labelCfg = cfg.cn[0];
12501                 contentCfg = cfg.cn[1];
12502             }
12503             
12504             if(this.labelWidth > 12){
12505                 labelCfg.style = "width: " + this.labelWidth + 'px';
12506             }
12507             
12508             if(this.labelWidth < 13 && this.labelmd == 0){
12509                 this.labelmd = this.labelWidth;
12510             }
12511             
12512             if(this.labellg > 0){
12513                 labelCfg.cls += ' col-lg-' + this.labellg;
12514                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12515             }
12516             
12517             if(this.labelmd > 0){
12518                 labelCfg.cls += ' col-md-' + this.labelmd;
12519                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12520             }
12521             
12522             if(this.labelsm > 0){
12523                 labelCfg.cls += ' col-sm-' + this.labelsm;
12524                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12525             }
12526             
12527             if(this.labelxs > 0){
12528                 labelCfg.cls += ' col-xs-' + this.labelxs;
12529                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12530             }
12531             
12532         } else if ( this.fieldLabel.length) {
12533 //                Roo.log(" label");
12534             cfg.cn = [
12535                 indicator,
12536                {
12537                    tag: 'label',
12538                    //cls : 'input-group-addon',
12539                    html : this.fieldLabel
12540
12541                },
12542
12543                combobox
12544
12545             ];
12546             
12547             if(this.indicatorpos == 'right'){
12548                 
12549                 cfg.cn = [
12550                     {
12551                        tag: 'label',
12552                        cn : [
12553                            {
12554                                tag : 'span',
12555                                html : this.fieldLabel
12556                            },
12557                            indicator
12558                        ]
12559
12560                     },
12561                     combobox
12562
12563                 ];
12564
12565             }
12566
12567         } else {
12568             
12569 //                Roo.log(" no label && no align");
12570                 cfg = combobox
12571                      
12572                 
12573         }
12574         
12575         var settings=this;
12576         ['xs','sm','md','lg'].map(function(size){
12577             if (settings[size]) {
12578                 cfg.cls += ' col-' + size + '-' + settings[size];
12579             }
12580         });
12581         
12582         return cfg;
12583         
12584     },
12585     
12586     
12587     
12588     // private
12589     onResize : function(w, h){
12590 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12591 //        if(typeof w == 'number'){
12592 //            var x = w - this.trigger.getWidth();
12593 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12594 //            this.trigger.setStyle('left', x+'px');
12595 //        }
12596     },
12597
12598     // private
12599     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12600
12601     // private
12602     getResizeEl : function(){
12603         return this.inputEl();
12604     },
12605
12606     // private
12607     getPositionEl : function(){
12608         return this.inputEl();
12609     },
12610
12611     // private
12612     alignErrorIcon : function(){
12613         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12614     },
12615
12616     // private
12617     initEvents : function(){
12618         
12619         this.createList();
12620         
12621         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12622         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12623         if(!this.multiple && this.showToggleBtn){
12624             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12625             if(this.hideTrigger){
12626                 this.trigger.setDisplayed(false);
12627             }
12628             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12629         }
12630         
12631         if(this.multiple){
12632             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12633         }
12634         
12635         if(this.removable && !this.editable && !this.tickable){
12636             var close = this.closeTriggerEl();
12637             
12638             if(close){
12639                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12640                 close.on('click', this.removeBtnClick, this, close);
12641             }
12642         }
12643         
12644         //this.trigger.addClassOnOver('x-form-trigger-over');
12645         //this.trigger.addClassOnClick('x-form-trigger-click');
12646         
12647         //if(!this.width){
12648         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12649         //}
12650     },
12651     
12652     closeTriggerEl : function()
12653     {
12654         var close = this.el.select('.roo-combo-removable-btn', true).first();
12655         return close ? close : false;
12656     },
12657     
12658     removeBtnClick : function(e, h, el)
12659     {
12660         e.preventDefault();
12661         
12662         if(this.fireEvent("remove", this) !== false){
12663             this.reset();
12664             this.fireEvent("afterremove", this)
12665         }
12666     },
12667     
12668     createList : function()
12669     {
12670         this.list = Roo.get(document.body).createChild({
12671             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12672             cls: 'typeahead typeahead-long dropdown-menu shadow',
12673             style: 'display:none'
12674         });
12675         
12676         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12677         
12678     },
12679
12680     // private
12681     initTrigger : function(){
12682        
12683     },
12684
12685     // private
12686     onDestroy : function(){
12687         if(this.trigger){
12688             this.trigger.removeAllListeners();
12689           //  this.trigger.remove();
12690         }
12691         //if(this.wrap){
12692         //    this.wrap.remove();
12693         //}
12694         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12695     },
12696
12697     // private
12698     onFocus : function(){
12699         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12700         /*
12701         if(!this.mimicing){
12702             this.wrap.addClass('x-trigger-wrap-focus');
12703             this.mimicing = true;
12704             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12705             if(this.monitorTab){
12706                 this.el.on("keydown", this.checkTab, this);
12707             }
12708         }
12709         */
12710     },
12711
12712     // private
12713     checkTab : function(e){
12714         if(e.getKey() == e.TAB){
12715             this.triggerBlur();
12716         }
12717     },
12718
12719     // private
12720     onBlur : function(){
12721         // do nothing
12722     },
12723
12724     // private
12725     mimicBlur : function(e, t){
12726         /*
12727         if(!this.wrap.contains(t) && this.validateBlur()){
12728             this.triggerBlur();
12729         }
12730         */
12731     },
12732
12733     // private
12734     triggerBlur : function(){
12735         this.mimicing = false;
12736         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12737         if(this.monitorTab){
12738             this.el.un("keydown", this.checkTab, this);
12739         }
12740         //this.wrap.removeClass('x-trigger-wrap-focus');
12741         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12742     },
12743
12744     // private
12745     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12746     validateBlur : function(e, t){
12747         return true;
12748     },
12749
12750     // private
12751     onDisable : function(){
12752         this.inputEl().dom.disabled = true;
12753         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12754         //if(this.wrap){
12755         //    this.wrap.addClass('x-item-disabled');
12756         //}
12757     },
12758
12759     // private
12760     onEnable : function(){
12761         this.inputEl().dom.disabled = false;
12762         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12763         //if(this.wrap){
12764         //    this.el.removeClass('x-item-disabled');
12765         //}
12766     },
12767
12768     // private
12769     onShow : function(){
12770         var ae = this.getActionEl();
12771         
12772         if(ae){
12773             ae.dom.style.display = '';
12774             ae.dom.style.visibility = 'visible';
12775         }
12776     },
12777
12778     // private
12779     
12780     onHide : function(){
12781         var ae = this.getActionEl();
12782         ae.dom.style.display = 'none';
12783     },
12784
12785     /**
12786      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12787      * by an implementing function.
12788      * @method
12789      * @param {EventObject} e
12790      */
12791     onTriggerClick : Roo.emptyFn
12792 });
12793  
12794 /*
12795 * Licence: LGPL
12796 */
12797
12798 /**
12799  * @class Roo.bootstrap.CardUploader
12800  * @extends Roo.bootstrap.Button
12801  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12802  * @cfg {Number} errorTimeout default 3000
12803  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12804  * @cfg {Array}  html The button text.
12805
12806  *
12807  * @constructor
12808  * Create a new CardUploader
12809  * @param {Object} config The config object
12810  */
12811
12812 Roo.bootstrap.CardUploader = function(config){
12813     
12814  
12815     
12816     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12817     
12818     
12819     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12820         return r.data.id
12821      });
12822     
12823      this.addEvents({
12824          // raw events
12825         /**
12826          * @event preview
12827          * When a image is clicked on - and needs to display a slideshow or similar..
12828          * @param {Roo.bootstrap.Card} this
12829          * @param {Object} The image information data 
12830          *
12831          */
12832         'preview' : true,
12833          /**
12834          * @event download
12835          * When a the download link is clicked
12836          * @param {Roo.bootstrap.Card} this
12837          * @param {Object} The image information data  contains 
12838          */
12839         'download' : true
12840         
12841     });
12842 };
12843  
12844 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12845     
12846      
12847     errorTimeout : 3000,
12848      
12849     images : false,
12850    
12851     fileCollection : false,
12852     allowBlank : true,
12853     
12854     getAutoCreate : function()
12855     {
12856         
12857         var cfg =  {
12858             cls :'form-group' ,
12859             cn : [
12860                
12861                 {
12862                     tag: 'label',
12863                    //cls : 'input-group-addon',
12864                     html : this.fieldLabel
12865
12866                 },
12867
12868                 {
12869                     tag: 'input',
12870                     type : 'hidden',
12871                     name : this.name,
12872                     value : this.value,
12873                     cls : 'd-none  form-control'
12874                 },
12875                 
12876                 {
12877                     tag: 'input',
12878                     multiple : 'multiple',
12879                     type : 'file',
12880                     cls : 'd-none  roo-card-upload-selector'
12881                 },
12882                 
12883                 {
12884                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12885                 },
12886                 {
12887                     cls : 'card-columns roo-card-uploader-container'
12888                 }
12889
12890             ]
12891         };
12892            
12893          
12894         return cfg;
12895     },
12896     
12897     getChildContainer : function() /// what children are added to.
12898     {
12899         return this.containerEl;
12900     },
12901    
12902     getButtonContainer : function() /// what children are added to.
12903     {
12904         return this.el.select(".roo-card-uploader-button-container").first();
12905     },
12906    
12907     initEvents : function()
12908     {
12909         
12910         Roo.bootstrap.Input.prototype.initEvents.call(this);
12911         
12912         var t = this;
12913         this.addxtype({
12914             xns: Roo.bootstrap,
12915
12916             xtype : 'Button',
12917             container_method : 'getButtonContainer' ,            
12918             html :  this.html, // fix changable?
12919             cls : 'w-100 ',
12920             listeners : {
12921                 'click' : function(btn, e) {
12922                     t.onClick(e);
12923                 }
12924             }
12925         });
12926         
12927         
12928         
12929         
12930         this.urlAPI = (window.createObjectURL && window) || 
12931                                 (window.URL && URL.revokeObjectURL && URL) || 
12932                                 (window.webkitURL && webkitURL);
12933                         
12934          
12935          
12936          
12937         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12938         
12939         this.selectorEl.on('change', this.onFileSelected, this);
12940         if (this.images) {
12941             var t = this;
12942             this.images.forEach(function(img) {
12943                 t.addCard(img)
12944             });
12945             this.images = false;
12946         }
12947         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12948          
12949        
12950     },
12951     
12952    
12953     onClick : function(e)
12954     {
12955         e.preventDefault();
12956          
12957         this.selectorEl.dom.click();
12958          
12959     },
12960     
12961     onFileSelected : function(e)
12962     {
12963         e.preventDefault();
12964         
12965         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12966             return;
12967         }
12968         
12969         Roo.each(this.selectorEl.dom.files, function(file){    
12970             this.addFile(file);
12971         }, this);
12972          
12973     },
12974     
12975       
12976     
12977       
12978     
12979     addFile : function(file)
12980     {
12981            
12982         if(typeof(file) === 'string'){
12983             throw "Add file by name?"; // should not happen
12984             return;
12985         }
12986         
12987         if(!file || !this.urlAPI){
12988             return;
12989         }
12990         
12991         // file;
12992         // file.type;
12993         
12994         var _this = this;
12995         
12996         
12997         var url = _this.urlAPI.createObjectURL( file);
12998            
12999         this.addCard({
13000             id : Roo.bootstrap.CardUploader.ID--,
13001             is_uploaded : false,
13002             src : url,
13003             srcfile : file,
13004             title : file.name,
13005             mimetype : file.type,
13006             preview : false,
13007             is_deleted : 0
13008         });
13009         
13010     },
13011     
13012     /**
13013      * addCard - add an Attachment to the uploader
13014      * @param data - the data about the image to upload
13015      *
13016      * {
13017           id : 123
13018           title : "Title of file",
13019           is_uploaded : false,
13020           src : "http://.....",
13021           srcfile : { the File upload object },
13022           mimetype : file.type,
13023           preview : false,
13024           is_deleted : 0
13025           .. any other data...
13026         }
13027      *
13028      * 
13029     */
13030     
13031     addCard : function (data)
13032     {
13033         // hidden input element?
13034         // if the file is not an image...
13035         //then we need to use something other that and header_image
13036         var t = this;
13037         //   remove.....
13038         var footer = [
13039             {
13040                 xns : Roo.bootstrap,
13041                 xtype : 'CardFooter',
13042                  items: [
13043                     {
13044                         xns : Roo.bootstrap,
13045                         xtype : 'Element',
13046                         cls : 'd-flex',
13047                         items : [
13048                             
13049                             {
13050                                 xns : Roo.bootstrap,
13051                                 xtype : 'Button',
13052                                 html : String.format("<small>{0}</small>", data.title),
13053                                 cls : 'col-10 text-left',
13054                                 size: 'sm',
13055                                 weight: 'link',
13056                                 fa : 'download',
13057                                 listeners : {
13058                                     click : function() {
13059                                      
13060                                         t.fireEvent( "download", t, data );
13061                                     }
13062                                 }
13063                             },
13064                           
13065                             {
13066                                 xns : Roo.bootstrap,
13067                                 xtype : 'Button',
13068                                 style: 'max-height: 28px; ',
13069                                 size : 'sm',
13070                                 weight: 'danger',
13071                                 cls : 'col-2',
13072                                 fa : 'times',
13073                                 listeners : {
13074                                     click : function() {
13075                                         t.removeCard(data.id)
13076                                     }
13077                                 }
13078                             }
13079                         ]
13080                     }
13081                     
13082                 ] 
13083             }
13084             
13085         ];
13086         
13087         var cn = this.addxtype(
13088             {
13089                  
13090                 xns : Roo.bootstrap,
13091                 xtype : 'Card',
13092                 closeable : true,
13093                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13094                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13095                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13096                 data : data,
13097                 html : false,
13098                  
13099                 items : footer,
13100                 initEvents : function() {
13101                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13102                     var card = this;
13103                     this.imgEl = this.el.select('.card-img-top').first();
13104                     if (this.imgEl) {
13105                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13106                         this.imgEl.set({ 'pointer' : 'cursor' });
13107                                   
13108                     }
13109                     this.getCardFooter().addClass('p-1');
13110                     
13111                   
13112                 }
13113                 
13114             }
13115         );
13116         // dont' really need ot update items.
13117         // this.items.push(cn);
13118         this.fileCollection.add(cn);
13119         
13120         if (!data.srcfile) {
13121             this.updateInput();
13122             return;
13123         }
13124             
13125         var _t = this;
13126         var reader = new FileReader();
13127         reader.addEventListener("load", function() {  
13128             data.srcdata =  reader.result;
13129             _t.updateInput();
13130         });
13131         reader.readAsDataURL(data.srcfile);
13132         
13133         
13134         
13135     },
13136     removeCard : function(id)
13137     {
13138         
13139         var card  = this.fileCollection.get(id);
13140         card.data.is_deleted = 1;
13141         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13142         //this.fileCollection.remove(card);
13143         //this.items = this.items.filter(function(e) { return e != card });
13144         // dont' really need ot update items.
13145         card.el.dom.parentNode.removeChild(card.el.dom);
13146         this.updateInput();
13147
13148         
13149     },
13150     reset: function()
13151     {
13152         this.fileCollection.each(function(card) {
13153             if (card.el.dom && card.el.dom.parentNode) {
13154                 card.el.dom.parentNode.removeChild(card.el.dom);
13155             }
13156         });
13157         this.fileCollection.clear();
13158         this.updateInput();
13159     },
13160     
13161     updateInput : function()
13162     {
13163          var data = [];
13164         this.fileCollection.each(function(e) {
13165             data.push(e.data);
13166             
13167         });
13168         this.inputEl().dom.value = JSON.stringify(data);
13169         
13170         
13171         
13172     }
13173     
13174     
13175 });
13176
13177
13178 Roo.bootstrap.CardUploader.ID = -1;/*
13179  * Based on:
13180  * Ext JS Library 1.1.1
13181  * Copyright(c) 2006-2007, Ext JS, LLC.
13182  *
13183  * Originally Released Under LGPL - original licence link has changed is not relivant.
13184  *
13185  * Fork - LGPL
13186  * <script type="text/javascript">
13187  */
13188
13189
13190 /**
13191  * @class Roo.data.SortTypes
13192  * @singleton
13193  * Defines the default sorting (casting?) comparison functions used when sorting data.
13194  */
13195 Roo.data.SortTypes = {
13196     /**
13197      * Default sort that does nothing
13198      * @param {Mixed} s The value being converted
13199      * @return {Mixed} The comparison value
13200      */
13201     none : function(s){
13202         return s;
13203     },
13204     
13205     /**
13206      * The regular expression used to strip tags
13207      * @type {RegExp}
13208      * @property
13209      */
13210     stripTagsRE : /<\/?[^>]+>/gi,
13211     
13212     /**
13213      * Strips all HTML tags to sort on text only
13214      * @param {Mixed} s The value being converted
13215      * @return {String} The comparison value
13216      */
13217     asText : function(s){
13218         return String(s).replace(this.stripTagsRE, "");
13219     },
13220     
13221     /**
13222      * Strips all HTML tags to sort on text only - Case insensitive
13223      * @param {Mixed} s The value being converted
13224      * @return {String} The comparison value
13225      */
13226     asUCText : function(s){
13227         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13228     },
13229     
13230     /**
13231      * Case insensitive string
13232      * @param {Mixed} s The value being converted
13233      * @return {String} The comparison value
13234      */
13235     asUCString : function(s) {
13236         return String(s).toUpperCase();
13237     },
13238     
13239     /**
13240      * Date sorting
13241      * @param {Mixed} s The value being converted
13242      * @return {Number} The comparison value
13243      */
13244     asDate : function(s) {
13245         if(!s){
13246             return 0;
13247         }
13248         if(s instanceof Date){
13249             return s.getTime();
13250         }
13251         return Date.parse(String(s));
13252     },
13253     
13254     /**
13255      * Float sorting
13256      * @param {Mixed} s The value being converted
13257      * @return {Float} The comparison value
13258      */
13259     asFloat : function(s) {
13260         var val = parseFloat(String(s).replace(/,/g, ""));
13261         if(isNaN(val)) {
13262             val = 0;
13263         }
13264         return val;
13265     },
13266     
13267     /**
13268      * Integer sorting
13269      * @param {Mixed} s The value being converted
13270      * @return {Number} The comparison value
13271      */
13272     asInt : function(s) {
13273         var val = parseInt(String(s).replace(/,/g, ""));
13274         if(isNaN(val)) {
13275             val = 0;
13276         }
13277         return val;
13278     }
13279 };/*
13280  * Based on:
13281  * Ext JS Library 1.1.1
13282  * Copyright(c) 2006-2007, Ext JS, LLC.
13283  *
13284  * Originally Released Under LGPL - original licence link has changed is not relivant.
13285  *
13286  * Fork - LGPL
13287  * <script type="text/javascript">
13288  */
13289
13290 /**
13291 * @class Roo.data.Record
13292  * Instances of this class encapsulate both record <em>definition</em> information, and record
13293  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13294  * to access Records cached in an {@link Roo.data.Store} object.<br>
13295  * <p>
13296  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13297  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13298  * objects.<br>
13299  * <p>
13300  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13301  * @constructor
13302  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13303  * {@link #create}. The parameters are the same.
13304  * @param {Array} data An associative Array of data values keyed by the field name.
13305  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13306  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13307  * not specified an integer id is generated.
13308  */
13309 Roo.data.Record = function(data, id){
13310     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13311     this.data = data;
13312 };
13313
13314 /**
13315  * Generate a constructor for a specific record layout.
13316  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13317  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13318  * Each field definition object may contain the following properties: <ul>
13319  * <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,
13320  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13321  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13322  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13323  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13324  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13325  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13326  * this may be omitted.</p></li>
13327  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13328  * <ul><li>auto (Default, implies no conversion)</li>
13329  * <li>string</li>
13330  * <li>int</li>
13331  * <li>float</li>
13332  * <li>boolean</li>
13333  * <li>date</li></ul></p></li>
13334  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13335  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13336  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13337  * by the Reader into an object that will be stored in the Record. It is passed the
13338  * following parameters:<ul>
13339  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13340  * </ul></p></li>
13341  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13342  * </ul>
13343  * <br>usage:<br><pre><code>
13344 var TopicRecord = Roo.data.Record.create(
13345     {name: 'title', mapping: 'topic_title'},
13346     {name: 'author', mapping: 'username'},
13347     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13348     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13349     {name: 'lastPoster', mapping: 'user2'},
13350     {name: 'excerpt', mapping: 'post_text'}
13351 );
13352
13353 var myNewRecord = new TopicRecord({
13354     title: 'Do my job please',
13355     author: 'noobie',
13356     totalPosts: 1,
13357     lastPost: new Date(),
13358     lastPoster: 'Animal',
13359     excerpt: 'No way dude!'
13360 });
13361 myStore.add(myNewRecord);
13362 </code></pre>
13363  * @method create
13364  * @static
13365  */
13366 Roo.data.Record.create = function(o){
13367     var f = function(){
13368         f.superclass.constructor.apply(this, arguments);
13369     };
13370     Roo.extend(f, Roo.data.Record);
13371     var p = f.prototype;
13372     p.fields = new Roo.util.MixedCollection(false, function(field){
13373         return field.name;
13374     });
13375     for(var i = 0, len = o.length; i < len; i++){
13376         p.fields.add(new Roo.data.Field(o[i]));
13377     }
13378     f.getField = function(name){
13379         return p.fields.get(name);  
13380     };
13381     return f;
13382 };
13383
13384 Roo.data.Record.AUTO_ID = 1000;
13385 Roo.data.Record.EDIT = 'edit';
13386 Roo.data.Record.REJECT = 'reject';
13387 Roo.data.Record.COMMIT = 'commit';
13388
13389 Roo.data.Record.prototype = {
13390     /**
13391      * Readonly flag - true if this record has been modified.
13392      * @type Boolean
13393      */
13394     dirty : false,
13395     editing : false,
13396     error: null,
13397     modified: null,
13398
13399     // private
13400     join : function(store){
13401         this.store = store;
13402     },
13403
13404     /**
13405      * Set the named field to the specified value.
13406      * @param {String} name The name of the field to set.
13407      * @param {Object} value The value to set the field to.
13408      */
13409     set : function(name, value){
13410         if(this.data[name] == value){
13411             return;
13412         }
13413         this.dirty = true;
13414         if(!this.modified){
13415             this.modified = {};
13416         }
13417         if(typeof this.modified[name] == 'undefined'){
13418             this.modified[name] = this.data[name];
13419         }
13420         this.data[name] = value;
13421         if(!this.editing && this.store){
13422             this.store.afterEdit(this);
13423         }       
13424     },
13425
13426     /**
13427      * Get the value of the named field.
13428      * @param {String} name The name of the field to get the value of.
13429      * @return {Object} The value of the field.
13430      */
13431     get : function(name){
13432         return this.data[name]; 
13433     },
13434
13435     // private
13436     beginEdit : function(){
13437         this.editing = true;
13438         this.modified = {}; 
13439     },
13440
13441     // private
13442     cancelEdit : function(){
13443         this.editing = false;
13444         delete this.modified;
13445     },
13446
13447     // private
13448     endEdit : function(){
13449         this.editing = false;
13450         if(this.dirty && this.store){
13451             this.store.afterEdit(this);
13452         }
13453     },
13454
13455     /**
13456      * Usually called by the {@link Roo.data.Store} which owns the Record.
13457      * Rejects all changes made to the Record since either creation, or the last commit operation.
13458      * Modified fields are reverted to their original values.
13459      * <p>
13460      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13461      * of reject operations.
13462      */
13463     reject : function(){
13464         var m = this.modified;
13465         for(var n in m){
13466             if(typeof m[n] != "function"){
13467                 this.data[n] = m[n];
13468             }
13469         }
13470         this.dirty = false;
13471         delete this.modified;
13472         this.editing = false;
13473         if(this.store){
13474             this.store.afterReject(this);
13475         }
13476     },
13477
13478     /**
13479      * Usually called by the {@link Roo.data.Store} which owns the Record.
13480      * Commits all changes made to the Record since either creation, or the last commit operation.
13481      * <p>
13482      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13483      * of commit operations.
13484      */
13485     commit : function(){
13486         this.dirty = false;
13487         delete this.modified;
13488         this.editing = false;
13489         if(this.store){
13490             this.store.afterCommit(this);
13491         }
13492     },
13493
13494     // private
13495     hasError : function(){
13496         return this.error != null;
13497     },
13498
13499     // private
13500     clearError : function(){
13501         this.error = null;
13502     },
13503
13504     /**
13505      * Creates a copy of this record.
13506      * @param {String} id (optional) A new record id if you don't want to use this record's id
13507      * @return {Record}
13508      */
13509     copy : function(newId) {
13510         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13511     }
13512 };/*
13513  * Based on:
13514  * Ext JS Library 1.1.1
13515  * Copyright(c) 2006-2007, Ext JS, LLC.
13516  *
13517  * Originally Released Under LGPL - original licence link has changed is not relivant.
13518  *
13519  * Fork - LGPL
13520  * <script type="text/javascript">
13521  */
13522
13523
13524
13525 /**
13526  * @class Roo.data.Store
13527  * @extends Roo.util.Observable
13528  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13529  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13530  * <p>
13531  * 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
13532  * has no knowledge of the format of the data returned by the Proxy.<br>
13533  * <p>
13534  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13535  * instances from the data object. These records are cached and made available through accessor functions.
13536  * @constructor
13537  * Creates a new Store.
13538  * @param {Object} config A config object containing the objects needed for the Store to access data,
13539  * and read the data into Records.
13540  */
13541 Roo.data.Store = function(config){
13542     this.data = new Roo.util.MixedCollection(false);
13543     this.data.getKey = function(o){
13544         return o.id;
13545     };
13546     this.baseParams = {};
13547     // private
13548     this.paramNames = {
13549         "start" : "start",
13550         "limit" : "limit",
13551         "sort" : "sort",
13552         "dir" : "dir",
13553         "multisort" : "_multisort"
13554     };
13555
13556     if(config && config.data){
13557         this.inlineData = config.data;
13558         delete config.data;
13559     }
13560
13561     Roo.apply(this, config);
13562     
13563     if(this.reader){ // reader passed
13564         this.reader = Roo.factory(this.reader, Roo.data);
13565         this.reader.xmodule = this.xmodule || false;
13566         if(!this.recordType){
13567             this.recordType = this.reader.recordType;
13568         }
13569         if(this.reader.onMetaChange){
13570             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13571         }
13572     }
13573
13574     if(this.recordType){
13575         this.fields = this.recordType.prototype.fields;
13576     }
13577     this.modified = [];
13578
13579     this.addEvents({
13580         /**
13581          * @event datachanged
13582          * Fires when the data cache has changed, and a widget which is using this Store
13583          * as a Record cache should refresh its view.
13584          * @param {Store} this
13585          */
13586         datachanged : true,
13587         /**
13588          * @event metachange
13589          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13590          * @param {Store} this
13591          * @param {Object} meta The JSON metadata
13592          */
13593         metachange : true,
13594         /**
13595          * @event add
13596          * Fires when Records have been added to the Store
13597          * @param {Store} this
13598          * @param {Roo.data.Record[]} records The array of Records added
13599          * @param {Number} index The index at which the record(s) were added
13600          */
13601         add : true,
13602         /**
13603          * @event remove
13604          * Fires when a Record has been removed from the Store
13605          * @param {Store} this
13606          * @param {Roo.data.Record} record The Record that was removed
13607          * @param {Number} index The index at which the record was removed
13608          */
13609         remove : true,
13610         /**
13611          * @event update
13612          * Fires when a Record has been updated
13613          * @param {Store} this
13614          * @param {Roo.data.Record} record The Record that was updated
13615          * @param {String} operation The update operation being performed.  Value may be one of:
13616          * <pre><code>
13617  Roo.data.Record.EDIT
13618  Roo.data.Record.REJECT
13619  Roo.data.Record.COMMIT
13620          * </code></pre>
13621          */
13622         update : true,
13623         /**
13624          * @event clear
13625          * Fires when the data cache has been cleared.
13626          * @param {Store} this
13627          */
13628         clear : true,
13629         /**
13630          * @event beforeload
13631          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13632          * the load action will be canceled.
13633          * @param {Store} this
13634          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13635          */
13636         beforeload : true,
13637         /**
13638          * @event beforeloadadd
13639          * Fires after a new set of Records has been loaded.
13640          * @param {Store} this
13641          * @param {Roo.data.Record[]} records The Records that were loaded
13642          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13643          */
13644         beforeloadadd : true,
13645         /**
13646          * @event load
13647          * Fires after a new set of Records has been loaded, before they are added to the store.
13648          * @param {Store} this
13649          * @param {Roo.data.Record[]} records The Records that were loaded
13650          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13651          * @params {Object} return from reader
13652          */
13653         load : true,
13654         /**
13655          * @event loadexception
13656          * Fires if an exception occurs in the Proxy during loading.
13657          * Called with the signature of the Proxy's "loadexception" event.
13658          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13659          * 
13660          * @param {Proxy} 
13661          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13662          * @param {Object} load options 
13663          * @param {Object} jsonData from your request (normally this contains the Exception)
13664          */
13665         loadexception : true
13666     });
13667     
13668     if(this.proxy){
13669         this.proxy = Roo.factory(this.proxy, Roo.data);
13670         this.proxy.xmodule = this.xmodule || false;
13671         this.relayEvents(this.proxy,  ["loadexception"]);
13672     }
13673     this.sortToggle = {};
13674     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13675
13676     Roo.data.Store.superclass.constructor.call(this);
13677
13678     if(this.inlineData){
13679         this.loadData(this.inlineData);
13680         delete this.inlineData;
13681     }
13682 };
13683
13684 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13685      /**
13686     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13687     * without a remote query - used by combo/forms at present.
13688     */
13689     
13690     /**
13691     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13692     */
13693     /**
13694     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13695     */
13696     /**
13697     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13698     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13699     */
13700     /**
13701     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13702     * on any HTTP request
13703     */
13704     /**
13705     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13706     */
13707     /**
13708     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13709     */
13710     multiSort: false,
13711     /**
13712     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13713     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13714     */
13715     remoteSort : false,
13716
13717     /**
13718     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13719      * loaded or when a record is removed. (defaults to false).
13720     */
13721     pruneModifiedRecords : false,
13722
13723     // private
13724     lastOptions : null,
13725
13726     /**
13727      * Add Records to the Store and fires the add event.
13728      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13729      */
13730     add : function(records){
13731         records = [].concat(records);
13732         for(var i = 0, len = records.length; i < len; i++){
13733             records[i].join(this);
13734         }
13735         var index = this.data.length;
13736         this.data.addAll(records);
13737         this.fireEvent("add", this, records, index);
13738     },
13739
13740     /**
13741      * Remove a Record from the Store and fires the remove event.
13742      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13743      */
13744     remove : function(record){
13745         var index = this.data.indexOf(record);
13746         this.data.removeAt(index);
13747  
13748         if(this.pruneModifiedRecords){
13749             this.modified.remove(record);
13750         }
13751         this.fireEvent("remove", this, record, index);
13752     },
13753
13754     /**
13755      * Remove all Records from the Store and fires the clear event.
13756      */
13757     removeAll : function(){
13758         this.data.clear();
13759         if(this.pruneModifiedRecords){
13760             this.modified = [];
13761         }
13762         this.fireEvent("clear", this);
13763     },
13764
13765     /**
13766      * Inserts Records to the Store at the given index and fires the add event.
13767      * @param {Number} index The start index at which to insert the passed Records.
13768      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13769      */
13770     insert : function(index, records){
13771         records = [].concat(records);
13772         for(var i = 0, len = records.length; i < len; i++){
13773             this.data.insert(index, records[i]);
13774             records[i].join(this);
13775         }
13776         this.fireEvent("add", this, records, index);
13777     },
13778
13779     /**
13780      * Get the index within the cache of the passed Record.
13781      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13782      * @return {Number} The index of the passed Record. Returns -1 if not found.
13783      */
13784     indexOf : function(record){
13785         return this.data.indexOf(record);
13786     },
13787
13788     /**
13789      * Get the index within the cache of the Record with the passed id.
13790      * @param {String} id The id of the Record to find.
13791      * @return {Number} The index of the Record. Returns -1 if not found.
13792      */
13793     indexOfId : function(id){
13794         return this.data.indexOfKey(id);
13795     },
13796
13797     /**
13798      * Get the Record with the specified id.
13799      * @param {String} id The id of the Record to find.
13800      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13801      */
13802     getById : function(id){
13803         return this.data.key(id);
13804     },
13805
13806     /**
13807      * Get the Record at the specified index.
13808      * @param {Number} index The index of the Record to find.
13809      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13810      */
13811     getAt : function(index){
13812         return this.data.itemAt(index);
13813     },
13814
13815     /**
13816      * Returns a range of Records between specified indices.
13817      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13818      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13819      * @return {Roo.data.Record[]} An array of Records
13820      */
13821     getRange : function(start, end){
13822         return this.data.getRange(start, end);
13823     },
13824
13825     // private
13826     storeOptions : function(o){
13827         o = Roo.apply({}, o);
13828         delete o.callback;
13829         delete o.scope;
13830         this.lastOptions = o;
13831     },
13832
13833     /**
13834      * Loads the Record cache from the configured Proxy using the configured Reader.
13835      * <p>
13836      * If using remote paging, then the first load call must specify the <em>start</em>
13837      * and <em>limit</em> properties in the options.params property to establish the initial
13838      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13839      * <p>
13840      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13841      * and this call will return before the new data has been loaded. Perform any post-processing
13842      * in a callback function, or in a "load" event handler.</strong>
13843      * <p>
13844      * @param {Object} options An object containing properties which control loading options:<ul>
13845      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13846      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13847      * passed the following arguments:<ul>
13848      * <li>r : Roo.data.Record[]</li>
13849      * <li>options: Options object from the load call</li>
13850      * <li>success: Boolean success indicator</li></ul></li>
13851      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13852      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13853      * </ul>
13854      */
13855     load : function(options){
13856         options = options || {};
13857         if(this.fireEvent("beforeload", this, options) !== false){
13858             this.storeOptions(options);
13859             var p = Roo.apply(options.params || {}, this.baseParams);
13860             // if meta was not loaded from remote source.. try requesting it.
13861             if (!this.reader.metaFromRemote) {
13862                 p._requestMeta = 1;
13863             }
13864             if(this.sortInfo && this.remoteSort){
13865                 var pn = this.paramNames;
13866                 p[pn["sort"]] = this.sortInfo.field;
13867                 p[pn["dir"]] = this.sortInfo.direction;
13868             }
13869             if (this.multiSort) {
13870                 var pn = this.paramNames;
13871                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13872             }
13873             
13874             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13875         }
13876     },
13877
13878     /**
13879      * Reloads the Record cache from the configured Proxy using the configured Reader and
13880      * the options from the last load operation performed.
13881      * @param {Object} options (optional) An object containing properties which may override the options
13882      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13883      * the most recently used options are reused).
13884      */
13885     reload : function(options){
13886         this.load(Roo.applyIf(options||{}, this.lastOptions));
13887     },
13888
13889     // private
13890     // Called as a callback by the Reader during a load operation.
13891     loadRecords : function(o, options, success){
13892         if(!o || success === false){
13893             if(success !== false){
13894                 this.fireEvent("load", this, [], options, o);
13895             }
13896             if(options.callback){
13897                 options.callback.call(options.scope || this, [], options, false);
13898             }
13899             return;
13900         }
13901         // if data returned failure - throw an exception.
13902         if (o.success === false) {
13903             // show a message if no listener is registered.
13904             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13905                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13906             }
13907             // loadmask wil be hooked into this..
13908             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13909             return;
13910         }
13911         var r = o.records, t = o.totalRecords || r.length;
13912         
13913         this.fireEvent("beforeloadadd", this, r, options, o);
13914         
13915         if(!options || options.add !== true){
13916             if(this.pruneModifiedRecords){
13917                 this.modified = [];
13918             }
13919             for(var i = 0, len = r.length; i < len; i++){
13920                 r[i].join(this);
13921             }
13922             if(this.snapshot){
13923                 this.data = this.snapshot;
13924                 delete this.snapshot;
13925             }
13926             this.data.clear();
13927             this.data.addAll(r);
13928             this.totalLength = t;
13929             this.applySort();
13930             this.fireEvent("datachanged", this);
13931         }else{
13932             this.totalLength = Math.max(t, this.data.length+r.length);
13933             this.add(r);
13934         }
13935         
13936         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13937                 
13938             var e = new Roo.data.Record({});
13939
13940             e.set(this.parent.displayField, this.parent.emptyTitle);
13941             e.set(this.parent.valueField, '');
13942
13943             this.insert(0, e);
13944         }
13945             
13946         this.fireEvent("load", this, r, options, o);
13947         if(options.callback){
13948             options.callback.call(options.scope || this, r, options, true);
13949         }
13950     },
13951
13952
13953     /**
13954      * Loads data from a passed data block. A Reader which understands the format of the data
13955      * must have been configured in the constructor.
13956      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13957      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13958      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13959      */
13960     loadData : function(o, append){
13961         var r = this.reader.readRecords(o);
13962         this.loadRecords(r, {add: append}, true);
13963     },
13964     
13965      /**
13966      * using 'cn' the nested child reader read the child array into it's child stores.
13967      * @param {Object} rec The record with a 'children array
13968      */
13969     loadDataFromChildren : function(rec)
13970     {
13971         this.loadData(this.reader.toLoadData(rec));
13972     },
13973     
13974
13975     /**
13976      * Gets the number of cached records.
13977      * <p>
13978      * <em>If using paging, this may not be the total size of the dataset. If the data object
13979      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13980      * the data set size</em>
13981      */
13982     getCount : function(){
13983         return this.data.length || 0;
13984     },
13985
13986     /**
13987      * Gets the total number of records in the dataset as returned by the server.
13988      * <p>
13989      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13990      * the dataset size</em>
13991      */
13992     getTotalCount : function(){
13993         return this.totalLength || 0;
13994     },
13995
13996     /**
13997      * Returns the sort state of the Store as an object with two properties:
13998      * <pre><code>
13999  field {String} The name of the field by which the Records are sorted
14000  direction {String} The sort order, "ASC" or "DESC"
14001      * </code></pre>
14002      */
14003     getSortState : function(){
14004         return this.sortInfo;
14005     },
14006
14007     // private
14008     applySort : function(){
14009         if(this.sortInfo && !this.remoteSort){
14010             var s = this.sortInfo, f = s.field;
14011             var st = this.fields.get(f).sortType;
14012             var fn = function(r1, r2){
14013                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14014                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14015             };
14016             this.data.sort(s.direction, fn);
14017             if(this.snapshot && this.snapshot != this.data){
14018                 this.snapshot.sort(s.direction, fn);
14019             }
14020         }
14021     },
14022
14023     /**
14024      * Sets the default sort column and order to be used by the next load operation.
14025      * @param {String} fieldName The name of the field to sort by.
14026      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14027      */
14028     setDefaultSort : function(field, dir){
14029         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14030     },
14031
14032     /**
14033      * Sort the Records.
14034      * If remote sorting is used, the sort is performed on the server, and the cache is
14035      * reloaded. If local sorting is used, the cache is sorted internally.
14036      * @param {String} fieldName The name of the field to sort by.
14037      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14038      */
14039     sort : function(fieldName, dir){
14040         var f = this.fields.get(fieldName);
14041         if(!dir){
14042             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14043             
14044             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14045                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14046             }else{
14047                 dir = f.sortDir;
14048             }
14049         }
14050         this.sortToggle[f.name] = dir;
14051         this.sortInfo = {field: f.name, direction: dir};
14052         if(!this.remoteSort){
14053             this.applySort();
14054             this.fireEvent("datachanged", this);
14055         }else{
14056             this.load(this.lastOptions);
14057         }
14058     },
14059
14060     /**
14061      * Calls the specified function for each of the Records in the cache.
14062      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14063      * Returning <em>false</em> aborts and exits the iteration.
14064      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14065      */
14066     each : function(fn, scope){
14067         this.data.each(fn, scope);
14068     },
14069
14070     /**
14071      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14072      * (e.g., during paging).
14073      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14074      */
14075     getModifiedRecords : function(){
14076         return this.modified;
14077     },
14078
14079     // private
14080     createFilterFn : function(property, value, anyMatch){
14081         if(!value.exec){ // not a regex
14082             value = String(value);
14083             if(value.length == 0){
14084                 return false;
14085             }
14086             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14087         }
14088         return function(r){
14089             return value.test(r.data[property]);
14090         };
14091     },
14092
14093     /**
14094      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14095      * @param {String} property A field on your records
14096      * @param {Number} start The record index to start at (defaults to 0)
14097      * @param {Number} end The last record index to include (defaults to length - 1)
14098      * @return {Number} The sum
14099      */
14100     sum : function(property, start, end){
14101         var rs = this.data.items, v = 0;
14102         start = start || 0;
14103         end = (end || end === 0) ? end : rs.length-1;
14104
14105         for(var i = start; i <= end; i++){
14106             v += (rs[i].data[property] || 0);
14107         }
14108         return v;
14109     },
14110
14111     /**
14112      * Filter the records by a specified property.
14113      * @param {String} field A field on your records
14114      * @param {String/RegExp} value Either a string that the field
14115      * should start with or a RegExp to test against the field
14116      * @param {Boolean} anyMatch True to match any part not just the beginning
14117      */
14118     filter : function(property, value, anyMatch){
14119         var fn = this.createFilterFn(property, value, anyMatch);
14120         return fn ? this.filterBy(fn) : this.clearFilter();
14121     },
14122
14123     /**
14124      * Filter by a function. The specified function will be called with each
14125      * record in this data source. If the function returns true the record is included,
14126      * otherwise it is filtered.
14127      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14128      * @param {Object} scope (optional) The scope of the function (defaults to this)
14129      */
14130     filterBy : function(fn, scope){
14131         this.snapshot = this.snapshot || this.data;
14132         this.data = this.queryBy(fn, scope||this);
14133         this.fireEvent("datachanged", this);
14134     },
14135
14136     /**
14137      * Query the records by a specified property.
14138      * @param {String} field A field on your records
14139      * @param {String/RegExp} value Either a string that the field
14140      * should start with or a RegExp to test against the field
14141      * @param {Boolean} anyMatch True to match any part not just the beginning
14142      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14143      */
14144     query : function(property, value, anyMatch){
14145         var fn = this.createFilterFn(property, value, anyMatch);
14146         return fn ? this.queryBy(fn) : this.data.clone();
14147     },
14148
14149     /**
14150      * Query by a function. The specified function will be called with each
14151      * record in this data source. If the function returns true the record is included
14152      * in the results.
14153      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14154      * @param {Object} scope (optional) The scope of the function (defaults to this)
14155       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14156      **/
14157     queryBy : function(fn, scope){
14158         var data = this.snapshot || this.data;
14159         return data.filterBy(fn, scope||this);
14160     },
14161
14162     /**
14163      * Collects unique values for a particular dataIndex from this store.
14164      * @param {String} dataIndex The property to collect
14165      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14166      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14167      * @return {Array} An array of the unique values
14168      **/
14169     collect : function(dataIndex, allowNull, bypassFilter){
14170         var d = (bypassFilter === true && this.snapshot) ?
14171                 this.snapshot.items : this.data.items;
14172         var v, sv, r = [], l = {};
14173         for(var i = 0, len = d.length; i < len; i++){
14174             v = d[i].data[dataIndex];
14175             sv = String(v);
14176             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14177                 l[sv] = true;
14178                 r[r.length] = v;
14179             }
14180         }
14181         return r;
14182     },
14183
14184     /**
14185      * Revert to a view of the Record cache with no filtering applied.
14186      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14187      */
14188     clearFilter : function(suppressEvent){
14189         if(this.snapshot && this.snapshot != this.data){
14190             this.data = this.snapshot;
14191             delete this.snapshot;
14192             if(suppressEvent !== true){
14193                 this.fireEvent("datachanged", this);
14194             }
14195         }
14196     },
14197
14198     // private
14199     afterEdit : function(record){
14200         if(this.modified.indexOf(record) == -1){
14201             this.modified.push(record);
14202         }
14203         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14204     },
14205     
14206     // private
14207     afterReject : function(record){
14208         this.modified.remove(record);
14209         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14210     },
14211
14212     // private
14213     afterCommit : function(record){
14214         this.modified.remove(record);
14215         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14216     },
14217
14218     /**
14219      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14220      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14221      */
14222     commitChanges : function(){
14223         var m = this.modified.slice(0);
14224         this.modified = [];
14225         for(var i = 0, len = m.length; i < len; i++){
14226             m[i].commit();
14227         }
14228     },
14229
14230     /**
14231      * Cancel outstanding changes on all changed records.
14232      */
14233     rejectChanges : function(){
14234         var m = this.modified.slice(0);
14235         this.modified = [];
14236         for(var i = 0, len = m.length; i < len; i++){
14237             m[i].reject();
14238         }
14239     },
14240
14241     onMetaChange : function(meta, rtype, o){
14242         this.recordType = rtype;
14243         this.fields = rtype.prototype.fields;
14244         delete this.snapshot;
14245         this.sortInfo = meta.sortInfo || this.sortInfo;
14246         this.modified = [];
14247         this.fireEvent('metachange', this, this.reader.meta);
14248     },
14249     
14250     moveIndex : function(data, type)
14251     {
14252         var index = this.indexOf(data);
14253         
14254         var newIndex = index + type;
14255         
14256         this.remove(data);
14257         
14258         this.insert(newIndex, data);
14259         
14260     }
14261 });/*
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  * @class Roo.data.SimpleStore
14274  * @extends Roo.data.Store
14275  * Small helper class to make creating Stores from Array data easier.
14276  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14277  * @cfg {Array} fields An array of field definition objects, or field name strings.
14278  * @cfg {Object} an existing reader (eg. copied from another store)
14279  * @cfg {Array} data The multi-dimensional array of data
14280  * @constructor
14281  * @param {Object} config
14282  */
14283 Roo.data.SimpleStore = function(config)
14284 {
14285     Roo.data.SimpleStore.superclass.constructor.call(this, {
14286         isLocal : true,
14287         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14288                 id: config.id
14289             },
14290             Roo.data.Record.create(config.fields)
14291         ),
14292         proxy : new Roo.data.MemoryProxy(config.data)
14293     });
14294     this.load();
14295 };
14296 Roo.extend(Roo.data.SimpleStore, 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 /**
14309  * @extends Roo.data.Store
14310  * @class Roo.data.JsonStore
14311  * Small helper class to make creating Stores for JSON data easier. <br/>
14312 <pre><code>
14313 var store = new Roo.data.JsonStore({
14314     url: 'get-images.php',
14315     root: 'images',
14316     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14317 });
14318 </code></pre>
14319  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14320  * JsonReader and HttpProxy (unless inline data is provided).</b>
14321  * @cfg {Array} fields An array of field definition objects, or field name strings.
14322  * @constructor
14323  * @param {Object} config
14324  */
14325 Roo.data.JsonStore = function(c){
14326     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14327         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14328         reader: new Roo.data.JsonReader(c, c.fields)
14329     }));
14330 };
14331 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14332  * Based on:
14333  * Ext JS Library 1.1.1
14334  * Copyright(c) 2006-2007, Ext JS, LLC.
14335  *
14336  * Originally Released Under LGPL - original licence link has changed is not relivant.
14337  *
14338  * Fork - LGPL
14339  * <script type="text/javascript">
14340  */
14341
14342  
14343 Roo.data.Field = function(config){
14344     if(typeof config == "string"){
14345         config = {name: config};
14346     }
14347     Roo.apply(this, config);
14348     
14349     if(!this.type){
14350         this.type = "auto";
14351     }
14352     
14353     var st = Roo.data.SortTypes;
14354     // named sortTypes are supported, here we look them up
14355     if(typeof this.sortType == "string"){
14356         this.sortType = st[this.sortType];
14357     }
14358     
14359     // set default sortType for strings and dates
14360     if(!this.sortType){
14361         switch(this.type){
14362             case "string":
14363                 this.sortType = st.asUCString;
14364                 break;
14365             case "date":
14366                 this.sortType = st.asDate;
14367                 break;
14368             default:
14369                 this.sortType = st.none;
14370         }
14371     }
14372
14373     // define once
14374     var stripRe = /[\$,%]/g;
14375
14376     // prebuilt conversion function for this field, instead of
14377     // switching every time we're reading a value
14378     if(!this.convert){
14379         var cv, dateFormat = this.dateFormat;
14380         switch(this.type){
14381             case "":
14382             case "auto":
14383             case undefined:
14384                 cv = function(v){ return v; };
14385                 break;
14386             case "string":
14387                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14388                 break;
14389             case "int":
14390                 cv = function(v){
14391                     return v !== undefined && v !== null && v !== '' ?
14392                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14393                     };
14394                 break;
14395             case "float":
14396                 cv = function(v){
14397                     return v !== undefined && v !== null && v !== '' ?
14398                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14399                     };
14400                 break;
14401             case "bool":
14402             case "boolean":
14403                 cv = function(v){ return v === true || v === "true" || v == 1; };
14404                 break;
14405             case "date":
14406                 cv = function(v){
14407                     if(!v){
14408                         return '';
14409                     }
14410                     if(v instanceof Date){
14411                         return v;
14412                     }
14413                     if(dateFormat){
14414                         if(dateFormat == "timestamp"){
14415                             return new Date(v*1000);
14416                         }
14417                         return Date.parseDate(v, dateFormat);
14418                     }
14419                     var parsed = Date.parse(v);
14420                     return parsed ? new Date(parsed) : null;
14421                 };
14422              break;
14423             
14424         }
14425         this.convert = cv;
14426     }
14427 };
14428
14429 Roo.data.Field.prototype = {
14430     dateFormat: null,
14431     defaultValue: "",
14432     mapping: null,
14433     sortType : null,
14434     sortDir : "ASC"
14435 };/*
14436  * Based on:
14437  * Ext JS Library 1.1.1
14438  * Copyright(c) 2006-2007, Ext JS, LLC.
14439  *
14440  * Originally Released Under LGPL - original licence link has changed is not relivant.
14441  *
14442  * Fork - LGPL
14443  * <script type="text/javascript">
14444  */
14445  
14446 // Base class for reading structured data from a data source.  This class is intended to be
14447 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14448
14449 /**
14450  * @class Roo.data.DataReader
14451  * Base class for reading structured data from a data source.  This class is intended to be
14452  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14453  */
14454
14455 Roo.data.DataReader = function(meta, recordType){
14456     
14457     this.meta = meta;
14458     
14459     this.recordType = recordType instanceof Array ? 
14460         Roo.data.Record.create(recordType) : recordType;
14461 };
14462
14463 Roo.data.DataReader.prototype = {
14464     
14465     
14466     readerType : 'Data',
14467      /**
14468      * Create an empty record
14469      * @param {Object} data (optional) - overlay some values
14470      * @return {Roo.data.Record} record created.
14471      */
14472     newRow :  function(d) {
14473         var da =  {};
14474         this.recordType.prototype.fields.each(function(c) {
14475             switch( c.type) {
14476                 case 'int' : da[c.name] = 0; break;
14477                 case 'date' : da[c.name] = new Date(); break;
14478                 case 'float' : da[c.name] = 0.0; break;
14479                 case 'boolean' : da[c.name] = false; break;
14480                 default : da[c.name] = ""; break;
14481             }
14482             
14483         });
14484         return new this.recordType(Roo.apply(da, d));
14485     }
14486     
14487     
14488 };/*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498
14499 /**
14500  * @class Roo.data.DataProxy
14501  * @extends Roo.data.Observable
14502  * This class is an abstract base class for implementations which provide retrieval of
14503  * unformatted data objects.<br>
14504  * <p>
14505  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14506  * (of the appropriate type which knows how to parse the data object) to provide a block of
14507  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14508  * <p>
14509  * Custom implementations must implement the load method as described in
14510  * {@link Roo.data.HttpProxy#load}.
14511  */
14512 Roo.data.DataProxy = function(){
14513     this.addEvents({
14514         /**
14515          * @event beforeload
14516          * Fires before a network request is made to retrieve a data object.
14517          * @param {Object} This DataProxy object.
14518          * @param {Object} params The params parameter to the load function.
14519          */
14520         beforeload : true,
14521         /**
14522          * @event load
14523          * Fires before the load method's callback is called.
14524          * @param {Object} This DataProxy object.
14525          * @param {Object} o The data object.
14526          * @param {Object} arg The callback argument object passed to the load function.
14527          */
14528         load : true,
14529         /**
14530          * @event loadexception
14531          * Fires if an Exception occurs during data retrieval.
14532          * @param {Object} This DataProxy object.
14533          * @param {Object} o The data object.
14534          * @param {Object} arg The callback argument object passed to the load function.
14535          * @param {Object} e The Exception.
14536          */
14537         loadexception : true
14538     });
14539     Roo.data.DataProxy.superclass.constructor.call(this);
14540 };
14541
14542 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14543
14544     /**
14545      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14546      */
14547 /*
14548  * Based on:
14549  * Ext JS Library 1.1.1
14550  * Copyright(c) 2006-2007, Ext JS, LLC.
14551  *
14552  * Originally Released Under LGPL - original licence link has changed is not relivant.
14553  *
14554  * Fork - LGPL
14555  * <script type="text/javascript">
14556  */
14557 /**
14558  * @class Roo.data.MemoryProxy
14559  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14560  * to the Reader when its load method is called.
14561  * @constructor
14562  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14563  */
14564 Roo.data.MemoryProxy = function(data){
14565     if (data.data) {
14566         data = data.data;
14567     }
14568     Roo.data.MemoryProxy.superclass.constructor.call(this);
14569     this.data = data;
14570 };
14571
14572 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14573     
14574     /**
14575      * Load data from the requested source (in this case an in-memory
14576      * data object passed to the constructor), read the data object into
14577      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14578      * process that block using the passed callback.
14579      * @param {Object} params This parameter is not used by the MemoryProxy class.
14580      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14581      * object into a block of Roo.data.Records.
14582      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14583      * The function must be passed <ul>
14584      * <li>The Record block object</li>
14585      * <li>The "arg" argument from the load function</li>
14586      * <li>A boolean success indicator</li>
14587      * </ul>
14588      * @param {Object} scope The scope in which to call the callback
14589      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14590      */
14591     load : function(params, reader, callback, scope, arg){
14592         params = params || {};
14593         var result;
14594         try {
14595             result = reader.readRecords(params.data ? params.data :this.data);
14596         }catch(e){
14597             this.fireEvent("loadexception", this, arg, null, e);
14598             callback.call(scope, null, arg, false);
14599             return;
14600         }
14601         callback.call(scope, result, arg, true);
14602     },
14603     
14604     // private
14605     update : function(params, records){
14606         
14607     }
14608 });/*
14609  * Based on:
14610  * Ext JS Library 1.1.1
14611  * Copyright(c) 2006-2007, Ext JS, LLC.
14612  *
14613  * Originally Released Under LGPL - original licence link has changed is not relivant.
14614  *
14615  * Fork - LGPL
14616  * <script type="text/javascript">
14617  */
14618 /**
14619  * @class Roo.data.HttpProxy
14620  * @extends Roo.data.DataProxy
14621  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14622  * configured to reference a certain URL.<br><br>
14623  * <p>
14624  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14625  * from which the running page was served.<br><br>
14626  * <p>
14627  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14628  * <p>
14629  * Be aware that to enable the browser to parse an XML document, the server must set
14630  * the Content-Type header in the HTTP response to "text/xml".
14631  * @constructor
14632  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14633  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14634  * will be used to make the request.
14635  */
14636 Roo.data.HttpProxy = function(conn){
14637     Roo.data.HttpProxy.superclass.constructor.call(this);
14638     // is conn a conn config or a real conn?
14639     this.conn = conn;
14640     this.useAjax = !conn || !conn.events;
14641   
14642 };
14643
14644 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14645     // thse are take from connection...
14646     
14647     /**
14648      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14649      */
14650     /**
14651      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14652      * extra parameters to each request made by this object. (defaults to undefined)
14653      */
14654     /**
14655      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14656      *  to each request made by this object. (defaults to undefined)
14657      */
14658     /**
14659      * @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)
14660      */
14661     /**
14662      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14663      */
14664      /**
14665      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14666      * @type Boolean
14667      */
14668   
14669
14670     /**
14671      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14672      * @type Boolean
14673      */
14674     /**
14675      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14676      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14677      * a finer-grained basis than the DataProxy events.
14678      */
14679     getConnection : function(){
14680         return this.useAjax ? Roo.Ajax : this.conn;
14681     },
14682
14683     /**
14684      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14685      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14686      * process that block using the passed callback.
14687      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14688      * for the request to the remote server.
14689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14690      * object into a block of Roo.data.Records.
14691      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14692      * The function must be passed <ul>
14693      * <li>The Record block object</li>
14694      * <li>The "arg" argument from the load function</li>
14695      * <li>A boolean success indicator</li>
14696      * </ul>
14697      * @param {Object} scope The scope in which to call the callback
14698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14699      */
14700     load : function(params, reader, callback, scope, arg){
14701         if(this.fireEvent("beforeload", this, params) !== false){
14702             var  o = {
14703                 params : params || {},
14704                 request: {
14705                     callback : callback,
14706                     scope : scope,
14707                     arg : arg
14708                 },
14709                 reader: reader,
14710                 callback : this.loadResponse,
14711                 scope: this
14712             };
14713             if(this.useAjax){
14714                 Roo.applyIf(o, this.conn);
14715                 if(this.activeRequest){
14716                     Roo.Ajax.abort(this.activeRequest);
14717                 }
14718                 this.activeRequest = Roo.Ajax.request(o);
14719             }else{
14720                 this.conn.request(o);
14721             }
14722         }else{
14723             callback.call(scope||this, null, arg, false);
14724         }
14725     },
14726
14727     // private
14728     loadResponse : function(o, success, response){
14729         delete this.activeRequest;
14730         if(!success){
14731             this.fireEvent("loadexception", this, o, response);
14732             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14733             return;
14734         }
14735         var result;
14736         try {
14737             result = o.reader.read(response);
14738         }catch(e){
14739             this.fireEvent("loadexception", this, o, response, e);
14740             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14741             return;
14742         }
14743         
14744         this.fireEvent("load", this, o, o.request.arg);
14745         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14746     },
14747
14748     // private
14749     update : function(dataSet){
14750
14751     },
14752
14753     // private
14754     updateResponse : function(dataSet){
14755
14756     }
14757 });/*
14758  * Based on:
14759  * Ext JS Library 1.1.1
14760  * Copyright(c) 2006-2007, Ext JS, LLC.
14761  *
14762  * Originally Released Under LGPL - original licence link has changed is not relivant.
14763  *
14764  * Fork - LGPL
14765  * <script type="text/javascript">
14766  */
14767
14768 /**
14769  * @class Roo.data.ScriptTagProxy
14770  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14771  * other than the originating domain of the running page.<br><br>
14772  * <p>
14773  * <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
14774  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14775  * <p>
14776  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14777  * source code that is used as the source inside a &lt;script> tag.<br><br>
14778  * <p>
14779  * In order for the browser to process the returned data, the server must wrap the data object
14780  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14781  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14782  * depending on whether the callback name was passed:
14783  * <p>
14784  * <pre><code>
14785 boolean scriptTag = false;
14786 String cb = request.getParameter("callback");
14787 if (cb != null) {
14788     scriptTag = true;
14789     response.setContentType("text/javascript");
14790 } else {
14791     response.setContentType("application/x-json");
14792 }
14793 Writer out = response.getWriter();
14794 if (scriptTag) {
14795     out.write(cb + "(");
14796 }
14797 out.print(dataBlock.toJsonString());
14798 if (scriptTag) {
14799     out.write(");");
14800 }
14801 </pre></code>
14802  *
14803  * @constructor
14804  * @param {Object} config A configuration object.
14805  */
14806 Roo.data.ScriptTagProxy = function(config){
14807     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14808     Roo.apply(this, config);
14809     this.head = document.getElementsByTagName("head")[0];
14810 };
14811
14812 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14813
14814 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14815     /**
14816      * @cfg {String} url The URL from which to request the data object.
14817      */
14818     /**
14819      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14820      */
14821     timeout : 30000,
14822     /**
14823      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14824      * the server the name of the callback function set up by the load call to process the returned data object.
14825      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14826      * javascript output which calls this named function passing the data object as its only parameter.
14827      */
14828     callbackParam : "callback",
14829     /**
14830      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14831      * name to the request.
14832      */
14833     nocache : true,
14834
14835     /**
14836      * Load data from the configured URL, read the data object into
14837      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14838      * process that block using the passed callback.
14839      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14840      * for the request to the remote server.
14841      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14842      * object into a block of Roo.data.Records.
14843      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14844      * The function must be passed <ul>
14845      * <li>The Record block object</li>
14846      * <li>The "arg" argument from the load function</li>
14847      * <li>A boolean success indicator</li>
14848      * </ul>
14849      * @param {Object} scope The scope in which to call the callback
14850      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14851      */
14852     load : function(params, reader, callback, scope, arg){
14853         if(this.fireEvent("beforeload", this, params) !== false){
14854
14855             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14856
14857             var url = this.url;
14858             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14859             if(this.nocache){
14860                 url += "&_dc=" + (new Date().getTime());
14861             }
14862             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14863             var trans = {
14864                 id : transId,
14865                 cb : "stcCallback"+transId,
14866                 scriptId : "stcScript"+transId,
14867                 params : params,
14868                 arg : arg,
14869                 url : url,
14870                 callback : callback,
14871                 scope : scope,
14872                 reader : reader
14873             };
14874             var conn = this;
14875
14876             window[trans.cb] = function(o){
14877                 conn.handleResponse(o, trans);
14878             };
14879
14880             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14881
14882             if(this.autoAbort !== false){
14883                 this.abort();
14884             }
14885
14886             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14887
14888             var script = document.createElement("script");
14889             script.setAttribute("src", url);
14890             script.setAttribute("type", "text/javascript");
14891             script.setAttribute("id", trans.scriptId);
14892             this.head.appendChild(script);
14893
14894             this.trans = trans;
14895         }else{
14896             callback.call(scope||this, null, arg, false);
14897         }
14898     },
14899
14900     // private
14901     isLoading : function(){
14902         return this.trans ? true : false;
14903     },
14904
14905     /**
14906      * Abort the current server request.
14907      */
14908     abort : function(){
14909         if(this.isLoading()){
14910             this.destroyTrans(this.trans);
14911         }
14912     },
14913
14914     // private
14915     destroyTrans : function(trans, isLoaded){
14916         this.head.removeChild(document.getElementById(trans.scriptId));
14917         clearTimeout(trans.timeoutId);
14918         if(isLoaded){
14919             window[trans.cb] = undefined;
14920             try{
14921                 delete window[trans.cb];
14922             }catch(e){}
14923         }else{
14924             // if hasn't been loaded, wait for load to remove it to prevent script error
14925             window[trans.cb] = function(){
14926                 window[trans.cb] = undefined;
14927                 try{
14928                     delete window[trans.cb];
14929                 }catch(e){}
14930             };
14931         }
14932     },
14933
14934     // private
14935     handleResponse : function(o, trans){
14936         this.trans = false;
14937         this.destroyTrans(trans, true);
14938         var result;
14939         try {
14940             result = trans.reader.readRecords(o);
14941         }catch(e){
14942             this.fireEvent("loadexception", this, o, trans.arg, e);
14943             trans.callback.call(trans.scope||window, null, trans.arg, false);
14944             return;
14945         }
14946         this.fireEvent("load", this, o, trans.arg);
14947         trans.callback.call(trans.scope||window, result, trans.arg, true);
14948     },
14949
14950     // private
14951     handleFailure : function(trans){
14952         this.trans = false;
14953         this.destroyTrans(trans, false);
14954         this.fireEvent("loadexception", this, null, trans.arg);
14955         trans.callback.call(trans.scope||window, null, trans.arg, false);
14956     }
14957 });/*
14958  * Based on:
14959  * Ext JS Library 1.1.1
14960  * Copyright(c) 2006-2007, Ext JS, LLC.
14961  *
14962  * Originally Released Under LGPL - original licence link has changed is not relivant.
14963  *
14964  * Fork - LGPL
14965  * <script type="text/javascript">
14966  */
14967
14968 /**
14969  * @class Roo.data.JsonReader
14970  * @extends Roo.data.DataReader
14971  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14972  * based on mappings in a provided Roo.data.Record constructor.
14973  * 
14974  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14975  * in the reply previously. 
14976  * 
14977  * <p>
14978  * Example code:
14979  * <pre><code>
14980 var RecordDef = Roo.data.Record.create([
14981     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14982     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14983 ]);
14984 var myReader = new Roo.data.JsonReader({
14985     totalProperty: "results",    // The property which contains the total dataset size (optional)
14986     root: "rows",                // The property which contains an Array of row objects
14987     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14988 }, RecordDef);
14989 </code></pre>
14990  * <p>
14991  * This would consume a JSON file like this:
14992  * <pre><code>
14993 { 'results': 2, 'rows': [
14994     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14995     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14996 }
14997 </code></pre>
14998  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14999  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15000  * paged from the remote server.
15001  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15002  * @cfg {String} root name of the property which contains the Array of row objects.
15003  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15004  * @cfg {Array} fields Array of field definition objects
15005  * @constructor
15006  * Create a new JsonReader
15007  * @param {Object} meta Metadata configuration options
15008  * @param {Object} recordType Either an Array of field definition objects,
15009  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15010  */
15011 Roo.data.JsonReader = function(meta, recordType){
15012     
15013     meta = meta || {};
15014     // set some defaults:
15015     Roo.applyIf(meta, {
15016         totalProperty: 'total',
15017         successProperty : 'success',
15018         root : 'data',
15019         id : 'id'
15020     });
15021     
15022     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15023 };
15024 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15025     
15026     readerType : 'Json',
15027     
15028     /**
15029      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15030      * Used by Store query builder to append _requestMeta to params.
15031      * 
15032      */
15033     metaFromRemote : false,
15034     /**
15035      * This method is only used by a DataProxy which has retrieved data from a remote server.
15036      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15037      * @return {Object} data A data block which is used by an Roo.data.Store object as
15038      * a cache of Roo.data.Records.
15039      */
15040     read : function(response){
15041         var json = response.responseText;
15042        
15043         var o = /* eval:var:o */ eval("("+json+")");
15044         if(!o) {
15045             throw {message: "JsonReader.read: Json object not found"};
15046         }
15047         
15048         if(o.metaData){
15049             
15050             delete this.ef;
15051             this.metaFromRemote = true;
15052             this.meta = o.metaData;
15053             this.recordType = Roo.data.Record.create(o.metaData.fields);
15054             this.onMetaChange(this.meta, this.recordType, o);
15055         }
15056         return this.readRecords(o);
15057     },
15058
15059     // private function a store will implement
15060     onMetaChange : function(meta, recordType, o){
15061
15062     },
15063
15064     /**
15065          * @ignore
15066          */
15067     simpleAccess: function(obj, subsc) {
15068         return obj[subsc];
15069     },
15070
15071         /**
15072          * @ignore
15073          */
15074     getJsonAccessor: function(){
15075         var re = /[\[\.]/;
15076         return function(expr) {
15077             try {
15078                 return(re.test(expr))
15079                     ? new Function("obj", "return obj." + expr)
15080                     : function(obj){
15081                         return obj[expr];
15082                     };
15083             } catch(e){}
15084             return Roo.emptyFn;
15085         };
15086     }(),
15087
15088     /**
15089      * Create a data block containing Roo.data.Records from an XML document.
15090      * @param {Object} o An object which contains an Array of row objects in the property specified
15091      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15092      * which contains the total size of the dataset.
15093      * @return {Object} data A data block which is used by an Roo.data.Store object as
15094      * a cache of Roo.data.Records.
15095      */
15096     readRecords : function(o){
15097         /**
15098          * After any data loads, the raw JSON data is available for further custom processing.
15099          * @type Object
15100          */
15101         this.o = o;
15102         var s = this.meta, Record = this.recordType,
15103             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15104
15105 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15106         if (!this.ef) {
15107             if(s.totalProperty) {
15108                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15109                 }
15110                 if(s.successProperty) {
15111                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15112                 }
15113                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15114                 if (s.id) {
15115                         var g = this.getJsonAccessor(s.id);
15116                         this.getId = function(rec) {
15117                                 var r = g(rec);  
15118                                 return (r === undefined || r === "") ? null : r;
15119                         };
15120                 } else {
15121                         this.getId = function(){return null;};
15122                 }
15123             this.ef = [];
15124             for(var jj = 0; jj < fl; jj++){
15125                 f = fi[jj];
15126                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15127                 this.ef[jj] = this.getJsonAccessor(map);
15128             }
15129         }
15130
15131         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15132         if(s.totalProperty){
15133             var vt = parseInt(this.getTotal(o), 10);
15134             if(!isNaN(vt)){
15135                 totalRecords = vt;
15136             }
15137         }
15138         if(s.successProperty){
15139             var vs = this.getSuccess(o);
15140             if(vs === false || vs === 'false'){
15141                 success = false;
15142             }
15143         }
15144         var records = [];
15145         for(var i = 0; i < c; i++){
15146                 var n = root[i];
15147             var values = {};
15148             var id = this.getId(n);
15149             for(var j = 0; j < fl; j++){
15150                 f = fi[j];
15151             var v = this.ef[j](n);
15152             if (!f.convert) {
15153                 Roo.log('missing convert for ' + f.name);
15154                 Roo.log(f);
15155                 continue;
15156             }
15157             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15158             }
15159             var record = new Record(values, id);
15160             record.json = n;
15161             records[i] = record;
15162         }
15163         return {
15164             raw : o,
15165             success : success,
15166             records : records,
15167             totalRecords : totalRecords
15168         };
15169     },
15170     // used when loading children.. @see loadDataFromChildren
15171     toLoadData: function(rec)
15172     {
15173         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15174         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15175         return { data : data, total : data.length };
15176         
15177     }
15178 });/*
15179  * Based on:
15180  * Ext JS Library 1.1.1
15181  * Copyright(c) 2006-2007, Ext JS, LLC.
15182  *
15183  * Originally Released Under LGPL - original licence link has changed is not relivant.
15184  *
15185  * Fork - LGPL
15186  * <script type="text/javascript">
15187  */
15188
15189 /**
15190  * @class Roo.data.ArrayReader
15191  * @extends Roo.data.DataReader
15192  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15193  * Each element of that Array represents a row of data fields. The
15194  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15195  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15196  * <p>
15197  * Example code:.
15198  * <pre><code>
15199 var RecordDef = Roo.data.Record.create([
15200     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15201     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15202 ]);
15203 var myReader = new Roo.data.ArrayReader({
15204     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15205 }, RecordDef);
15206 </code></pre>
15207  * <p>
15208  * This would consume an Array like this:
15209  * <pre><code>
15210 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15211   </code></pre>
15212  
15213  * @constructor
15214  * Create a new JsonReader
15215  * @param {Object} meta Metadata configuration options.
15216  * @param {Object|Array} recordType Either an Array of field definition objects
15217  * 
15218  * @cfg {Array} fields Array of field definition objects
15219  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15220  * as specified to {@link Roo.data.Record#create},
15221  * or an {@link Roo.data.Record} object
15222  *
15223  * 
15224  * created using {@link Roo.data.Record#create}.
15225  */
15226 Roo.data.ArrayReader = function(meta, recordType)
15227 {    
15228     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15229 };
15230
15231 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15232     
15233       /**
15234      * Create a data block containing Roo.data.Records from an XML document.
15235      * @param {Object} o An Array of row objects which represents the dataset.
15236      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15237      * a cache of Roo.data.Records.
15238      */
15239     readRecords : function(o)
15240     {
15241         var sid = this.meta ? this.meta.id : null;
15242         var recordType = this.recordType, fields = recordType.prototype.fields;
15243         var records = [];
15244         var root = o;
15245         for(var i = 0; i < root.length; i++){
15246             var n = root[i];
15247             var values = {};
15248             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15249             for(var j = 0, jlen = fields.length; j < jlen; j++){
15250                 var f = fields.items[j];
15251                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15252                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15253                 v = f.convert(v);
15254                 values[f.name] = v;
15255             }
15256             var record = new recordType(values, id);
15257             record.json = n;
15258             records[records.length] = record;
15259         }
15260         return {
15261             records : records,
15262             totalRecords : records.length
15263         };
15264     },
15265     // used when loading children.. @see loadDataFromChildren
15266     toLoadData: function(rec)
15267     {
15268         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15269         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15270         
15271     }
15272     
15273     
15274 });/*
15275  * - LGPL
15276  * * 
15277  */
15278
15279 /**
15280  * @class Roo.bootstrap.ComboBox
15281  * @extends Roo.bootstrap.TriggerField
15282  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15283  * @cfg {Boolean} append (true|false) default false
15284  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15285  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15286  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15287  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15288  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15289  * @cfg {Boolean} animate default true
15290  * @cfg {Boolean} emptyResultText only for touch device
15291  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15292  * @cfg {String} emptyTitle default ''
15293  * @cfg {Number} width fixed with? experimental
15294  * @constructor
15295  * Create a new ComboBox.
15296  * @param {Object} config Configuration options
15297  */
15298 Roo.bootstrap.ComboBox = function(config){
15299     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15300     this.addEvents({
15301         /**
15302          * @event expand
15303          * Fires when the dropdown list is expanded
15304         * @param {Roo.bootstrap.ComboBox} combo This combo box
15305         */
15306         'expand' : true,
15307         /**
15308          * @event collapse
15309          * Fires when the dropdown list is collapsed
15310         * @param {Roo.bootstrap.ComboBox} combo This combo box
15311         */
15312         'collapse' : true,
15313         /**
15314          * @event beforeselect
15315          * Fires before a list item is selected. Return false to cancel the selection.
15316         * @param {Roo.bootstrap.ComboBox} combo This combo box
15317         * @param {Roo.data.Record} record The data record returned from the underlying store
15318         * @param {Number} index The index of the selected item in the dropdown list
15319         */
15320         'beforeselect' : true,
15321         /**
15322          * @event select
15323          * Fires when a list item is selected
15324         * @param {Roo.bootstrap.ComboBox} combo This combo box
15325         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15326         * @param {Number} index The index of the selected item in the dropdown list
15327         */
15328         'select' : true,
15329         /**
15330          * @event beforequery
15331          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15332          * The event object passed has these properties:
15333         * @param {Roo.bootstrap.ComboBox} combo This combo box
15334         * @param {String} query The query
15335         * @param {Boolean} forceAll true to force "all" query
15336         * @param {Boolean} cancel true to cancel the query
15337         * @param {Object} e The query event object
15338         */
15339         'beforequery': true,
15340          /**
15341          * @event add
15342          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15343         * @param {Roo.bootstrap.ComboBox} combo This combo box
15344         */
15345         'add' : true,
15346         /**
15347          * @event edit
15348          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15349         * @param {Roo.bootstrap.ComboBox} combo This combo box
15350         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15351         */
15352         'edit' : true,
15353         /**
15354          * @event remove
15355          * Fires when the remove value from the combobox array
15356         * @param {Roo.bootstrap.ComboBox} combo This combo box
15357         */
15358         'remove' : true,
15359         /**
15360          * @event afterremove
15361          * Fires when the remove value from the combobox array
15362         * @param {Roo.bootstrap.ComboBox} combo This combo box
15363         */
15364         'afterremove' : true,
15365         /**
15366          * @event specialfilter
15367          * Fires when specialfilter
15368             * @param {Roo.bootstrap.ComboBox} combo This combo box
15369             */
15370         'specialfilter' : true,
15371         /**
15372          * @event tick
15373          * Fires when tick the element
15374             * @param {Roo.bootstrap.ComboBox} combo This combo box
15375             */
15376         'tick' : true,
15377         /**
15378          * @event touchviewdisplay
15379          * Fires when touch view require special display (default is using displayField)
15380             * @param {Roo.bootstrap.ComboBox} combo This combo box
15381             * @param {Object} cfg set html .
15382             */
15383         'touchviewdisplay' : true
15384         
15385     });
15386     
15387     this.item = [];
15388     this.tickItems = [];
15389     
15390     this.selectedIndex = -1;
15391     if(this.mode == 'local'){
15392         if(config.queryDelay === undefined){
15393             this.queryDelay = 10;
15394         }
15395         if(config.minChars === undefined){
15396             this.minChars = 0;
15397         }
15398     }
15399 };
15400
15401 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15402      
15403     /**
15404      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15405      * rendering into an Roo.Editor, defaults to false)
15406      */
15407     /**
15408      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15409      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15410      */
15411     /**
15412      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15413      */
15414     /**
15415      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15416      * the dropdown list (defaults to undefined, with no header element)
15417      */
15418
15419      /**
15420      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15421      */
15422      
15423      /**
15424      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15425      */
15426     listWidth: undefined,
15427     /**
15428      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15429      * mode = 'remote' or 'text' if mode = 'local')
15430      */
15431     displayField: undefined,
15432     
15433     /**
15434      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15435      * mode = 'remote' or 'value' if mode = 'local'). 
15436      * Note: use of a valueField requires the user make a selection
15437      * in order for a value to be mapped.
15438      */
15439     valueField: undefined,
15440     /**
15441      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15442      */
15443     modalTitle : '',
15444     
15445     /**
15446      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15447      * field's data value (defaults to the underlying DOM element's name)
15448      */
15449     hiddenName: undefined,
15450     /**
15451      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15452      */
15453     listClass: '',
15454     /**
15455      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15456      */
15457     selectedClass: 'active',
15458     
15459     /**
15460      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15461      */
15462     shadow:'sides',
15463     /**
15464      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15465      * anchor positions (defaults to 'tl-bl')
15466      */
15467     listAlign: 'tl-bl?',
15468     /**
15469      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15470      */
15471     maxHeight: 300,
15472     /**
15473      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15474      * query specified by the allQuery config option (defaults to 'query')
15475      */
15476     triggerAction: 'query',
15477     /**
15478      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15479      * (defaults to 4, does not apply if editable = false)
15480      */
15481     minChars : 4,
15482     /**
15483      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15484      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15485      */
15486     typeAhead: false,
15487     /**
15488      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15489      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15490      */
15491     queryDelay: 500,
15492     /**
15493      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15494      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15495      */
15496     pageSize: 0,
15497     /**
15498      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15499      * when editable = true (defaults to false)
15500      */
15501     selectOnFocus:false,
15502     /**
15503      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15504      */
15505     queryParam: 'query',
15506     /**
15507      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15508      * when mode = 'remote' (defaults to 'Loading...')
15509      */
15510     loadingText: 'Loading...',
15511     /**
15512      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15513      */
15514     resizable: false,
15515     /**
15516      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15517      */
15518     handleHeight : 8,
15519     /**
15520      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15521      * traditional select (defaults to true)
15522      */
15523     editable: true,
15524     /**
15525      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15526      */
15527     allQuery: '',
15528     /**
15529      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15530      */
15531     mode: 'remote',
15532     /**
15533      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15534      * listWidth has a higher value)
15535      */
15536     minListWidth : 70,
15537     /**
15538      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15539      * allow the user to set arbitrary text into the field (defaults to false)
15540      */
15541     forceSelection:false,
15542     /**
15543      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15544      * if typeAhead = true (defaults to 250)
15545      */
15546     typeAheadDelay : 250,
15547     /**
15548      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15549      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15550      */
15551     valueNotFoundText : undefined,
15552     /**
15553      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15554      */
15555     blockFocus : false,
15556     
15557     /**
15558      * @cfg {Boolean} disableClear Disable showing of clear button.
15559      */
15560     disableClear : false,
15561     /**
15562      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15563      */
15564     alwaysQuery : false,
15565     
15566     /**
15567      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15568      */
15569     multiple : false,
15570     
15571     /**
15572      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15573      */
15574     invalidClass : "has-warning",
15575     
15576     /**
15577      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15578      */
15579     validClass : "has-success",
15580     
15581     /**
15582      * @cfg {Boolean} specialFilter (true|false) special filter default false
15583      */
15584     specialFilter : false,
15585     
15586     /**
15587      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15588      */
15589     mobileTouchView : true,
15590     
15591     /**
15592      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15593      */
15594     useNativeIOS : false,
15595     
15596     /**
15597      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15598      */
15599     mobile_restrict_height : false,
15600     
15601     ios_options : false,
15602     
15603     //private
15604     addicon : false,
15605     editicon: false,
15606     
15607     page: 0,
15608     hasQuery: false,
15609     append: false,
15610     loadNext: false,
15611     autoFocus : true,
15612     tickable : false,
15613     btnPosition : 'right',
15614     triggerList : true,
15615     showToggleBtn : true,
15616     animate : true,
15617     emptyResultText: 'Empty',
15618     triggerText : 'Select',
15619     emptyTitle : '',
15620     width : false,
15621     
15622     // element that contains real text value.. (when hidden is used..)
15623     
15624     getAutoCreate : function()
15625     {   
15626         var cfg = false;
15627         //render
15628         /*
15629          * Render classic select for iso
15630          */
15631         
15632         if(Roo.isIOS && this.useNativeIOS){
15633             cfg = this.getAutoCreateNativeIOS();
15634             return cfg;
15635         }
15636         
15637         /*
15638          * Touch Devices
15639          */
15640         
15641         if(Roo.isTouch && this.mobileTouchView){
15642             cfg = this.getAutoCreateTouchView();
15643             return cfg;;
15644         }
15645         
15646         /*
15647          *  Normal ComboBox
15648          */
15649         if(!this.tickable){
15650             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15651             return cfg;
15652         }
15653         
15654         /*
15655          *  ComboBox with tickable selections
15656          */
15657              
15658         var align = this.labelAlign || this.parentLabelAlign();
15659         
15660         cfg = {
15661             cls : 'form-group roo-combobox-tickable' //input-group
15662         };
15663         
15664         var btn_text_select = '';
15665         var btn_text_done = '';
15666         var btn_text_cancel = '';
15667         
15668         if (this.btn_text_show) {
15669             btn_text_select = 'Select';
15670             btn_text_done = 'Done';
15671             btn_text_cancel = 'Cancel'; 
15672         }
15673         
15674         var buttons = {
15675             tag : 'div',
15676             cls : 'tickable-buttons',
15677             cn : [
15678                 {
15679                     tag : 'button',
15680                     type : 'button',
15681                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15682                     //html : this.triggerText
15683                     html: btn_text_select
15684                 },
15685                 {
15686                     tag : 'button',
15687                     type : 'button',
15688                     name : 'ok',
15689                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15690                     //html : 'Done'
15691                     html: btn_text_done
15692                 },
15693                 {
15694                     tag : 'button',
15695                     type : 'button',
15696                     name : 'cancel',
15697                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15698                     //html : 'Cancel'
15699                     html: btn_text_cancel
15700                 }
15701             ]
15702         };
15703         
15704         if(this.editable){
15705             buttons.cn.unshift({
15706                 tag: 'input',
15707                 cls: 'roo-select2-search-field-input'
15708             });
15709         }
15710         
15711         var _this = this;
15712         
15713         Roo.each(buttons.cn, function(c){
15714             if (_this.size) {
15715                 c.cls += ' btn-' + _this.size;
15716             }
15717
15718             if (_this.disabled) {
15719                 c.disabled = true;
15720             }
15721         });
15722         
15723         var box = {
15724             tag: 'div',
15725             style : 'display: contents',
15726             cn: [
15727                 {
15728                     tag: 'input',
15729                     type : 'hidden',
15730                     cls: 'form-hidden-field'
15731                 },
15732                 {
15733                     tag: 'ul',
15734                     cls: 'roo-select2-choices',
15735                     cn:[
15736                         {
15737                             tag: 'li',
15738                             cls: 'roo-select2-search-field',
15739                             cn: [
15740                                 buttons
15741                             ]
15742                         }
15743                     ]
15744                 }
15745             ]
15746         };
15747         
15748         var combobox = {
15749             cls: 'roo-select2-container input-group roo-select2-container-multi',
15750             cn: [
15751                 
15752                 box
15753 //                {
15754 //                    tag: 'ul',
15755 //                    cls: 'typeahead typeahead-long dropdown-menu',
15756 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15757 //                }
15758             ]
15759         };
15760         
15761         if(this.hasFeedback && !this.allowBlank){
15762             
15763             var feedback = {
15764                 tag: 'span',
15765                 cls: 'glyphicon form-control-feedback'
15766             };
15767
15768             combobox.cn.push(feedback);
15769         }
15770         
15771         
15772         
15773         var indicator = {
15774             tag : 'i',
15775             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15776             tooltip : 'This field is required'
15777         };
15778         if (Roo.bootstrap.version == 4) {
15779             indicator = {
15780                 tag : 'i',
15781                 style : 'display:none'
15782             };
15783         }
15784         if (align ==='left' && this.fieldLabel.length) {
15785             
15786             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15787             
15788             cfg.cn = [
15789                 indicator,
15790                 {
15791                     tag: 'label',
15792                     'for' :  id,
15793                     cls : 'control-label col-form-label',
15794                     html : this.fieldLabel
15795
15796                 },
15797                 {
15798                     cls : "", 
15799                     cn: [
15800                         combobox
15801                     ]
15802                 }
15803
15804             ];
15805             
15806             var labelCfg = cfg.cn[1];
15807             var contentCfg = cfg.cn[2];
15808             
15809
15810             if(this.indicatorpos == 'right'){
15811                 
15812                 cfg.cn = [
15813                     {
15814                         tag: 'label',
15815                         'for' :  id,
15816                         cls : 'control-label col-form-label',
15817                         cn : [
15818                             {
15819                                 tag : 'span',
15820                                 html : this.fieldLabel
15821                             },
15822                             indicator
15823                         ]
15824                     },
15825                     {
15826                         cls : "",
15827                         cn: [
15828                             combobox
15829                         ]
15830                     }
15831
15832                 ];
15833                 
15834                 
15835                 
15836                 labelCfg = cfg.cn[0];
15837                 contentCfg = cfg.cn[1];
15838             
15839             }
15840             
15841             if(this.labelWidth > 12){
15842                 labelCfg.style = "width: " + this.labelWidth + 'px';
15843             }
15844             if(this.width * 1 > 0){
15845                 contentCfg.style = "width: " + this.width + 'px';
15846             }
15847             if(this.labelWidth < 13 && this.labelmd == 0){
15848                 this.labelmd = this.labelWidth;
15849             }
15850             
15851             if(this.labellg > 0){
15852                 labelCfg.cls += ' col-lg-' + this.labellg;
15853                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15854             }
15855             
15856             if(this.labelmd > 0){
15857                 labelCfg.cls += ' col-md-' + this.labelmd;
15858                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15859             }
15860             
15861             if(this.labelsm > 0){
15862                 labelCfg.cls += ' col-sm-' + this.labelsm;
15863                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15864             }
15865             
15866             if(this.labelxs > 0){
15867                 labelCfg.cls += ' col-xs-' + this.labelxs;
15868                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15869             }
15870                 
15871                 
15872         } else if ( this.fieldLabel.length) {
15873 //                Roo.log(" label");
15874                  cfg.cn = [
15875                    indicator,
15876                     {
15877                         tag: 'label',
15878                         //cls : 'input-group-addon',
15879                         html : this.fieldLabel
15880                     },
15881                     combobox
15882                 ];
15883                 
15884                 if(this.indicatorpos == 'right'){
15885                     cfg.cn = [
15886                         {
15887                             tag: 'label',
15888                             //cls : 'input-group-addon',
15889                             html : this.fieldLabel
15890                         },
15891                         indicator,
15892                         combobox
15893                     ];
15894                     
15895                 }
15896
15897         } else {
15898             
15899 //                Roo.log(" no label && no align");
15900                 cfg = combobox
15901                      
15902                 
15903         }
15904          
15905         var settings=this;
15906         ['xs','sm','md','lg'].map(function(size){
15907             if (settings[size]) {
15908                 cfg.cls += ' col-' + size + '-' + settings[size];
15909             }
15910         });
15911         
15912         return cfg;
15913         
15914     },
15915     
15916     _initEventsCalled : false,
15917     
15918     // private
15919     initEvents: function()
15920     {   
15921         if (this._initEventsCalled) { // as we call render... prevent looping...
15922             return;
15923         }
15924         this._initEventsCalled = true;
15925         
15926         if (!this.store) {
15927             throw "can not find store for combo";
15928         }
15929         
15930         this.indicator = this.indicatorEl();
15931         
15932         this.store = Roo.factory(this.store, Roo.data);
15933         this.store.parent = this;
15934         
15935         // if we are building from html. then this element is so complex, that we can not really
15936         // use the rendered HTML.
15937         // so we have to trash and replace the previous code.
15938         if (Roo.XComponent.build_from_html) {
15939             // remove this element....
15940             var e = this.el.dom, k=0;
15941             while (e ) { e = e.previousSibling;  ++k;}
15942
15943             this.el.remove();
15944             
15945             this.el=false;
15946             this.rendered = false;
15947             
15948             this.render(this.parent().getChildContainer(true), k);
15949         }
15950         
15951         if(Roo.isIOS && this.useNativeIOS){
15952             this.initIOSView();
15953             return;
15954         }
15955         
15956         /*
15957          * Touch Devices
15958          */
15959         
15960         if(Roo.isTouch && this.mobileTouchView){
15961             this.initTouchView();
15962             return;
15963         }
15964         
15965         if(this.tickable){
15966             this.initTickableEvents();
15967             return;
15968         }
15969         
15970         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15971         
15972         if(this.hiddenName){
15973             
15974             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15975             
15976             this.hiddenField.dom.value =
15977                 this.hiddenValue !== undefined ? this.hiddenValue :
15978                 this.value !== undefined ? this.value : '';
15979
15980             // prevent input submission
15981             this.el.dom.removeAttribute('name');
15982             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15983              
15984              
15985         }
15986         //if(Roo.isGecko){
15987         //    this.el.dom.setAttribute('autocomplete', 'off');
15988         //}
15989         
15990         var cls = 'x-combo-list';
15991         
15992         //this.list = new Roo.Layer({
15993         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15994         //});
15995         
15996         var _this = this;
15997         
15998         (function(){
15999             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16000             _this.list.setWidth(lw);
16001         }).defer(100);
16002         
16003         this.list.on('mouseover', this.onViewOver, this);
16004         this.list.on('mousemove', this.onViewMove, this);
16005         this.list.on('scroll', this.onViewScroll, this);
16006         
16007         /*
16008         this.list.swallowEvent('mousewheel');
16009         this.assetHeight = 0;
16010
16011         if(this.title){
16012             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16013             this.assetHeight += this.header.getHeight();
16014         }
16015
16016         this.innerList = this.list.createChild({cls:cls+'-inner'});
16017         this.innerList.on('mouseover', this.onViewOver, this);
16018         this.innerList.on('mousemove', this.onViewMove, this);
16019         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16020         
16021         if(this.allowBlank && !this.pageSize && !this.disableClear){
16022             this.footer = this.list.createChild({cls:cls+'-ft'});
16023             this.pageTb = new Roo.Toolbar(this.footer);
16024            
16025         }
16026         if(this.pageSize){
16027             this.footer = this.list.createChild({cls:cls+'-ft'});
16028             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16029                     {pageSize: this.pageSize});
16030             
16031         }
16032         
16033         if (this.pageTb && this.allowBlank && !this.disableClear) {
16034             var _this = this;
16035             this.pageTb.add(new Roo.Toolbar.Fill(), {
16036                 cls: 'x-btn-icon x-btn-clear',
16037                 text: '&#160;',
16038                 handler: function()
16039                 {
16040                     _this.collapse();
16041                     _this.clearValue();
16042                     _this.onSelect(false, -1);
16043                 }
16044             });
16045         }
16046         if (this.footer) {
16047             this.assetHeight += this.footer.getHeight();
16048         }
16049         */
16050             
16051         if(!this.tpl){
16052             this.tpl = Roo.bootstrap.version == 4 ?
16053                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16054                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16055         }
16056
16057         this.view = new Roo.View(this.list, this.tpl, {
16058             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16059         });
16060         //this.view.wrapEl.setDisplayed(false);
16061         this.view.on('click', this.onViewClick, this);
16062         
16063         
16064         this.store.on('beforeload', this.onBeforeLoad, this);
16065         this.store.on('load', this.onLoad, this);
16066         this.store.on('loadexception', this.onLoadException, this);
16067         /*
16068         if(this.resizable){
16069             this.resizer = new Roo.Resizable(this.list,  {
16070                pinned:true, handles:'se'
16071             });
16072             this.resizer.on('resize', function(r, w, h){
16073                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16074                 this.listWidth = w;
16075                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16076                 this.restrictHeight();
16077             }, this);
16078             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16079         }
16080         */
16081         if(!this.editable){
16082             this.editable = true;
16083             this.setEditable(false);
16084         }
16085         
16086         /*
16087         
16088         if (typeof(this.events.add.listeners) != 'undefined') {
16089             
16090             this.addicon = this.wrap.createChild(
16091                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16092        
16093             this.addicon.on('click', function(e) {
16094                 this.fireEvent('add', this);
16095             }, this);
16096         }
16097         if (typeof(this.events.edit.listeners) != 'undefined') {
16098             
16099             this.editicon = this.wrap.createChild(
16100                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16101             if (this.addicon) {
16102                 this.editicon.setStyle('margin-left', '40px');
16103             }
16104             this.editicon.on('click', function(e) {
16105                 
16106                 // we fire even  if inothing is selected..
16107                 this.fireEvent('edit', this, this.lastData );
16108                 
16109             }, this);
16110         }
16111         */
16112         
16113         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16114             "up" : function(e){
16115                 this.inKeyMode = true;
16116                 this.selectPrev();
16117             },
16118
16119             "down" : function(e){
16120                 if(!this.isExpanded()){
16121                     this.onTriggerClick();
16122                 }else{
16123                     this.inKeyMode = true;
16124                     this.selectNext();
16125                 }
16126             },
16127
16128             "enter" : function(e){
16129 //                this.onViewClick();
16130                 //return true;
16131                 this.collapse();
16132                 
16133                 if(this.fireEvent("specialkey", this, e)){
16134                     this.onViewClick(false);
16135                 }
16136                 
16137                 return true;
16138             },
16139
16140             "esc" : function(e){
16141                 this.collapse();
16142             },
16143
16144             "tab" : function(e){
16145                 this.collapse();
16146                 
16147                 if(this.fireEvent("specialkey", this, e)){
16148                     this.onViewClick(false);
16149                 }
16150                 
16151                 return true;
16152             },
16153
16154             scope : this,
16155
16156             doRelay : function(foo, bar, hname){
16157                 if(hname == 'down' || this.scope.isExpanded()){
16158                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16159                 }
16160                 return true;
16161             },
16162
16163             forceKeyDown: true
16164         });
16165         
16166         
16167         this.queryDelay = Math.max(this.queryDelay || 10,
16168                 this.mode == 'local' ? 10 : 250);
16169         
16170         
16171         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16172         
16173         if(this.typeAhead){
16174             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16175         }
16176         if(this.editable !== false){
16177             this.inputEl().on("keyup", this.onKeyUp, this);
16178         }
16179         if(this.forceSelection){
16180             this.inputEl().on('blur', this.doForce, this);
16181         }
16182         
16183         if(this.multiple){
16184             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16185             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16186         }
16187     },
16188     
16189     initTickableEvents: function()
16190     {   
16191         this.createList();
16192         
16193         if(this.hiddenName){
16194             
16195             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16196             
16197             this.hiddenField.dom.value =
16198                 this.hiddenValue !== undefined ? this.hiddenValue :
16199                 this.value !== undefined ? this.value : '';
16200
16201             // prevent input submission
16202             this.el.dom.removeAttribute('name');
16203             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16204              
16205              
16206         }
16207         
16208 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16209         
16210         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16211         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16212         if(this.triggerList){
16213             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16214         }
16215          
16216         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16217         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16218         
16219         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16220         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16221         
16222         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16223         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16224         
16225         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16226         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16227         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16228         
16229         this.okBtn.hide();
16230         this.cancelBtn.hide();
16231         
16232         var _this = this;
16233         
16234         (function(){
16235             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16236             _this.list.setWidth(lw);
16237         }).defer(100);
16238         
16239         this.list.on('mouseover', this.onViewOver, this);
16240         this.list.on('mousemove', this.onViewMove, this);
16241         
16242         this.list.on('scroll', this.onViewScroll, this);
16243         
16244         if(!this.tpl){
16245             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16246                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16247         }
16248
16249         this.view = new Roo.View(this.list, this.tpl, {
16250             singleSelect:true,
16251             tickable:true,
16252             parent:this,
16253             store: this.store,
16254             selectedClass: this.selectedClass
16255         });
16256         
16257         //this.view.wrapEl.setDisplayed(false);
16258         this.view.on('click', this.onViewClick, this);
16259         
16260         
16261         
16262         this.store.on('beforeload', this.onBeforeLoad, this);
16263         this.store.on('load', this.onLoad, this);
16264         this.store.on('loadexception', this.onLoadException, this);
16265         
16266         if(this.editable){
16267             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16268                 "up" : function(e){
16269                     this.inKeyMode = true;
16270                     this.selectPrev();
16271                 },
16272
16273                 "down" : function(e){
16274                     this.inKeyMode = true;
16275                     this.selectNext();
16276                 },
16277
16278                 "enter" : function(e){
16279                     if(this.fireEvent("specialkey", this, e)){
16280                         this.onViewClick(false);
16281                     }
16282                     
16283                     return true;
16284                 },
16285
16286                 "esc" : function(e){
16287                     this.onTickableFooterButtonClick(e, false, false);
16288                 },
16289
16290                 "tab" : function(e){
16291                     this.fireEvent("specialkey", this, e);
16292                     
16293                     this.onTickableFooterButtonClick(e, false, false);
16294                     
16295                     return true;
16296                 },
16297
16298                 scope : this,
16299
16300                 doRelay : function(e, fn, key){
16301                     if(this.scope.isExpanded()){
16302                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16303                     }
16304                     return true;
16305                 },
16306
16307                 forceKeyDown: true
16308             });
16309         }
16310         
16311         this.queryDelay = Math.max(this.queryDelay || 10,
16312                 this.mode == 'local' ? 10 : 250);
16313         
16314         
16315         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16316         
16317         if(this.typeAhead){
16318             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16319         }
16320         
16321         if(this.editable !== false){
16322             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16323         }
16324         
16325         this.indicator = this.indicatorEl();
16326         
16327         if(this.indicator){
16328             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16329             this.indicator.hide();
16330         }
16331         
16332     },
16333
16334     onDestroy : function(){
16335         if(this.view){
16336             this.view.setStore(null);
16337             this.view.el.removeAllListeners();
16338             this.view.el.remove();
16339             this.view.purgeListeners();
16340         }
16341         if(this.list){
16342             this.list.dom.innerHTML  = '';
16343         }
16344         
16345         if(this.store){
16346             this.store.un('beforeload', this.onBeforeLoad, this);
16347             this.store.un('load', this.onLoad, this);
16348             this.store.un('loadexception', this.onLoadException, this);
16349         }
16350         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16351     },
16352
16353     // private
16354     fireKey : function(e){
16355         if(e.isNavKeyPress() && !this.list.isVisible()){
16356             this.fireEvent("specialkey", this, e);
16357         }
16358     },
16359
16360     // private
16361     onResize: function(w, h)
16362     {
16363         
16364         
16365 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16366 //        
16367 //        if(typeof w != 'number'){
16368 //            // we do not handle it!?!?
16369 //            return;
16370 //        }
16371 //        var tw = this.trigger.getWidth();
16372 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16373 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16374 //        var x = w - tw;
16375 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16376 //            
16377 //        //this.trigger.setStyle('left', x+'px');
16378 //        
16379 //        if(this.list && this.listWidth === undefined){
16380 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16381 //            this.list.setWidth(lw);
16382 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16383 //        }
16384         
16385     
16386         
16387     },
16388
16389     /**
16390      * Allow or prevent the user from directly editing the field text.  If false is passed,
16391      * the user will only be able to select from the items defined in the dropdown list.  This method
16392      * is the runtime equivalent of setting the 'editable' config option at config time.
16393      * @param {Boolean} value True to allow the user to directly edit the field text
16394      */
16395     setEditable : function(value){
16396         if(value == this.editable){
16397             return;
16398         }
16399         this.editable = value;
16400         if(!value){
16401             this.inputEl().dom.setAttribute('readOnly', true);
16402             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16403             this.inputEl().addClass('x-combo-noedit');
16404         }else{
16405             this.inputEl().dom.removeAttribute('readOnly');
16406             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16407             this.inputEl().removeClass('x-combo-noedit');
16408         }
16409     },
16410
16411     // private
16412     
16413     onBeforeLoad : function(combo,opts){
16414         if(!this.hasFocus){
16415             return;
16416         }
16417          if (!opts.add) {
16418             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16419          }
16420         this.restrictHeight();
16421         this.selectedIndex = -1;
16422     },
16423
16424     // private
16425     onLoad : function(){
16426         
16427         this.hasQuery = false;
16428         
16429         if(!this.hasFocus){
16430             return;
16431         }
16432         
16433         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16434             this.loading.hide();
16435         }
16436         
16437         if(this.store.getCount() > 0){
16438             
16439             this.expand();
16440             this.restrictHeight();
16441             if(this.lastQuery == this.allQuery){
16442                 if(this.editable && !this.tickable){
16443                     this.inputEl().dom.select();
16444                 }
16445                 
16446                 if(
16447                     !this.selectByValue(this.value, true) &&
16448                     this.autoFocus && 
16449                     (
16450                         !this.store.lastOptions ||
16451                         typeof(this.store.lastOptions.add) == 'undefined' || 
16452                         this.store.lastOptions.add != true
16453                     )
16454                 ){
16455                     this.select(0, true);
16456                 }
16457             }else{
16458                 if(this.autoFocus){
16459                     this.selectNext();
16460                 }
16461                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16462                     this.taTask.delay(this.typeAheadDelay);
16463                 }
16464             }
16465         }else{
16466             this.onEmptyResults();
16467         }
16468         
16469         //this.el.focus();
16470     },
16471     // private
16472     onLoadException : function()
16473     {
16474         this.hasQuery = false;
16475         
16476         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16477             this.loading.hide();
16478         }
16479         
16480         if(this.tickable && this.editable){
16481             return;
16482         }
16483         
16484         this.collapse();
16485         // only causes errors at present
16486         //Roo.log(this.store.reader.jsonData);
16487         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16488             // fixme
16489             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16490         //}
16491         
16492         
16493     },
16494     // private
16495     onTypeAhead : function(){
16496         if(this.store.getCount() > 0){
16497             var r = this.store.getAt(0);
16498             var newValue = r.data[this.displayField];
16499             var len = newValue.length;
16500             var selStart = this.getRawValue().length;
16501             
16502             if(selStart != len){
16503                 this.setRawValue(newValue);
16504                 this.selectText(selStart, newValue.length);
16505             }
16506         }
16507     },
16508
16509     // private
16510     onSelect : function(record, index){
16511         
16512         if(this.fireEvent('beforeselect', this, record, index) !== false){
16513         
16514             this.setFromData(index > -1 ? record.data : false);
16515             
16516             this.collapse();
16517             this.fireEvent('select', this, record, index);
16518         }
16519     },
16520
16521     /**
16522      * Returns the currently selected field value or empty string if no value is set.
16523      * @return {String} value The selected value
16524      */
16525     getValue : function()
16526     {
16527         if(Roo.isIOS && this.useNativeIOS){
16528             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16529         }
16530         
16531         if(this.multiple){
16532             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16533         }
16534         
16535         if(this.valueField){
16536             return typeof this.value != 'undefined' ? this.value : '';
16537         }else{
16538             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16539         }
16540     },
16541     
16542     getRawValue : function()
16543     {
16544         if(Roo.isIOS && this.useNativeIOS){
16545             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16546         }
16547         
16548         var v = this.inputEl().getValue();
16549         
16550         return v;
16551     },
16552
16553     /**
16554      * Clears any text/value currently set in the field
16555      */
16556     clearValue : function(){
16557         
16558         if(this.hiddenField){
16559             this.hiddenField.dom.value = '';
16560         }
16561         this.value = '';
16562         this.setRawValue('');
16563         this.lastSelectionText = '';
16564         this.lastData = false;
16565         
16566         var close = this.closeTriggerEl();
16567         
16568         if(close){
16569             close.hide();
16570         }
16571         
16572         this.validate();
16573         
16574     },
16575
16576     /**
16577      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16578      * will be displayed in the field.  If the value does not match the data value of an existing item,
16579      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16580      * Otherwise the field will be blank (although the value will still be set).
16581      * @param {String} value The value to match
16582      */
16583     setValue : function(v)
16584     {
16585         if(Roo.isIOS && this.useNativeIOS){
16586             this.setIOSValue(v);
16587             return;
16588         }
16589         
16590         if(this.multiple){
16591             this.syncValue();
16592             return;
16593         }
16594         
16595         var text = v;
16596         if(this.valueField){
16597             var r = this.findRecord(this.valueField, v);
16598             if(r){
16599                 text = r.data[this.displayField];
16600             }else if(this.valueNotFoundText !== undefined){
16601                 text = this.valueNotFoundText;
16602             }
16603         }
16604         this.lastSelectionText = text;
16605         if(this.hiddenField){
16606             this.hiddenField.dom.value = v;
16607         }
16608         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16609         this.value = v;
16610         
16611         var close = this.closeTriggerEl();
16612         
16613         if(close){
16614             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16615         }
16616         
16617         this.validate();
16618     },
16619     /**
16620      * @property {Object} the last set data for the element
16621      */
16622     
16623     lastData : false,
16624     /**
16625      * Sets the value of the field based on a object which is related to the record format for the store.
16626      * @param {Object} value the value to set as. or false on reset?
16627      */
16628     setFromData : function(o){
16629         
16630         if(this.multiple){
16631             this.addItem(o);
16632             return;
16633         }
16634             
16635         var dv = ''; // display value
16636         var vv = ''; // value value..
16637         this.lastData = o;
16638         if (this.displayField) {
16639             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16640         } else {
16641             // this is an error condition!!!
16642             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16643         }
16644         
16645         if(this.valueField){
16646             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16647         }
16648         
16649         var close = this.closeTriggerEl();
16650         
16651         if(close){
16652             if(dv.length || vv * 1 > 0){
16653                 close.show() ;
16654                 this.blockFocus=true;
16655             } else {
16656                 close.hide();
16657             }             
16658         }
16659         
16660         if(this.hiddenField){
16661             this.hiddenField.dom.value = vv;
16662             
16663             this.lastSelectionText = dv;
16664             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16665             this.value = vv;
16666             return;
16667         }
16668         // no hidden field.. - we store the value in 'value', but still display
16669         // display field!!!!
16670         this.lastSelectionText = dv;
16671         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16672         this.value = vv;
16673         
16674         
16675         
16676     },
16677     // private
16678     reset : function(){
16679         // overridden so that last data is reset..
16680         
16681         if(this.multiple){
16682             this.clearItem();
16683             return;
16684         }
16685         
16686         this.setValue(this.originalValue);
16687         //this.clearInvalid();
16688         this.lastData = false;
16689         if (this.view) {
16690             this.view.clearSelections();
16691         }
16692         
16693         this.validate();
16694     },
16695     // private
16696     findRecord : function(prop, value){
16697         var record;
16698         if(this.store.getCount() > 0){
16699             this.store.each(function(r){
16700                 if(r.data[prop] == value){
16701                     record = r;
16702                     return false;
16703                 }
16704                 return true;
16705             });
16706         }
16707         return record;
16708     },
16709     
16710     getName: function()
16711     {
16712         // returns hidden if it's set..
16713         if (!this.rendered) {return ''};
16714         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16715         
16716     },
16717     // private
16718     onViewMove : function(e, t){
16719         this.inKeyMode = false;
16720     },
16721
16722     // private
16723     onViewOver : function(e, t){
16724         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16725             return;
16726         }
16727         var item = this.view.findItemFromChild(t);
16728         
16729         if(item){
16730             var index = this.view.indexOf(item);
16731             this.select(index, false);
16732         }
16733     },
16734
16735     // private
16736     onViewClick : function(view, doFocus, el, e)
16737     {
16738         var index = this.view.getSelectedIndexes()[0];
16739         
16740         var r = this.store.getAt(index);
16741         
16742         if(this.tickable){
16743             
16744             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16745                 return;
16746             }
16747             
16748             var rm = false;
16749             var _this = this;
16750             
16751             Roo.each(this.tickItems, function(v,k){
16752                 
16753                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16754                     Roo.log(v);
16755                     _this.tickItems.splice(k, 1);
16756                     
16757                     if(typeof(e) == 'undefined' && view == false){
16758                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16759                     }
16760                     
16761                     rm = true;
16762                     return;
16763                 }
16764             });
16765             
16766             if(rm){
16767                 return;
16768             }
16769             
16770             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16771                 this.tickItems.push(r.data);
16772             }
16773             
16774             if(typeof(e) == 'undefined' && view == false){
16775                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16776             }
16777                     
16778             return;
16779         }
16780         
16781         if(r){
16782             this.onSelect(r, index);
16783         }
16784         if(doFocus !== false && !this.blockFocus){
16785             this.inputEl().focus();
16786         }
16787     },
16788
16789     // private
16790     restrictHeight : function(){
16791         //this.innerList.dom.style.height = '';
16792         //var inner = this.innerList.dom;
16793         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16794         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16795         //this.list.beginUpdate();
16796         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16797         this.list.alignTo(this.inputEl(), this.listAlign);
16798         this.list.alignTo(this.inputEl(), this.listAlign);
16799         //this.list.endUpdate();
16800     },
16801
16802     // private
16803     onEmptyResults : function(){
16804         
16805         if(this.tickable && this.editable){
16806             this.hasFocus = false;
16807             this.restrictHeight();
16808             return;
16809         }
16810         
16811         this.collapse();
16812     },
16813
16814     /**
16815      * Returns true if the dropdown list is expanded, else false.
16816      */
16817     isExpanded : function(){
16818         return this.list.isVisible();
16819     },
16820
16821     /**
16822      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16823      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824      * @param {String} value The data value of the item to select
16825      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826      * selected item if it is not currently in view (defaults to true)
16827      * @return {Boolean} True if the value matched an item in the list, else false
16828      */
16829     selectByValue : function(v, scrollIntoView){
16830         if(v !== undefined && v !== null){
16831             var r = this.findRecord(this.valueField || this.displayField, v);
16832             if(r){
16833                 this.select(this.store.indexOf(r), scrollIntoView);
16834                 return true;
16835             }
16836         }
16837         return false;
16838     },
16839
16840     /**
16841      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16842      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16843      * @param {Number} index The zero-based index of the list item to select
16844      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16845      * selected item if it is not currently in view (defaults to true)
16846      */
16847     select : function(index, scrollIntoView){
16848         this.selectedIndex = index;
16849         this.view.select(index);
16850         if(scrollIntoView !== false){
16851             var el = this.view.getNode(index);
16852             /*
16853              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16854              */
16855             if(el){
16856                 this.list.scrollChildIntoView(el, false);
16857             }
16858         }
16859     },
16860
16861     // private
16862     selectNext : function(){
16863         var ct = this.store.getCount();
16864         if(ct > 0){
16865             if(this.selectedIndex == -1){
16866                 this.select(0);
16867             }else if(this.selectedIndex < ct-1){
16868                 this.select(this.selectedIndex+1);
16869             }
16870         }
16871     },
16872
16873     // private
16874     selectPrev : function(){
16875         var ct = this.store.getCount();
16876         if(ct > 0){
16877             if(this.selectedIndex == -1){
16878                 this.select(0);
16879             }else if(this.selectedIndex != 0){
16880                 this.select(this.selectedIndex-1);
16881             }
16882         }
16883     },
16884
16885     // private
16886     onKeyUp : function(e){
16887         if(this.editable !== false && !e.isSpecialKey()){
16888             this.lastKey = e.getKey();
16889             this.dqTask.delay(this.queryDelay);
16890         }
16891     },
16892
16893     // private
16894     validateBlur : function(){
16895         return !this.list || !this.list.isVisible();   
16896     },
16897
16898     // private
16899     initQuery : function(){
16900         
16901         var v = this.getRawValue();
16902         
16903         if(this.tickable && this.editable){
16904             v = this.tickableInputEl().getValue();
16905         }
16906         
16907         this.doQuery(v);
16908     },
16909
16910     // private
16911     doForce : function(){
16912         if(this.inputEl().dom.value.length > 0){
16913             this.inputEl().dom.value =
16914                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16915              
16916         }
16917     },
16918
16919     /**
16920      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16921      * query allowing the query action to be canceled if needed.
16922      * @param {String} query The SQL query to execute
16923      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16924      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16925      * saved in the current store (defaults to false)
16926      */
16927     doQuery : function(q, forceAll){
16928         
16929         if(q === undefined || q === null){
16930             q = '';
16931         }
16932         var qe = {
16933             query: q,
16934             forceAll: forceAll,
16935             combo: this,
16936             cancel:false
16937         };
16938         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16939             return false;
16940         }
16941         q = qe.query;
16942         
16943         forceAll = qe.forceAll;
16944         if(forceAll === true || (q.length >= this.minChars)){
16945             
16946             this.hasQuery = true;
16947             
16948             if(this.lastQuery != q || this.alwaysQuery){
16949                 this.lastQuery = q;
16950                 if(this.mode == 'local'){
16951                     this.selectedIndex = -1;
16952                     if(forceAll){
16953                         this.store.clearFilter();
16954                     }else{
16955                         
16956                         if(this.specialFilter){
16957                             this.fireEvent('specialfilter', this);
16958                             this.onLoad();
16959                             return;
16960                         }
16961                         
16962                         this.store.filter(this.displayField, q);
16963                     }
16964                     
16965                     this.store.fireEvent("datachanged", this.store);
16966                     
16967                     this.onLoad();
16968                     
16969                     
16970                 }else{
16971                     
16972                     this.store.baseParams[this.queryParam] = q;
16973                     
16974                     var options = {params : this.getParams(q)};
16975                     
16976                     if(this.loadNext){
16977                         options.add = true;
16978                         options.params.start = this.page * this.pageSize;
16979                     }
16980                     
16981                     this.store.load(options);
16982                     
16983                     /*
16984                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16985                      *  we should expand the list on onLoad
16986                      *  so command out it
16987                      */
16988 //                    this.expand();
16989                 }
16990             }else{
16991                 this.selectedIndex = -1;
16992                 this.onLoad();   
16993             }
16994         }
16995         
16996         this.loadNext = false;
16997     },
16998     
16999     // private
17000     getParams : function(q){
17001         var p = {};
17002         //p[this.queryParam] = q;
17003         
17004         if(this.pageSize){
17005             p.start = 0;
17006             p.limit = this.pageSize;
17007         }
17008         return p;
17009     },
17010
17011     /**
17012      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17013      */
17014     collapse : function(){
17015         if(!this.isExpanded()){
17016             return;
17017         }
17018         
17019         this.list.hide();
17020         
17021         this.hasFocus = false;
17022         
17023         if(this.tickable){
17024             this.okBtn.hide();
17025             this.cancelBtn.hide();
17026             this.trigger.show();
17027             
17028             if(this.editable){
17029                 this.tickableInputEl().dom.value = '';
17030                 this.tickableInputEl().blur();
17031             }
17032             
17033         }
17034         
17035         Roo.get(document).un('mousedown', this.collapseIf, this);
17036         Roo.get(document).un('mousewheel', this.collapseIf, this);
17037         if (!this.editable) {
17038             Roo.get(document).un('keydown', this.listKeyPress, this);
17039         }
17040         this.fireEvent('collapse', this);
17041         
17042         this.validate();
17043     },
17044
17045     // private
17046     collapseIf : function(e){
17047         var in_combo  = e.within(this.el);
17048         var in_list =  e.within(this.list);
17049         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17050         
17051         if (in_combo || in_list || is_list) {
17052             //e.stopPropagation();
17053             return;
17054         }
17055         
17056         if(this.tickable){
17057             this.onTickableFooterButtonClick(e, false, false);
17058         }
17059
17060         this.collapse();
17061         
17062     },
17063
17064     /**
17065      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17066      */
17067     expand : function(){
17068        
17069         if(this.isExpanded() || !this.hasFocus){
17070             return;
17071         }
17072         
17073         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17074         this.list.setWidth(lw);
17075         
17076         Roo.log('expand');
17077         
17078         this.list.show();
17079         
17080         this.restrictHeight();
17081         
17082         if(this.tickable){
17083             
17084             this.tickItems = Roo.apply([], this.item);
17085             
17086             this.okBtn.show();
17087             this.cancelBtn.show();
17088             this.trigger.hide();
17089             
17090             if(this.editable){
17091                 this.tickableInputEl().focus();
17092             }
17093             
17094         }
17095         
17096         Roo.get(document).on('mousedown', this.collapseIf, this);
17097         Roo.get(document).on('mousewheel', this.collapseIf, this);
17098         if (!this.editable) {
17099             Roo.get(document).on('keydown', this.listKeyPress, this);
17100         }
17101         
17102         this.fireEvent('expand', this);
17103     },
17104
17105     // private
17106     // Implements the default empty TriggerField.onTriggerClick function
17107     onTriggerClick : function(e)
17108     {
17109         Roo.log('trigger click');
17110         
17111         if(this.disabled || !this.triggerList){
17112             return;
17113         }
17114         
17115         this.page = 0;
17116         this.loadNext = false;
17117         
17118         if(this.isExpanded()){
17119             this.collapse();
17120             if (!this.blockFocus) {
17121                 this.inputEl().focus();
17122             }
17123             
17124         }else {
17125             this.hasFocus = true;
17126             if(this.triggerAction == 'all') {
17127                 this.doQuery(this.allQuery, true);
17128             } else {
17129                 this.doQuery(this.getRawValue());
17130             }
17131             if (!this.blockFocus) {
17132                 this.inputEl().focus();
17133             }
17134         }
17135     },
17136     
17137     onTickableTriggerClick : function(e)
17138     {
17139         if(this.disabled){
17140             return;
17141         }
17142         
17143         this.page = 0;
17144         this.loadNext = false;
17145         this.hasFocus = true;
17146         
17147         if(this.triggerAction == 'all') {
17148             this.doQuery(this.allQuery, true);
17149         } else {
17150             this.doQuery(this.getRawValue());
17151         }
17152     },
17153     
17154     onSearchFieldClick : function(e)
17155     {
17156         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17157             this.onTickableFooterButtonClick(e, false, false);
17158             return;
17159         }
17160         
17161         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17162             return;
17163         }
17164         
17165         this.page = 0;
17166         this.loadNext = false;
17167         this.hasFocus = true;
17168         
17169         if(this.triggerAction == 'all') {
17170             this.doQuery(this.allQuery, true);
17171         } else {
17172             this.doQuery(this.getRawValue());
17173         }
17174     },
17175     
17176     listKeyPress : function(e)
17177     {
17178         //Roo.log('listkeypress');
17179         // scroll to first matching element based on key pres..
17180         if (e.isSpecialKey()) {
17181             return false;
17182         }
17183         var k = String.fromCharCode(e.getKey()).toUpperCase();
17184         //Roo.log(k);
17185         var match  = false;
17186         var csel = this.view.getSelectedNodes();
17187         var cselitem = false;
17188         if (csel.length) {
17189             var ix = this.view.indexOf(csel[0]);
17190             cselitem  = this.store.getAt(ix);
17191             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17192                 cselitem = false;
17193             }
17194             
17195         }
17196         
17197         this.store.each(function(v) { 
17198             if (cselitem) {
17199                 // start at existing selection.
17200                 if (cselitem.id == v.id) {
17201                     cselitem = false;
17202                 }
17203                 return true;
17204             }
17205                 
17206             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17207                 match = this.store.indexOf(v);
17208                 return false;
17209             }
17210             return true;
17211         }, this);
17212         
17213         if (match === false) {
17214             return true; // no more action?
17215         }
17216         // scroll to?
17217         this.view.select(match);
17218         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17219         sn.scrollIntoView(sn.dom.parentNode, false);
17220     },
17221     
17222     onViewScroll : function(e, t){
17223         
17224         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){
17225             return;
17226         }
17227         
17228         this.hasQuery = true;
17229         
17230         this.loading = this.list.select('.loading', true).first();
17231         
17232         if(this.loading === null){
17233             this.list.createChild({
17234                 tag: 'div',
17235                 cls: 'loading roo-select2-more-results roo-select2-active',
17236                 html: 'Loading more results...'
17237             });
17238             
17239             this.loading = this.list.select('.loading', true).first();
17240             
17241             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17242             
17243             this.loading.hide();
17244         }
17245         
17246         this.loading.show();
17247         
17248         var _combo = this;
17249         
17250         this.page++;
17251         this.loadNext = true;
17252         
17253         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17254         
17255         return;
17256     },
17257     
17258     addItem : function(o)
17259     {   
17260         var dv = ''; // display value
17261         
17262         if (this.displayField) {
17263             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17264         } else {
17265             // this is an error condition!!!
17266             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17267         }
17268         
17269         if(!dv.length){
17270             return;
17271         }
17272         
17273         var choice = this.choices.createChild({
17274             tag: 'li',
17275             cls: 'roo-select2-search-choice',
17276             cn: [
17277                 {
17278                     tag: 'div',
17279                     html: dv
17280                 },
17281                 {
17282                     tag: 'a',
17283                     href: '#',
17284                     cls: 'roo-select2-search-choice-close fa fa-times',
17285                     tabindex: '-1'
17286                 }
17287             ]
17288             
17289         }, this.searchField);
17290         
17291         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17292         
17293         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17294         
17295         this.item.push(o);
17296         
17297         this.lastData = o;
17298         
17299         this.syncValue();
17300         
17301         this.inputEl().dom.value = '';
17302         
17303         this.validate();
17304     },
17305     
17306     onRemoveItem : function(e, _self, o)
17307     {
17308         e.preventDefault();
17309         
17310         this.lastItem = Roo.apply([], this.item);
17311         
17312         var index = this.item.indexOf(o.data) * 1;
17313         
17314         if( index < 0){
17315             Roo.log('not this item?!');
17316             return;
17317         }
17318         
17319         this.item.splice(index, 1);
17320         o.item.remove();
17321         
17322         this.syncValue();
17323         
17324         this.fireEvent('remove', this, e);
17325         
17326         this.validate();
17327         
17328     },
17329     
17330     syncValue : function()
17331     {
17332         if(!this.item.length){
17333             this.clearValue();
17334             return;
17335         }
17336             
17337         var value = [];
17338         var _this = this;
17339         Roo.each(this.item, function(i){
17340             if(_this.valueField){
17341                 value.push(i[_this.valueField]);
17342                 return;
17343             }
17344
17345             value.push(i);
17346         });
17347
17348         this.value = value.join(',');
17349
17350         if(this.hiddenField){
17351             this.hiddenField.dom.value = this.value;
17352         }
17353         
17354         this.store.fireEvent("datachanged", this.store);
17355         
17356         this.validate();
17357     },
17358     
17359     clearItem : function()
17360     {
17361         if(!this.multiple){
17362             return;
17363         }
17364         
17365         this.item = [];
17366         
17367         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17368            c.remove();
17369         });
17370         
17371         this.syncValue();
17372         
17373         this.validate();
17374         
17375         if(this.tickable && !Roo.isTouch){
17376             this.view.refresh();
17377         }
17378     },
17379     
17380     inputEl: function ()
17381     {
17382         if(Roo.isIOS && this.useNativeIOS){
17383             return this.el.select('select.roo-ios-select', true).first();
17384         }
17385         
17386         if(Roo.isTouch && this.mobileTouchView){
17387             return this.el.select('input.form-control',true).first();
17388         }
17389         
17390         if(this.tickable){
17391             return this.searchField;
17392         }
17393         
17394         return this.el.select('input.form-control',true).first();
17395     },
17396     
17397     onTickableFooterButtonClick : function(e, btn, el)
17398     {
17399         e.preventDefault();
17400         
17401         this.lastItem = Roo.apply([], this.item);
17402         
17403         if(btn && btn.name == 'cancel'){
17404             this.tickItems = Roo.apply([], this.item);
17405             this.collapse();
17406             return;
17407         }
17408         
17409         this.clearItem();
17410         
17411         var _this = this;
17412         
17413         Roo.each(this.tickItems, function(o){
17414             _this.addItem(o);
17415         });
17416         
17417         this.collapse();
17418         
17419     },
17420     
17421     validate : function()
17422     {
17423         if(this.getVisibilityEl().hasClass('hidden')){
17424             return true;
17425         }
17426         
17427         var v = this.getRawValue();
17428         
17429         if(this.multiple){
17430             v = this.getValue();
17431         }
17432         
17433         if(this.disabled || this.allowBlank || v.length){
17434             this.markValid();
17435             return true;
17436         }
17437         
17438         this.markInvalid();
17439         return false;
17440     },
17441     
17442     tickableInputEl : function()
17443     {
17444         if(!this.tickable || !this.editable){
17445             return this.inputEl();
17446         }
17447         
17448         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17449     },
17450     
17451     
17452     getAutoCreateTouchView : function()
17453     {
17454         var id = Roo.id();
17455         
17456         var cfg = {
17457             cls: 'form-group' //input-group
17458         };
17459         
17460         var input =  {
17461             tag: 'input',
17462             id : id,
17463             type : this.inputType,
17464             cls : 'form-control x-combo-noedit',
17465             autocomplete: 'new-password',
17466             placeholder : this.placeholder || '',
17467             readonly : true
17468         };
17469         
17470         if (this.name) {
17471             input.name = this.name;
17472         }
17473         
17474         if (this.size) {
17475             input.cls += ' input-' + this.size;
17476         }
17477         
17478         if (this.disabled) {
17479             input.disabled = true;
17480         }
17481         
17482         var inputblock = {
17483             cls : 'roo-combobox-wrap',
17484             cn : [
17485                 input
17486             ]
17487         };
17488         
17489         if(this.before){
17490             inputblock.cls += ' input-group';
17491             
17492             inputblock.cn.unshift({
17493                 tag :'span',
17494                 cls : 'input-group-addon input-group-prepend input-group-text',
17495                 html : this.before
17496             });
17497         }
17498         
17499         if(this.removable && !this.multiple){
17500             inputblock.cls += ' roo-removable';
17501             
17502             inputblock.cn.push({
17503                 tag: 'button',
17504                 html : 'x',
17505                 cls : 'roo-combo-removable-btn close'
17506             });
17507         }
17508
17509         if(this.hasFeedback && !this.allowBlank){
17510             
17511             inputblock.cls += ' has-feedback';
17512             
17513             inputblock.cn.push({
17514                 tag: 'span',
17515                 cls: 'glyphicon form-control-feedback'
17516             });
17517             
17518         }
17519         
17520         if (this.after) {
17521             
17522             inputblock.cls += (this.before) ? '' : ' input-group';
17523             
17524             inputblock.cn.push({
17525                 tag :'span',
17526                 cls : 'input-group-addon input-group-append input-group-text',
17527                 html : this.after
17528             });
17529         }
17530
17531         
17532         var ibwrap = inputblock;
17533         
17534         if(this.multiple){
17535             ibwrap = {
17536                 tag: 'ul',
17537                 cls: 'roo-select2-choices',
17538                 cn:[
17539                     {
17540                         tag: 'li',
17541                         cls: 'roo-select2-search-field',
17542                         cn: [
17543
17544                             inputblock
17545                         ]
17546                     }
17547                 ]
17548             };
17549         
17550             
17551         }
17552         
17553         var combobox = {
17554             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17555             cn: [
17556                 {
17557                     tag: 'input',
17558                     type : 'hidden',
17559                     cls: 'form-hidden-field'
17560                 },
17561                 ibwrap
17562             ]
17563         };
17564         
17565         if(!this.multiple && this.showToggleBtn){
17566             
17567             var caret = {
17568                 cls: 'caret'
17569             };
17570             
17571             if (this.caret != false) {
17572                 caret = {
17573                      tag: 'i',
17574                      cls: 'fa fa-' + this.caret
17575                 };
17576                 
17577             }
17578             
17579             combobox.cn.push({
17580                 tag :'span',
17581                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17582                 cn : [
17583                     Roo.bootstrap.version == 3 ? caret : '',
17584                     {
17585                         tag: 'span',
17586                         cls: 'combobox-clear',
17587                         cn  : [
17588                             {
17589                                 tag : 'i',
17590                                 cls: 'icon-remove'
17591                             }
17592                         ]
17593                     }
17594                 ]
17595
17596             })
17597         }
17598         
17599         if(this.multiple){
17600             combobox.cls += ' roo-select2-container-multi';
17601         }
17602         
17603         var required =  this.allowBlank ?  {
17604                     tag : 'i',
17605                     style: 'display: none'
17606                 } : {
17607                    tag : 'i',
17608                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17609                    tooltip : 'This field is required'
17610                 };
17611         
17612         var align = this.labelAlign || this.parentLabelAlign();
17613         
17614         if (align ==='left' && this.fieldLabel.length) {
17615
17616             cfg.cn = [
17617                 required,
17618                 {
17619                     tag: 'label',
17620                     cls : 'control-label col-form-label',
17621                     html : this.fieldLabel
17622
17623                 },
17624                 {
17625                     cls : 'roo-combobox-wrap ', 
17626                     cn: [
17627                         combobox
17628                     ]
17629                 }
17630             ];
17631             
17632             var labelCfg = cfg.cn[1];
17633             var contentCfg = cfg.cn[2];
17634             
17635
17636             if(this.indicatorpos == 'right'){
17637                 cfg.cn = [
17638                     {
17639                         tag: 'label',
17640                         'for' :  id,
17641                         cls : 'control-label col-form-label',
17642                         cn : [
17643                             {
17644                                 tag : 'span',
17645                                 html : this.fieldLabel
17646                             },
17647                             required
17648                         ]
17649                     },
17650                     {
17651                         cls : "roo-combobox-wrap ",
17652                         cn: [
17653                             combobox
17654                         ]
17655                     }
17656
17657                 ];
17658                 
17659                 labelCfg = cfg.cn[0];
17660                 contentCfg = cfg.cn[1];
17661             }
17662             
17663            
17664             
17665             if(this.labelWidth > 12){
17666                 labelCfg.style = "width: " + this.labelWidth + 'px';
17667             }
17668            
17669             if(this.labelWidth < 13 && this.labelmd == 0){
17670                 this.labelmd = this.labelWidth;
17671             }
17672             
17673             if(this.labellg > 0){
17674                 labelCfg.cls += ' col-lg-' + this.labellg;
17675                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17676             }
17677             
17678             if(this.labelmd > 0){
17679                 labelCfg.cls += ' col-md-' + this.labelmd;
17680                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17681             }
17682             
17683             if(this.labelsm > 0){
17684                 labelCfg.cls += ' col-sm-' + this.labelsm;
17685                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17686             }
17687             
17688             if(this.labelxs > 0){
17689                 labelCfg.cls += ' col-xs-' + this.labelxs;
17690                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17691             }
17692                 
17693                 
17694         } else if ( this.fieldLabel.length) {
17695             cfg.cn = [
17696                required,
17697                 {
17698                     tag: 'label',
17699                     cls : 'control-label',
17700                     html : this.fieldLabel
17701
17702                 },
17703                 {
17704                     cls : '', 
17705                     cn: [
17706                         combobox
17707                     ]
17708                 }
17709             ];
17710             
17711             if(this.indicatorpos == 'right'){
17712                 cfg.cn = [
17713                     {
17714                         tag: 'label',
17715                         cls : 'control-label',
17716                         html : this.fieldLabel,
17717                         cn : [
17718                             required
17719                         ]
17720                     },
17721                     {
17722                         cls : '', 
17723                         cn: [
17724                             combobox
17725                         ]
17726                     }
17727                 ];
17728             }
17729         } else {
17730             cfg.cn = combobox;    
17731         }
17732         
17733         
17734         var settings = this;
17735         
17736         ['xs','sm','md','lg'].map(function(size){
17737             if (settings[size]) {
17738                 cfg.cls += ' col-' + size + '-' + settings[size];
17739             }
17740         });
17741         
17742         return cfg;
17743     },
17744     
17745     initTouchView : function()
17746     {
17747         this.renderTouchView();
17748         
17749         this.touchViewEl.on('scroll', function(){
17750             this.el.dom.scrollTop = 0;
17751         }, this);
17752         
17753         this.originalValue = this.getValue();
17754         
17755         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17756         
17757         this.inputEl().on("click", this.showTouchView, this);
17758         if (this.triggerEl) {
17759             this.triggerEl.on("click", this.showTouchView, this);
17760         }
17761         
17762         
17763         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17764         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17765         
17766         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17767         
17768         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17769         this.store.on('load', this.onTouchViewLoad, this);
17770         this.store.on('loadexception', this.onTouchViewLoadException, this);
17771         
17772         if(this.hiddenName){
17773             
17774             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17775             
17776             this.hiddenField.dom.value =
17777                 this.hiddenValue !== undefined ? this.hiddenValue :
17778                 this.value !== undefined ? this.value : '';
17779         
17780             this.el.dom.removeAttribute('name');
17781             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17782         }
17783         
17784         if(this.multiple){
17785             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17786             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17787         }
17788         
17789         if(this.removable && !this.multiple){
17790             var close = this.closeTriggerEl();
17791             if(close){
17792                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17793                 close.on('click', this.removeBtnClick, this, close);
17794             }
17795         }
17796         /*
17797          * fix the bug in Safari iOS8
17798          */
17799         this.inputEl().on("focus", function(e){
17800             document.activeElement.blur();
17801         }, this);
17802         
17803         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17804         
17805         return;
17806         
17807         
17808     },
17809     
17810     renderTouchView : function()
17811     {
17812         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17813         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17814         
17815         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17816         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17817         
17818         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17819         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17820         this.touchViewBodyEl.setStyle('overflow', 'auto');
17821         
17822         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17823         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17824         
17825         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17826         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17827         
17828     },
17829     
17830     showTouchView : function()
17831     {
17832         if(this.disabled){
17833             return;
17834         }
17835         
17836         this.touchViewHeaderEl.hide();
17837
17838         if(this.modalTitle.length){
17839             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17840             this.touchViewHeaderEl.show();
17841         }
17842
17843         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17844         this.touchViewEl.show();
17845
17846         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17847         
17848         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17849         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17850
17851         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17852
17853         if(this.modalTitle.length){
17854             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17855         }
17856         
17857         this.touchViewBodyEl.setHeight(bodyHeight);
17858
17859         if(this.animate){
17860             var _this = this;
17861             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17862         }else{
17863             this.touchViewEl.addClass(['in','show']);
17864         }
17865         
17866         if(this._touchViewMask){
17867             Roo.get(document.body).addClass("x-body-masked");
17868             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17869             this._touchViewMask.setStyle('z-index', 10000);
17870             this._touchViewMask.addClass('show');
17871         }
17872         
17873         this.doTouchViewQuery();
17874         
17875     },
17876     
17877     hideTouchView : function()
17878     {
17879         this.touchViewEl.removeClass(['in','show']);
17880
17881         if(this.animate){
17882             var _this = this;
17883             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17884         }else{
17885             this.touchViewEl.setStyle('display', 'none');
17886         }
17887         
17888         if(this._touchViewMask){
17889             this._touchViewMask.removeClass('show');
17890             Roo.get(document.body).removeClass("x-body-masked");
17891         }
17892     },
17893     
17894     setTouchViewValue : function()
17895     {
17896         if(this.multiple){
17897             this.clearItem();
17898         
17899             var _this = this;
17900
17901             Roo.each(this.tickItems, function(o){
17902                 this.addItem(o);
17903             }, this);
17904         }
17905         
17906         this.hideTouchView();
17907     },
17908     
17909     doTouchViewQuery : function()
17910     {
17911         var qe = {
17912             query: '',
17913             forceAll: true,
17914             combo: this,
17915             cancel:false
17916         };
17917         
17918         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17919             return false;
17920         }
17921         
17922         if(!this.alwaysQuery || this.mode == 'local'){
17923             this.onTouchViewLoad();
17924             return;
17925         }
17926         
17927         this.store.load();
17928     },
17929     
17930     onTouchViewBeforeLoad : function(combo,opts)
17931     {
17932         return;
17933     },
17934
17935     // private
17936     onTouchViewLoad : function()
17937     {
17938         if(this.store.getCount() < 1){
17939             this.onTouchViewEmptyResults();
17940             return;
17941         }
17942         
17943         this.clearTouchView();
17944         
17945         var rawValue = this.getRawValue();
17946         
17947         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17948         
17949         this.tickItems = [];
17950         
17951         this.store.data.each(function(d, rowIndex){
17952             var row = this.touchViewListGroup.createChild(template);
17953             
17954             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17955                 row.addClass(d.data.cls);
17956             }
17957             
17958             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17959                 var cfg = {
17960                     data : d.data,
17961                     html : d.data[this.displayField]
17962                 };
17963                 
17964                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17965                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17966                 }
17967             }
17968             row.removeClass('selected');
17969             if(!this.multiple && this.valueField &&
17970                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17971             {
17972                 // radio buttons..
17973                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17974                 row.addClass('selected');
17975             }
17976             
17977             if(this.multiple && this.valueField &&
17978                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17979             {
17980                 
17981                 // checkboxes...
17982                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17983                 this.tickItems.push(d.data);
17984             }
17985             
17986             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17987             
17988         }, this);
17989         
17990         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17991         
17992         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17993
17994         if(this.modalTitle.length){
17995             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17996         }
17997
17998         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17999         
18000         if(this.mobile_restrict_height && listHeight < bodyHeight){
18001             this.touchViewBodyEl.setHeight(listHeight);
18002         }
18003         
18004         var _this = this;
18005         
18006         if(firstChecked && listHeight > bodyHeight){
18007             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18008         }
18009         
18010     },
18011     
18012     onTouchViewLoadException : function()
18013     {
18014         this.hideTouchView();
18015     },
18016     
18017     onTouchViewEmptyResults : function()
18018     {
18019         this.clearTouchView();
18020         
18021         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18022         
18023         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18024         
18025     },
18026     
18027     clearTouchView : function()
18028     {
18029         this.touchViewListGroup.dom.innerHTML = '';
18030     },
18031     
18032     onTouchViewClick : function(e, el, o)
18033     {
18034         e.preventDefault();
18035         
18036         var row = o.row;
18037         var rowIndex = o.rowIndex;
18038         
18039         var r = this.store.getAt(rowIndex);
18040         
18041         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18042             
18043             if(!this.multiple){
18044                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18045                     c.dom.removeAttribute('checked');
18046                 }, this);
18047
18048                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18049
18050                 this.setFromData(r.data);
18051
18052                 var close = this.closeTriggerEl();
18053
18054                 if(close){
18055                     close.show();
18056                 }
18057
18058                 this.hideTouchView();
18059
18060                 this.fireEvent('select', this, r, rowIndex);
18061
18062                 return;
18063             }
18064
18065             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18066                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18067                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18068                 return;
18069             }
18070
18071             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18072             this.addItem(r.data);
18073             this.tickItems.push(r.data);
18074         }
18075     },
18076     
18077     getAutoCreateNativeIOS : function()
18078     {
18079         var cfg = {
18080             cls: 'form-group' //input-group,
18081         };
18082         
18083         var combobox =  {
18084             tag: 'select',
18085             cls : 'roo-ios-select'
18086         };
18087         
18088         if (this.name) {
18089             combobox.name = this.name;
18090         }
18091         
18092         if (this.disabled) {
18093             combobox.disabled = true;
18094         }
18095         
18096         var settings = this;
18097         
18098         ['xs','sm','md','lg'].map(function(size){
18099             if (settings[size]) {
18100                 cfg.cls += ' col-' + size + '-' + settings[size];
18101             }
18102         });
18103         
18104         cfg.cn = combobox;
18105         
18106         return cfg;
18107         
18108     },
18109     
18110     initIOSView : function()
18111     {
18112         this.store.on('load', this.onIOSViewLoad, this);
18113         
18114         return;
18115     },
18116     
18117     onIOSViewLoad : function()
18118     {
18119         if(this.store.getCount() < 1){
18120             return;
18121         }
18122         
18123         this.clearIOSView();
18124         
18125         if(this.allowBlank) {
18126             
18127             var default_text = '-- SELECT --';
18128             
18129             if(this.placeholder.length){
18130                 default_text = this.placeholder;
18131             }
18132             
18133             if(this.emptyTitle.length){
18134                 default_text += ' - ' + this.emptyTitle + ' -';
18135             }
18136             
18137             var opt = this.inputEl().createChild({
18138                 tag: 'option',
18139                 value : 0,
18140                 html : default_text
18141             });
18142             
18143             var o = {};
18144             o[this.valueField] = 0;
18145             o[this.displayField] = default_text;
18146             
18147             this.ios_options.push({
18148                 data : o,
18149                 el : opt
18150             });
18151             
18152         }
18153         
18154         this.store.data.each(function(d, rowIndex){
18155             
18156             var html = '';
18157             
18158             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18159                 html = d.data[this.displayField];
18160             }
18161             
18162             var value = '';
18163             
18164             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18165                 value = d.data[this.valueField];
18166             }
18167             
18168             var option = {
18169                 tag: 'option',
18170                 value : value,
18171                 html : html
18172             };
18173             
18174             if(this.value == d.data[this.valueField]){
18175                 option['selected'] = true;
18176             }
18177             
18178             var opt = this.inputEl().createChild(option);
18179             
18180             this.ios_options.push({
18181                 data : d.data,
18182                 el : opt
18183             });
18184             
18185         }, this);
18186         
18187         this.inputEl().on('change', function(){
18188            this.fireEvent('select', this);
18189         }, this);
18190         
18191     },
18192     
18193     clearIOSView: function()
18194     {
18195         this.inputEl().dom.innerHTML = '';
18196         
18197         this.ios_options = [];
18198     },
18199     
18200     setIOSValue: function(v)
18201     {
18202         this.value = v;
18203         
18204         if(!this.ios_options){
18205             return;
18206         }
18207         
18208         Roo.each(this.ios_options, function(opts){
18209            
18210            opts.el.dom.removeAttribute('selected');
18211            
18212            if(opts.data[this.valueField] != v){
18213                return;
18214            }
18215            
18216            opts.el.dom.setAttribute('selected', true);
18217            
18218         }, this);
18219     }
18220
18221     /** 
18222     * @cfg {Boolean} grow 
18223     * @hide 
18224     */
18225     /** 
18226     * @cfg {Number} growMin 
18227     * @hide 
18228     */
18229     /** 
18230     * @cfg {Number} growMax 
18231     * @hide 
18232     */
18233     /**
18234      * @hide
18235      * @method autoSize
18236      */
18237 });
18238
18239 Roo.apply(Roo.bootstrap.ComboBox,  {
18240     
18241     header : {
18242         tag: 'div',
18243         cls: 'modal-header',
18244         cn: [
18245             {
18246                 tag: 'h4',
18247                 cls: 'modal-title'
18248             }
18249         ]
18250     },
18251     
18252     body : {
18253         tag: 'div',
18254         cls: 'modal-body',
18255         cn: [
18256             {
18257                 tag: 'ul',
18258                 cls: 'list-group'
18259             }
18260         ]
18261     },
18262     
18263     listItemRadio : {
18264         tag: 'li',
18265         cls: 'list-group-item',
18266         cn: [
18267             {
18268                 tag: 'span',
18269                 cls: 'roo-combobox-list-group-item-value'
18270             },
18271             {
18272                 tag: 'div',
18273                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18274                 cn: [
18275                     {
18276                         tag: 'input',
18277                         type: 'radio'
18278                     },
18279                     {
18280                         tag: 'label'
18281                     }
18282                 ]
18283             }
18284         ]
18285     },
18286     
18287     listItemCheckbox : {
18288         tag: 'li',
18289         cls: 'list-group-item',
18290         cn: [
18291             {
18292                 tag: 'span',
18293                 cls: 'roo-combobox-list-group-item-value'
18294             },
18295             {
18296                 tag: 'div',
18297                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18298                 cn: [
18299                     {
18300                         tag: 'input',
18301                         type: 'checkbox'
18302                     },
18303                     {
18304                         tag: 'label'
18305                     }
18306                 ]
18307             }
18308         ]
18309     },
18310     
18311     emptyResult : {
18312         tag: 'div',
18313         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18314     },
18315     
18316     footer : {
18317         tag: 'div',
18318         cls: 'modal-footer',
18319         cn: [
18320             {
18321                 tag: 'div',
18322                 cls: 'row',
18323                 cn: [
18324                     {
18325                         tag: 'div',
18326                         cls: 'col-xs-6 text-left',
18327                         cn: {
18328                             tag: 'button',
18329                             cls: 'btn btn-danger roo-touch-view-cancel',
18330                             html: 'Cancel'
18331                         }
18332                     },
18333                     {
18334                         tag: 'div',
18335                         cls: 'col-xs-6 text-right',
18336                         cn: {
18337                             tag: 'button',
18338                             cls: 'btn btn-success roo-touch-view-ok',
18339                             html: 'OK'
18340                         }
18341                     }
18342                 ]
18343             }
18344         ]
18345         
18346     }
18347 });
18348
18349 Roo.apply(Roo.bootstrap.ComboBox,  {
18350     
18351     touchViewTemplate : {
18352         tag: 'div',
18353         cls: 'modal fade roo-combobox-touch-view',
18354         cn: [
18355             {
18356                 tag: 'div',
18357                 cls: 'modal-dialog',
18358                 style : 'position:fixed', // we have to fix position....
18359                 cn: [
18360                     {
18361                         tag: 'div',
18362                         cls: 'modal-content',
18363                         cn: [
18364                             Roo.bootstrap.ComboBox.header,
18365                             Roo.bootstrap.ComboBox.body,
18366                             Roo.bootstrap.ComboBox.footer
18367                         ]
18368                     }
18369                 ]
18370             }
18371         ]
18372     }
18373 });/*
18374  * Based on:
18375  * Ext JS Library 1.1.1
18376  * Copyright(c) 2006-2007, Ext JS, LLC.
18377  *
18378  * Originally Released Under LGPL - original licence link has changed is not relivant.
18379  *
18380  * Fork - LGPL
18381  * <script type="text/javascript">
18382  */
18383
18384 /**
18385  * @class Roo.View
18386  * @extends Roo.util.Observable
18387  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18388  * This class also supports single and multi selection modes. <br>
18389  * Create a data model bound view:
18390  <pre><code>
18391  var store = new Roo.data.Store(...);
18392
18393  var view = new Roo.View({
18394     el : "my-element",
18395     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18396  
18397     singleSelect: true,
18398     selectedClass: "ydataview-selected",
18399     store: store
18400  });
18401
18402  // listen for node click?
18403  view.on("click", function(vw, index, node, e){
18404  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18405  });
18406
18407  // load XML data
18408  dataModel.load("foobar.xml");
18409  </code></pre>
18410  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18411  * <br><br>
18412  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18413  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18414  * 
18415  * Note: old style constructor is still suported (container, template, config)
18416  * 
18417  * @constructor
18418  * Create a new View
18419  * @param {Object} config The config object
18420  * 
18421  */
18422 Roo.View = function(config, depreciated_tpl, depreciated_config){
18423     
18424     this.parent = false;
18425     
18426     if (typeof(depreciated_tpl) == 'undefined') {
18427         // new way.. - universal constructor.
18428         Roo.apply(this, config);
18429         this.el  = Roo.get(this.el);
18430     } else {
18431         // old format..
18432         this.el  = Roo.get(config);
18433         this.tpl = depreciated_tpl;
18434         Roo.apply(this, depreciated_config);
18435     }
18436     this.wrapEl  = this.el.wrap().wrap();
18437     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18438     
18439     
18440     if(typeof(this.tpl) == "string"){
18441         this.tpl = new Roo.Template(this.tpl);
18442     } else {
18443         // support xtype ctors..
18444         this.tpl = new Roo.factory(this.tpl, Roo);
18445     }
18446     
18447     
18448     this.tpl.compile();
18449     
18450     /** @private */
18451     this.addEvents({
18452         /**
18453          * @event beforeclick
18454          * Fires before a click is processed. Returns false to cancel the default action.
18455          * @param {Roo.View} this
18456          * @param {Number} index The index of the target node
18457          * @param {HTMLElement} node The target node
18458          * @param {Roo.EventObject} e The raw event object
18459          */
18460             "beforeclick" : true,
18461         /**
18462          * @event click
18463          * Fires when a template node is clicked.
18464          * @param {Roo.View} this
18465          * @param {Number} index The index of the target node
18466          * @param {HTMLElement} node The target node
18467          * @param {Roo.EventObject} e The raw event object
18468          */
18469             "click" : true,
18470         /**
18471          * @event dblclick
18472          * Fires when a template node is double clicked.
18473          * @param {Roo.View} this
18474          * @param {Number} index The index of the target node
18475          * @param {HTMLElement} node The target node
18476          * @param {Roo.EventObject} e The raw event object
18477          */
18478             "dblclick" : true,
18479         /**
18480          * @event contextmenu
18481          * Fires when a template node is right clicked.
18482          * @param {Roo.View} this
18483          * @param {Number} index The index of the target node
18484          * @param {HTMLElement} node The target node
18485          * @param {Roo.EventObject} e The raw event object
18486          */
18487             "contextmenu" : true,
18488         /**
18489          * @event selectionchange
18490          * Fires when the selected nodes change.
18491          * @param {Roo.View} this
18492          * @param {Array} selections Array of the selected nodes
18493          */
18494             "selectionchange" : true,
18495     
18496         /**
18497          * @event beforeselect
18498          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18499          * @param {Roo.View} this
18500          * @param {HTMLElement} node The node to be selected
18501          * @param {Array} selections Array of currently selected nodes
18502          */
18503             "beforeselect" : true,
18504         /**
18505          * @event preparedata
18506          * Fires on every row to render, to allow you to change the data.
18507          * @param {Roo.View} this
18508          * @param {Object} data to be rendered (change this)
18509          */
18510           "preparedata" : true
18511           
18512           
18513         });
18514
18515
18516
18517     this.el.on({
18518         "click": this.onClick,
18519         "dblclick": this.onDblClick,
18520         "contextmenu": this.onContextMenu,
18521         scope:this
18522     });
18523
18524     this.selections = [];
18525     this.nodes = [];
18526     this.cmp = new Roo.CompositeElementLite([]);
18527     if(this.store){
18528         this.store = Roo.factory(this.store, Roo.data);
18529         this.setStore(this.store, true);
18530     }
18531     
18532     if ( this.footer && this.footer.xtype) {
18533            
18534          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18535         
18536         this.footer.dataSource = this.store;
18537         this.footer.container = fctr;
18538         this.footer = Roo.factory(this.footer, Roo);
18539         fctr.insertFirst(this.el);
18540         
18541         // this is a bit insane - as the paging toolbar seems to detach the el..
18542 //        dom.parentNode.parentNode.parentNode
18543          // they get detached?
18544     }
18545     
18546     
18547     Roo.View.superclass.constructor.call(this);
18548     
18549     
18550 };
18551
18552 Roo.extend(Roo.View, Roo.util.Observable, {
18553     
18554      /**
18555      * @cfg {Roo.data.Store} store Data store to load data from.
18556      */
18557     store : false,
18558     
18559     /**
18560      * @cfg {String|Roo.Element} el The container element.
18561      */
18562     el : '',
18563     
18564     /**
18565      * @cfg {String|Roo.Template} tpl The template used by this View 
18566      */
18567     tpl : false,
18568     /**
18569      * @cfg {String} dataName the named area of the template to use as the data area
18570      *                          Works with domtemplates roo-name="name"
18571      */
18572     dataName: false,
18573     /**
18574      * @cfg {String} selectedClass The css class to add to selected nodes
18575      */
18576     selectedClass : "x-view-selected",
18577      /**
18578      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18579      */
18580     emptyText : "",
18581     
18582     /**
18583      * @cfg {String} text to display on mask (default Loading)
18584      */
18585     mask : false,
18586     /**
18587      * @cfg {Boolean} multiSelect Allow multiple selection
18588      */
18589     multiSelect : false,
18590     /**
18591      * @cfg {Boolean} singleSelect Allow single selection
18592      */
18593     singleSelect:  false,
18594     
18595     /**
18596      * @cfg {Boolean} toggleSelect - selecting 
18597      */
18598     toggleSelect : false,
18599     
18600     /**
18601      * @cfg {Boolean} tickable - selecting 
18602      */
18603     tickable : false,
18604     
18605     /**
18606      * Returns the element this view is bound to.
18607      * @return {Roo.Element}
18608      */
18609     getEl : function(){
18610         return this.wrapEl;
18611     },
18612     
18613     
18614
18615     /**
18616      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18617      */
18618     refresh : function(){
18619         //Roo.log('refresh');
18620         var t = this.tpl;
18621         
18622         // if we are using something like 'domtemplate', then
18623         // the what gets used is:
18624         // t.applySubtemplate(NAME, data, wrapping data..)
18625         // the outer template then get' applied with
18626         //     the store 'extra data'
18627         // and the body get's added to the
18628         //      roo-name="data" node?
18629         //      <span class='roo-tpl-{name}'></span> ?????
18630         
18631         
18632         
18633         this.clearSelections();
18634         this.el.update("");
18635         var html = [];
18636         var records = this.store.getRange();
18637         if(records.length < 1) {
18638             
18639             // is this valid??  = should it render a template??
18640             
18641             this.el.update(this.emptyText);
18642             return;
18643         }
18644         var el = this.el;
18645         if (this.dataName) {
18646             this.el.update(t.apply(this.store.meta)); //????
18647             el = this.el.child('.roo-tpl-' + this.dataName);
18648         }
18649         
18650         for(var i = 0, len = records.length; i < len; i++){
18651             var data = this.prepareData(records[i].data, i, records[i]);
18652             this.fireEvent("preparedata", this, data, i, records[i]);
18653             
18654             var d = Roo.apply({}, data);
18655             
18656             if(this.tickable){
18657                 Roo.apply(d, {'roo-id' : Roo.id()});
18658                 
18659                 var _this = this;
18660             
18661                 Roo.each(this.parent.item, function(item){
18662                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18663                         return;
18664                     }
18665                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18666                 });
18667             }
18668             
18669             html[html.length] = Roo.util.Format.trim(
18670                 this.dataName ?
18671                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18672                     t.apply(d)
18673             );
18674         }
18675         
18676         
18677         
18678         el.update(html.join(""));
18679         this.nodes = el.dom.childNodes;
18680         this.updateIndexes(0);
18681     },
18682     
18683
18684     /**
18685      * Function to override to reformat the data that is sent to
18686      * the template for each node.
18687      * DEPRICATED - use the preparedata event handler.
18688      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18689      * a JSON object for an UpdateManager bound view).
18690      */
18691     prepareData : function(data, index, record)
18692     {
18693         this.fireEvent("preparedata", this, data, index, record);
18694         return data;
18695     },
18696
18697     onUpdate : function(ds, record){
18698         // Roo.log('on update');   
18699         this.clearSelections();
18700         var index = this.store.indexOf(record);
18701         var n = this.nodes[index];
18702         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18703         n.parentNode.removeChild(n);
18704         this.updateIndexes(index, index);
18705     },
18706
18707     
18708     
18709 // --------- FIXME     
18710     onAdd : function(ds, records, index)
18711     {
18712         //Roo.log(['on Add', ds, records, index] );        
18713         this.clearSelections();
18714         if(this.nodes.length == 0){
18715             this.refresh();
18716             return;
18717         }
18718         var n = this.nodes[index];
18719         for(var i = 0, len = records.length; i < len; i++){
18720             var d = this.prepareData(records[i].data, i, records[i]);
18721             if(n){
18722                 this.tpl.insertBefore(n, d);
18723             }else{
18724                 
18725                 this.tpl.append(this.el, d);
18726             }
18727         }
18728         this.updateIndexes(index);
18729     },
18730
18731     onRemove : function(ds, record, index){
18732        // Roo.log('onRemove');
18733         this.clearSelections();
18734         var el = this.dataName  ?
18735             this.el.child('.roo-tpl-' + this.dataName) :
18736             this.el; 
18737         
18738         el.dom.removeChild(this.nodes[index]);
18739         this.updateIndexes(index);
18740     },
18741
18742     /**
18743      * Refresh an individual node.
18744      * @param {Number} index
18745      */
18746     refreshNode : function(index){
18747         this.onUpdate(this.store, this.store.getAt(index));
18748     },
18749
18750     updateIndexes : function(startIndex, endIndex){
18751         var ns = this.nodes;
18752         startIndex = startIndex || 0;
18753         endIndex = endIndex || ns.length - 1;
18754         for(var i = startIndex; i <= endIndex; i++){
18755             ns[i].nodeIndex = i;
18756         }
18757     },
18758
18759     /**
18760      * Changes the data store this view uses and refresh the view.
18761      * @param {Store} store
18762      */
18763     setStore : function(store, initial){
18764         if(!initial && this.store){
18765             this.store.un("datachanged", this.refresh);
18766             this.store.un("add", this.onAdd);
18767             this.store.un("remove", this.onRemove);
18768             this.store.un("update", this.onUpdate);
18769             this.store.un("clear", this.refresh);
18770             this.store.un("beforeload", this.onBeforeLoad);
18771             this.store.un("load", this.onLoad);
18772             this.store.un("loadexception", this.onLoad);
18773         }
18774         if(store){
18775           
18776             store.on("datachanged", this.refresh, this);
18777             store.on("add", this.onAdd, this);
18778             store.on("remove", this.onRemove, this);
18779             store.on("update", this.onUpdate, this);
18780             store.on("clear", this.refresh, this);
18781             store.on("beforeload", this.onBeforeLoad, this);
18782             store.on("load", this.onLoad, this);
18783             store.on("loadexception", this.onLoad, this);
18784         }
18785         
18786         if(store){
18787             this.refresh();
18788         }
18789     },
18790     /**
18791      * onbeforeLoad - masks the loading area.
18792      *
18793      */
18794     onBeforeLoad : function(store,opts)
18795     {
18796          //Roo.log('onBeforeLoad');   
18797         if (!opts.add) {
18798             this.el.update("");
18799         }
18800         this.el.mask(this.mask ? this.mask : "Loading" ); 
18801     },
18802     onLoad : function ()
18803     {
18804         this.el.unmask();
18805     },
18806     
18807
18808     /**
18809      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18810      * @param {HTMLElement} node
18811      * @return {HTMLElement} The template node
18812      */
18813     findItemFromChild : function(node){
18814         var el = this.dataName  ?
18815             this.el.child('.roo-tpl-' + this.dataName,true) :
18816             this.el.dom; 
18817         
18818         if(!node || node.parentNode == el){
18819                     return node;
18820             }
18821             var p = node.parentNode;
18822             while(p && p != el){
18823             if(p.parentNode == el){
18824                 return p;
18825             }
18826             p = p.parentNode;
18827         }
18828             return null;
18829     },
18830
18831     /** @ignore */
18832     onClick : function(e){
18833         var item = this.findItemFromChild(e.getTarget());
18834         if(item){
18835             var index = this.indexOf(item);
18836             if(this.onItemClick(item, index, e) !== false){
18837                 this.fireEvent("click", this, index, item, e);
18838             }
18839         }else{
18840             this.clearSelections();
18841         }
18842     },
18843
18844     /** @ignore */
18845     onContextMenu : function(e){
18846         var item = this.findItemFromChild(e.getTarget());
18847         if(item){
18848             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18849         }
18850     },
18851
18852     /** @ignore */
18853     onDblClick : function(e){
18854         var item = this.findItemFromChild(e.getTarget());
18855         if(item){
18856             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18857         }
18858     },
18859
18860     onItemClick : function(item, index, e)
18861     {
18862         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18863             return false;
18864         }
18865         if (this.toggleSelect) {
18866             var m = this.isSelected(item) ? 'unselect' : 'select';
18867             //Roo.log(m);
18868             var _t = this;
18869             _t[m](item, true, false);
18870             return true;
18871         }
18872         if(this.multiSelect || this.singleSelect){
18873             if(this.multiSelect && e.shiftKey && this.lastSelection){
18874                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18875             }else{
18876                 this.select(item, this.multiSelect && e.ctrlKey);
18877                 this.lastSelection = item;
18878             }
18879             
18880             if(!this.tickable){
18881                 e.preventDefault();
18882             }
18883             
18884         }
18885         return true;
18886     },
18887
18888     /**
18889      * Get the number of selected nodes.
18890      * @return {Number}
18891      */
18892     getSelectionCount : function(){
18893         return this.selections.length;
18894     },
18895
18896     /**
18897      * Get the currently selected nodes.
18898      * @return {Array} An array of HTMLElements
18899      */
18900     getSelectedNodes : function(){
18901         return this.selections;
18902     },
18903
18904     /**
18905      * Get the indexes of the selected nodes.
18906      * @return {Array}
18907      */
18908     getSelectedIndexes : function(){
18909         var indexes = [], s = this.selections;
18910         for(var i = 0, len = s.length; i < len; i++){
18911             indexes.push(s[i].nodeIndex);
18912         }
18913         return indexes;
18914     },
18915
18916     /**
18917      * Clear all selections
18918      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18919      */
18920     clearSelections : function(suppressEvent){
18921         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18922             this.cmp.elements = this.selections;
18923             this.cmp.removeClass(this.selectedClass);
18924             this.selections = [];
18925             if(!suppressEvent){
18926                 this.fireEvent("selectionchange", this, this.selections);
18927             }
18928         }
18929     },
18930
18931     /**
18932      * Returns true if the passed node is selected
18933      * @param {HTMLElement/Number} node The node or node index
18934      * @return {Boolean}
18935      */
18936     isSelected : function(node){
18937         var s = this.selections;
18938         if(s.length < 1){
18939             return false;
18940         }
18941         node = this.getNode(node);
18942         return s.indexOf(node) !== -1;
18943     },
18944
18945     /**
18946      * Selects nodes.
18947      * @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
18948      * @param {Boolean} keepExisting (optional) true to keep existing selections
18949      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18950      */
18951     select : function(nodeInfo, keepExisting, suppressEvent){
18952         if(nodeInfo instanceof Array){
18953             if(!keepExisting){
18954                 this.clearSelections(true);
18955             }
18956             for(var i = 0, len = nodeInfo.length; i < len; i++){
18957                 this.select(nodeInfo[i], true, true);
18958             }
18959             return;
18960         } 
18961         var node = this.getNode(nodeInfo);
18962         if(!node || this.isSelected(node)){
18963             return; // already selected.
18964         }
18965         if(!keepExisting){
18966             this.clearSelections(true);
18967         }
18968         
18969         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18970             Roo.fly(node).addClass(this.selectedClass);
18971             this.selections.push(node);
18972             if(!suppressEvent){
18973                 this.fireEvent("selectionchange", this, this.selections);
18974             }
18975         }
18976         
18977         
18978     },
18979       /**
18980      * Unselects nodes.
18981      * @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
18982      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18983      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18984      */
18985     unselect : function(nodeInfo, keepExisting, suppressEvent)
18986     {
18987         if(nodeInfo instanceof Array){
18988             Roo.each(this.selections, function(s) {
18989                 this.unselect(s, nodeInfo);
18990             }, this);
18991             return;
18992         }
18993         var node = this.getNode(nodeInfo);
18994         if(!node || !this.isSelected(node)){
18995             //Roo.log("not selected");
18996             return; // not selected.
18997         }
18998         // fireevent???
18999         var ns = [];
19000         Roo.each(this.selections, function(s) {
19001             if (s == node ) {
19002                 Roo.fly(node).removeClass(this.selectedClass);
19003
19004                 return;
19005             }
19006             ns.push(s);
19007         },this);
19008         
19009         this.selections= ns;
19010         this.fireEvent("selectionchange", this, this.selections);
19011     },
19012
19013     /**
19014      * Gets a template node.
19015      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19016      * @return {HTMLElement} The node or null if it wasn't found
19017      */
19018     getNode : function(nodeInfo){
19019         if(typeof nodeInfo == "string"){
19020             return document.getElementById(nodeInfo);
19021         }else if(typeof nodeInfo == "number"){
19022             return this.nodes[nodeInfo];
19023         }
19024         return nodeInfo;
19025     },
19026
19027     /**
19028      * Gets a range template nodes.
19029      * @param {Number} startIndex
19030      * @param {Number} endIndex
19031      * @return {Array} An array of nodes
19032      */
19033     getNodes : function(start, end){
19034         var ns = this.nodes;
19035         start = start || 0;
19036         end = typeof end == "undefined" ? ns.length - 1 : end;
19037         var nodes = [];
19038         if(start <= end){
19039             for(var i = start; i <= end; i++){
19040                 nodes.push(ns[i]);
19041             }
19042         } else{
19043             for(var i = start; i >= end; i--){
19044                 nodes.push(ns[i]);
19045             }
19046         }
19047         return nodes;
19048     },
19049
19050     /**
19051      * Finds the index of the passed node
19052      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19053      * @return {Number} The index of the node or -1
19054      */
19055     indexOf : function(node){
19056         node = this.getNode(node);
19057         if(typeof node.nodeIndex == "number"){
19058             return node.nodeIndex;
19059         }
19060         var ns = this.nodes;
19061         for(var i = 0, len = ns.length; i < len; i++){
19062             if(ns[i] == node){
19063                 return i;
19064             }
19065         }
19066         return -1;
19067     }
19068 });
19069 /*
19070  * - LGPL
19071  *
19072  * based on jquery fullcalendar
19073  * 
19074  */
19075
19076 Roo.bootstrap = Roo.bootstrap || {};
19077 /**
19078  * @class Roo.bootstrap.Calendar
19079  * @extends Roo.bootstrap.Component
19080  * Bootstrap Calendar class
19081  * @cfg {Boolean} loadMask (true|false) default false
19082  * @cfg {Object} header generate the user specific header of the calendar, default false
19083
19084  * @constructor
19085  * Create a new Container
19086  * @param {Object} config The config object
19087  */
19088
19089
19090
19091 Roo.bootstrap.Calendar = function(config){
19092     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19093      this.addEvents({
19094         /**
19095              * @event select
19096              * Fires when a date is selected
19097              * @param {DatePicker} this
19098              * @param {Date} date The selected date
19099              */
19100         'select': true,
19101         /**
19102              * @event monthchange
19103              * Fires when the displayed month changes 
19104              * @param {DatePicker} this
19105              * @param {Date} date The selected month
19106              */
19107         'monthchange': true,
19108         /**
19109              * @event evententer
19110              * Fires when mouse over an event
19111              * @param {Calendar} this
19112              * @param {event} Event
19113              */
19114         'evententer': true,
19115         /**
19116              * @event eventleave
19117              * Fires when the mouse leaves an
19118              * @param {Calendar} this
19119              * @param {event}
19120              */
19121         'eventleave': true,
19122         /**
19123              * @event eventclick
19124              * Fires when the mouse click an
19125              * @param {Calendar} this
19126              * @param {event}
19127              */
19128         'eventclick': true
19129         
19130     });
19131
19132 };
19133
19134 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19135     
19136      /**
19137      * @cfg {Number} startDay
19138      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19139      */
19140     startDay : 0,
19141     
19142     loadMask : false,
19143     
19144     header : false,
19145       
19146     getAutoCreate : function(){
19147         
19148         
19149         var fc_button = function(name, corner, style, content ) {
19150             return Roo.apply({},{
19151                 tag : 'span',
19152                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19153                          (corner.length ?
19154                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19155                             ''
19156                         ),
19157                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19158                 unselectable: 'on'
19159             });
19160         };
19161         
19162         var header = {};
19163         
19164         if(!this.header){
19165             header = {
19166                 tag : 'table',
19167                 cls : 'fc-header',
19168                 style : 'width:100%',
19169                 cn : [
19170                     {
19171                         tag: 'tr',
19172                         cn : [
19173                             {
19174                                 tag : 'td',
19175                                 cls : 'fc-header-left',
19176                                 cn : [
19177                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19178                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19179                                     { tag: 'span', cls: 'fc-header-space' },
19180                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19181
19182
19183                                 ]
19184                             },
19185
19186                             {
19187                                 tag : 'td',
19188                                 cls : 'fc-header-center',
19189                                 cn : [
19190                                     {
19191                                         tag: 'span',
19192                                         cls: 'fc-header-title',
19193                                         cn : {
19194                                             tag: 'H2',
19195                                             html : 'month / year'
19196                                         }
19197                                     }
19198
19199                                 ]
19200                             },
19201                             {
19202                                 tag : 'td',
19203                                 cls : 'fc-header-right',
19204                                 cn : [
19205                               /*      fc_button('month', 'left', '', 'month' ),
19206                                     fc_button('week', '', '', 'week' ),
19207                                     fc_button('day', 'right', '', 'day' )
19208                                 */    
19209
19210                                 ]
19211                             }
19212
19213                         ]
19214                     }
19215                 ]
19216             };
19217         }
19218         
19219         header = this.header;
19220         
19221        
19222         var cal_heads = function() {
19223             var ret = [];
19224             // fixme - handle this.
19225             
19226             for (var i =0; i < Date.dayNames.length; i++) {
19227                 var d = Date.dayNames[i];
19228                 ret.push({
19229                     tag: 'th',
19230                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19231                     html : d.substring(0,3)
19232                 });
19233                 
19234             }
19235             ret[0].cls += ' fc-first';
19236             ret[6].cls += ' fc-last';
19237             return ret;
19238         };
19239         var cal_cell = function(n) {
19240             return  {
19241                 tag: 'td',
19242                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19243                 cn : [
19244                     {
19245                         cn : [
19246                             {
19247                                 cls: 'fc-day-number',
19248                                 html: 'D'
19249                             },
19250                             {
19251                                 cls: 'fc-day-content',
19252                              
19253                                 cn : [
19254                                      {
19255                                         style: 'position: relative;' // height: 17px;
19256                                     }
19257                                 ]
19258                             }
19259                             
19260                             
19261                         ]
19262                     }
19263                 ]
19264                 
19265             }
19266         };
19267         var cal_rows = function() {
19268             
19269             var ret = [];
19270             for (var r = 0; r < 6; r++) {
19271                 var row= {
19272                     tag : 'tr',
19273                     cls : 'fc-week',
19274                     cn : []
19275                 };
19276                 
19277                 for (var i =0; i < Date.dayNames.length; i++) {
19278                     var d = Date.dayNames[i];
19279                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19280
19281                 }
19282                 row.cn[0].cls+=' fc-first';
19283                 row.cn[0].cn[0].style = 'min-height:90px';
19284                 row.cn[6].cls+=' fc-last';
19285                 ret.push(row);
19286                 
19287             }
19288             ret[0].cls += ' fc-first';
19289             ret[4].cls += ' fc-prev-last';
19290             ret[5].cls += ' fc-last';
19291             return ret;
19292             
19293         };
19294         
19295         var cal_table = {
19296             tag: 'table',
19297             cls: 'fc-border-separate',
19298             style : 'width:100%',
19299             cellspacing  : 0,
19300             cn : [
19301                 { 
19302                     tag: 'thead',
19303                     cn : [
19304                         { 
19305                             tag: 'tr',
19306                             cls : 'fc-first fc-last',
19307                             cn : cal_heads()
19308                         }
19309                     ]
19310                 },
19311                 { 
19312                     tag: 'tbody',
19313                     cn : cal_rows()
19314                 }
19315                   
19316             ]
19317         };
19318          
19319          var cfg = {
19320             cls : 'fc fc-ltr',
19321             cn : [
19322                 header,
19323                 {
19324                     cls : 'fc-content',
19325                     style : "position: relative;",
19326                     cn : [
19327                         {
19328                             cls : 'fc-view fc-view-month fc-grid',
19329                             style : 'position: relative',
19330                             unselectable : 'on',
19331                             cn : [
19332                                 {
19333                                     cls : 'fc-event-container',
19334                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19335                                 },
19336                                 cal_table
19337                             ]
19338                         }
19339                     ]
19340     
19341                 }
19342            ] 
19343             
19344         };
19345         
19346          
19347         
19348         return cfg;
19349     },
19350     
19351     
19352     initEvents : function()
19353     {
19354         if(!this.store){
19355             throw "can not find store for calendar";
19356         }
19357         
19358         var mark = {
19359             tag: "div",
19360             cls:"x-dlg-mask",
19361             style: "text-align:center",
19362             cn: [
19363                 {
19364                     tag: "div",
19365                     style: "background-color:white;width:50%;margin:250 auto",
19366                     cn: [
19367                         {
19368                             tag: "img",
19369                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19370                         },
19371                         {
19372                             tag: "span",
19373                             html: "Loading"
19374                         }
19375                         
19376                     ]
19377                 }
19378             ]
19379         };
19380         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19381         
19382         var size = this.el.select('.fc-content', true).first().getSize();
19383         this.maskEl.setSize(size.width, size.height);
19384         this.maskEl.enableDisplayMode("block");
19385         if(!this.loadMask){
19386             this.maskEl.hide();
19387         }
19388         
19389         this.store = Roo.factory(this.store, Roo.data);
19390         this.store.on('load', this.onLoad, this);
19391         this.store.on('beforeload', this.onBeforeLoad, this);
19392         
19393         this.resize();
19394         
19395         this.cells = this.el.select('.fc-day',true);
19396         //Roo.log(this.cells);
19397         this.textNodes = this.el.query('.fc-day-number');
19398         this.cells.addClassOnOver('fc-state-hover');
19399         
19400         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19401         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19402         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19403         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19404         
19405         this.on('monthchange', this.onMonthChange, this);
19406         
19407         this.update(new Date().clearTime());
19408     },
19409     
19410     resize : function() {
19411         var sz  = this.el.getSize();
19412         
19413         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19414         this.el.select('.fc-day-content div',true).setHeight(34);
19415     },
19416     
19417     
19418     // private
19419     showPrevMonth : function(e){
19420         this.update(this.activeDate.add("mo", -1));
19421     },
19422     showToday : function(e){
19423         this.update(new Date().clearTime());
19424     },
19425     // private
19426     showNextMonth : function(e){
19427         this.update(this.activeDate.add("mo", 1));
19428     },
19429
19430     // private
19431     showPrevYear : function(){
19432         this.update(this.activeDate.add("y", -1));
19433     },
19434
19435     // private
19436     showNextYear : function(){
19437         this.update(this.activeDate.add("y", 1));
19438     },
19439
19440     
19441    // private
19442     update : function(date)
19443     {
19444         var vd = this.activeDate;
19445         this.activeDate = date;
19446 //        if(vd && this.el){
19447 //            var t = date.getTime();
19448 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19449 //                Roo.log('using add remove');
19450 //                
19451 //                this.fireEvent('monthchange', this, date);
19452 //                
19453 //                this.cells.removeClass("fc-state-highlight");
19454 //                this.cells.each(function(c){
19455 //                   if(c.dateValue == t){
19456 //                       c.addClass("fc-state-highlight");
19457 //                       setTimeout(function(){
19458 //                            try{c.dom.firstChild.focus();}catch(e){}
19459 //                       }, 50);
19460 //                       return false;
19461 //                   }
19462 //                   return true;
19463 //                });
19464 //                return;
19465 //            }
19466 //        }
19467         
19468         var days = date.getDaysInMonth();
19469         
19470         var firstOfMonth = date.getFirstDateOfMonth();
19471         var startingPos = firstOfMonth.getDay()-this.startDay;
19472         
19473         if(startingPos < this.startDay){
19474             startingPos += 7;
19475         }
19476         
19477         var pm = date.add(Date.MONTH, -1);
19478         var prevStart = pm.getDaysInMonth()-startingPos;
19479 //        
19480         this.cells = this.el.select('.fc-day',true);
19481         this.textNodes = this.el.query('.fc-day-number');
19482         this.cells.addClassOnOver('fc-state-hover');
19483         
19484         var cells = this.cells.elements;
19485         var textEls = this.textNodes;
19486         
19487         Roo.each(cells, function(cell){
19488             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19489         });
19490         
19491         days += startingPos;
19492
19493         // convert everything to numbers so it's fast
19494         var day = 86400000;
19495         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19496         //Roo.log(d);
19497         //Roo.log(pm);
19498         //Roo.log(prevStart);
19499         
19500         var today = new Date().clearTime().getTime();
19501         var sel = date.clearTime().getTime();
19502         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19503         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19504         var ddMatch = this.disabledDatesRE;
19505         var ddText = this.disabledDatesText;
19506         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19507         var ddaysText = this.disabledDaysText;
19508         var format = this.format;
19509         
19510         var setCellClass = function(cal, cell){
19511             cell.row = 0;
19512             cell.events = [];
19513             cell.more = [];
19514             //Roo.log('set Cell Class');
19515             cell.title = "";
19516             var t = d.getTime();
19517             
19518             //Roo.log(d);
19519             
19520             cell.dateValue = t;
19521             if(t == today){
19522                 cell.className += " fc-today";
19523                 cell.className += " fc-state-highlight";
19524                 cell.title = cal.todayText;
19525             }
19526             if(t == sel){
19527                 // disable highlight in other month..
19528                 //cell.className += " fc-state-highlight";
19529                 
19530             }
19531             // disabling
19532             if(t < min) {
19533                 cell.className = " fc-state-disabled";
19534                 cell.title = cal.minText;
19535                 return;
19536             }
19537             if(t > max) {
19538                 cell.className = " fc-state-disabled";
19539                 cell.title = cal.maxText;
19540                 return;
19541             }
19542             if(ddays){
19543                 if(ddays.indexOf(d.getDay()) != -1){
19544                     cell.title = ddaysText;
19545                     cell.className = " fc-state-disabled";
19546                 }
19547             }
19548             if(ddMatch && format){
19549                 var fvalue = d.dateFormat(format);
19550                 if(ddMatch.test(fvalue)){
19551                     cell.title = ddText.replace("%0", fvalue);
19552                     cell.className = " fc-state-disabled";
19553                 }
19554             }
19555             
19556             if (!cell.initialClassName) {
19557                 cell.initialClassName = cell.dom.className;
19558             }
19559             
19560             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19561         };
19562
19563         var i = 0;
19564         
19565         for(; i < startingPos; i++) {
19566             textEls[i].innerHTML = (++prevStart);
19567             d.setDate(d.getDate()+1);
19568             
19569             cells[i].className = "fc-past fc-other-month";
19570             setCellClass(this, cells[i]);
19571         }
19572         
19573         var intDay = 0;
19574         
19575         for(; i < days; i++){
19576             intDay = i - startingPos + 1;
19577             textEls[i].innerHTML = (intDay);
19578             d.setDate(d.getDate()+1);
19579             
19580             cells[i].className = ''; // "x-date-active";
19581             setCellClass(this, cells[i]);
19582         }
19583         var extraDays = 0;
19584         
19585         for(; i < 42; i++) {
19586             textEls[i].innerHTML = (++extraDays);
19587             d.setDate(d.getDate()+1);
19588             
19589             cells[i].className = "fc-future fc-other-month";
19590             setCellClass(this, cells[i]);
19591         }
19592         
19593         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19594         
19595         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19596         
19597         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19598         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19599         
19600         if(totalRows != 6){
19601             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19602             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19603         }
19604         
19605         this.fireEvent('monthchange', this, date);
19606         
19607         
19608         /*
19609         if(!this.internalRender){
19610             var main = this.el.dom.firstChild;
19611             var w = main.offsetWidth;
19612             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19613             Roo.fly(main).setWidth(w);
19614             this.internalRender = true;
19615             // opera does not respect the auto grow header center column
19616             // then, after it gets a width opera refuses to recalculate
19617             // without a second pass
19618             if(Roo.isOpera && !this.secondPass){
19619                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19620                 this.secondPass = true;
19621                 this.update.defer(10, this, [date]);
19622             }
19623         }
19624         */
19625         
19626     },
19627     
19628     findCell : function(dt) {
19629         dt = dt.clearTime().getTime();
19630         var ret = false;
19631         this.cells.each(function(c){
19632             //Roo.log("check " +c.dateValue + '?=' + dt);
19633             if(c.dateValue == dt){
19634                 ret = c;
19635                 return false;
19636             }
19637             return true;
19638         });
19639         
19640         return ret;
19641     },
19642     
19643     findCells : function(ev) {
19644         var s = ev.start.clone().clearTime().getTime();
19645        // Roo.log(s);
19646         var e= ev.end.clone().clearTime().getTime();
19647        // Roo.log(e);
19648         var ret = [];
19649         this.cells.each(function(c){
19650              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19651             
19652             if(c.dateValue > e){
19653                 return ;
19654             }
19655             if(c.dateValue < s){
19656                 return ;
19657             }
19658             ret.push(c);
19659         });
19660         
19661         return ret;    
19662     },
19663     
19664 //    findBestRow: function(cells)
19665 //    {
19666 //        var ret = 0;
19667 //        
19668 //        for (var i =0 ; i < cells.length;i++) {
19669 //            ret  = Math.max(cells[i].rows || 0,ret);
19670 //        }
19671 //        return ret;
19672 //        
19673 //    },
19674     
19675     
19676     addItem : function(ev)
19677     {
19678         // look for vertical location slot in
19679         var cells = this.findCells(ev);
19680         
19681 //        ev.row = this.findBestRow(cells);
19682         
19683         // work out the location.
19684         
19685         var crow = false;
19686         var rows = [];
19687         for(var i =0; i < cells.length; i++) {
19688             
19689             cells[i].row = cells[0].row;
19690             
19691             if(i == 0){
19692                 cells[i].row = cells[i].row + 1;
19693             }
19694             
19695             if (!crow) {
19696                 crow = {
19697                     start : cells[i],
19698                     end :  cells[i]
19699                 };
19700                 continue;
19701             }
19702             if (crow.start.getY() == cells[i].getY()) {
19703                 // on same row.
19704                 crow.end = cells[i];
19705                 continue;
19706             }
19707             // different row.
19708             rows.push(crow);
19709             crow = {
19710                 start: cells[i],
19711                 end : cells[i]
19712             };
19713             
19714         }
19715         
19716         rows.push(crow);
19717         ev.els = [];
19718         ev.rows = rows;
19719         ev.cells = cells;
19720         
19721         cells[0].events.push(ev);
19722         
19723         this.calevents.push(ev);
19724     },
19725     
19726     clearEvents: function() {
19727         
19728         if(!this.calevents){
19729             return;
19730         }
19731         
19732         Roo.each(this.cells.elements, function(c){
19733             c.row = 0;
19734             c.events = [];
19735             c.more = [];
19736         });
19737         
19738         Roo.each(this.calevents, function(e) {
19739             Roo.each(e.els, function(el) {
19740                 el.un('mouseenter' ,this.onEventEnter, this);
19741                 el.un('mouseleave' ,this.onEventLeave, this);
19742                 el.remove();
19743             },this);
19744         },this);
19745         
19746         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19747             e.remove();
19748         });
19749         
19750     },
19751     
19752     renderEvents: function()
19753     {   
19754         var _this = this;
19755         
19756         this.cells.each(function(c) {
19757             
19758             if(c.row < 5){
19759                 return;
19760             }
19761             
19762             var ev = c.events;
19763             
19764             var r = 4;
19765             if(c.row != c.events.length){
19766                 r = 4 - (4 - (c.row - c.events.length));
19767             }
19768             
19769             c.events = ev.slice(0, r);
19770             c.more = ev.slice(r);
19771             
19772             if(c.more.length && c.more.length == 1){
19773                 c.events.push(c.more.pop());
19774             }
19775             
19776             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19777             
19778         });
19779             
19780         this.cells.each(function(c) {
19781             
19782             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19783             
19784             
19785             for (var e = 0; e < c.events.length; e++){
19786                 var ev = c.events[e];
19787                 var rows = ev.rows;
19788                 
19789                 for(var i = 0; i < rows.length; i++) {
19790                 
19791                     // how many rows should it span..
19792
19793                     var  cfg = {
19794                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19795                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19796
19797                         unselectable : "on",
19798                         cn : [
19799                             {
19800                                 cls: 'fc-event-inner',
19801                                 cn : [
19802     //                                {
19803     //                                  tag:'span',
19804     //                                  cls: 'fc-event-time',
19805     //                                  html : cells.length > 1 ? '' : ev.time
19806     //                                },
19807                                     {
19808                                       tag:'span',
19809                                       cls: 'fc-event-title',
19810                                       html : String.format('{0}', ev.title)
19811                                     }
19812
19813
19814                                 ]
19815                             },
19816                             {
19817                                 cls: 'ui-resizable-handle ui-resizable-e',
19818                                 html : '&nbsp;&nbsp;&nbsp'
19819                             }
19820
19821                         ]
19822                     };
19823
19824                     if (i == 0) {
19825                         cfg.cls += ' fc-event-start';
19826                     }
19827                     if ((i+1) == rows.length) {
19828                         cfg.cls += ' fc-event-end';
19829                     }
19830
19831                     var ctr = _this.el.select('.fc-event-container',true).first();
19832                     var cg = ctr.createChild(cfg);
19833
19834                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19835                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19836
19837                     var r = (c.more.length) ? 1 : 0;
19838                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19839                     cg.setWidth(ebox.right - sbox.x -2);
19840
19841                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19842                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19843                     cg.on('click', _this.onEventClick, _this, ev);
19844
19845                     ev.els.push(cg);
19846                     
19847                 }
19848                 
19849             }
19850             
19851             
19852             if(c.more.length){
19853                 var  cfg = {
19854                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19855                     style : 'position: absolute',
19856                     unselectable : "on",
19857                     cn : [
19858                         {
19859                             cls: 'fc-event-inner',
19860                             cn : [
19861                                 {
19862                                   tag:'span',
19863                                   cls: 'fc-event-title',
19864                                   html : 'More'
19865                                 }
19866
19867
19868                             ]
19869                         },
19870                         {
19871                             cls: 'ui-resizable-handle ui-resizable-e',
19872                             html : '&nbsp;&nbsp;&nbsp'
19873                         }
19874
19875                     ]
19876                 };
19877
19878                 var ctr = _this.el.select('.fc-event-container',true).first();
19879                 var cg = ctr.createChild(cfg);
19880
19881                 var sbox = c.select('.fc-day-content',true).first().getBox();
19882                 var ebox = c.select('.fc-day-content',true).first().getBox();
19883                 //Roo.log(cg);
19884                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19885                 cg.setWidth(ebox.right - sbox.x -2);
19886
19887                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19888                 
19889             }
19890             
19891         });
19892         
19893         
19894         
19895     },
19896     
19897     onEventEnter: function (e, el,event,d) {
19898         this.fireEvent('evententer', this, el, event);
19899     },
19900     
19901     onEventLeave: function (e, el,event,d) {
19902         this.fireEvent('eventleave', this, el, event);
19903     },
19904     
19905     onEventClick: function (e, el,event,d) {
19906         this.fireEvent('eventclick', this, el, event);
19907     },
19908     
19909     onMonthChange: function () {
19910         this.store.load();
19911     },
19912     
19913     onMoreEventClick: function(e, el, more)
19914     {
19915         var _this = this;
19916         
19917         this.calpopover.placement = 'right';
19918         this.calpopover.setTitle('More');
19919         
19920         this.calpopover.setContent('');
19921         
19922         var ctr = this.calpopover.el.select('.popover-content', true).first();
19923         
19924         Roo.each(more, function(m){
19925             var cfg = {
19926                 cls : 'fc-event-hori fc-event-draggable',
19927                 html : m.title
19928             };
19929             var cg = ctr.createChild(cfg);
19930             
19931             cg.on('click', _this.onEventClick, _this, m);
19932         });
19933         
19934         this.calpopover.show(el);
19935         
19936         
19937     },
19938     
19939     onLoad: function () 
19940     {   
19941         this.calevents = [];
19942         var cal = this;
19943         
19944         if(this.store.getCount() > 0){
19945             this.store.data.each(function(d){
19946                cal.addItem({
19947                     id : d.data.id,
19948                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19949                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19950                     time : d.data.start_time,
19951                     title : d.data.title,
19952                     description : d.data.description,
19953                     venue : d.data.venue
19954                 });
19955             });
19956         }
19957         
19958         this.renderEvents();
19959         
19960         if(this.calevents.length && this.loadMask){
19961             this.maskEl.hide();
19962         }
19963     },
19964     
19965     onBeforeLoad: function()
19966     {
19967         this.clearEvents();
19968         if(this.loadMask){
19969             this.maskEl.show();
19970         }
19971     }
19972 });
19973
19974  
19975  /*
19976  * - LGPL
19977  *
19978  * element
19979  * 
19980  */
19981
19982 /**
19983  * @class Roo.bootstrap.Popover
19984  * @extends Roo.bootstrap.Component
19985  * Bootstrap Popover class
19986  * @cfg {String} html contents of the popover   (or false to use children..)
19987  * @cfg {String} title of popover (or false to hide)
19988  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19989  * @cfg {String} trigger click || hover (or false to trigger manually)
19990  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19991  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19992  *      - if false and it has a 'parent' then it will be automatically added to that element
19993  *      - if string - Roo.get  will be called 
19994  * @cfg {Number} delay - delay before showing
19995  
19996  * @constructor
19997  * Create a new Popover
19998  * @param {Object} config The config object
19999  */
20000
20001 Roo.bootstrap.Popover = function(config){
20002     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20003     
20004     this.addEvents({
20005         // raw events
20006          /**
20007          * @event show
20008          * After the popover show
20009          * 
20010          * @param {Roo.bootstrap.Popover} this
20011          */
20012         "show" : true,
20013         /**
20014          * @event hide
20015          * After the popover hide
20016          * 
20017          * @param {Roo.bootstrap.Popover} this
20018          */
20019         "hide" : true
20020     });
20021 };
20022
20023 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20024     
20025     title: false,
20026     html: false,
20027     
20028     placement : 'right',
20029     trigger : 'hover', // hover
20030     modal : false,
20031     delay : 0,
20032     
20033     over: false,
20034     
20035     can_build_overlaid : false,
20036     
20037     maskEl : false, // the mask element
20038     headerEl : false,
20039     contentEl : false,
20040     alignEl : false, // when show is called with an element - this get's stored.
20041     
20042     getChildContainer : function()
20043     {
20044         return this.contentEl;
20045         
20046     },
20047     getPopoverHeader : function()
20048     {
20049         this.title = true; // flag not to hide it..
20050         this.headerEl.addClass('p-0');
20051         return this.headerEl
20052     },
20053     
20054     
20055     getAutoCreate : function(){
20056          
20057         var cfg = {
20058            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20059            style: 'display:block',
20060            cn : [
20061                 {
20062                     cls : 'arrow'
20063                 },
20064                 {
20065                     cls : 'popover-inner ',
20066                     cn : [
20067                         {
20068                             tag: 'h3',
20069                             cls: 'popover-title popover-header',
20070                             html : this.title === false ? '' : this.title
20071                         },
20072                         {
20073                             cls : 'popover-content popover-body '  + (this.cls || ''),
20074                             html : this.html || ''
20075                         }
20076                     ]
20077                     
20078                 }
20079            ]
20080         };
20081         
20082         return cfg;
20083     },
20084     /**
20085      * @param {string} the title
20086      */
20087     setTitle: function(str)
20088     {
20089         this.title = str;
20090         if (this.el) {
20091             this.headerEl.dom.innerHTML = str;
20092         }
20093         
20094     },
20095     /**
20096      * @param {string} the body content
20097      */
20098     setContent: function(str)
20099     {
20100         this.html = str;
20101         if (this.contentEl) {
20102             this.contentEl.dom.innerHTML = str;
20103         }
20104         
20105     },
20106     // as it get's added to the bottom of the page.
20107     onRender : function(ct, position)
20108     {
20109         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20110         
20111         
20112         
20113         if(!this.el){
20114             var cfg = Roo.apply({},  this.getAutoCreate());
20115             cfg.id = Roo.id();
20116             
20117             if (this.cls) {
20118                 cfg.cls += ' ' + this.cls;
20119             }
20120             if (this.style) {
20121                 cfg.style = this.style;
20122             }
20123             //Roo.log("adding to ");
20124             this.el = Roo.get(document.body).createChild(cfg, position);
20125 //            Roo.log(this.el);
20126         }
20127         
20128         this.contentEl = this.el.select('.popover-content',true).first();
20129         this.headerEl =  this.el.select('.popover-title',true).first();
20130         
20131         var nitems = [];
20132         if(typeof(this.items) != 'undefined'){
20133             var items = this.items;
20134             delete this.items;
20135
20136             for(var i =0;i < items.length;i++) {
20137                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20138             }
20139         }
20140
20141         this.items = nitems;
20142         
20143         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20144         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20145         
20146         
20147         
20148         this.initEvents();
20149     },
20150     
20151     resizeMask : function()
20152     {
20153         this.maskEl.setSize(
20154             Roo.lib.Dom.getViewWidth(true),
20155             Roo.lib.Dom.getViewHeight(true)
20156         );
20157     },
20158     
20159     initEvents : function()
20160     {
20161         
20162         if (!this.modal) { 
20163             Roo.bootstrap.Popover.register(this);
20164         }
20165          
20166         this.arrowEl = this.el.select('.arrow',true).first();
20167         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20168         this.el.enableDisplayMode('block');
20169         this.el.hide();
20170  
20171         
20172         if (this.over === false && !this.parent()) {
20173             return; 
20174         }
20175         if (this.triggers === false) {
20176             return;
20177         }
20178          
20179         // support parent
20180         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20181         var triggers = this.trigger ? this.trigger.split(' ') : [];
20182         Roo.each(triggers, function(trigger) {
20183         
20184             if (trigger == 'click') {
20185                 on_el.on('click', this.toggle, this);
20186             } else if (trigger != 'manual') {
20187                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20188                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20189       
20190                 on_el.on(eventIn  ,this.enter, this);
20191                 on_el.on(eventOut, this.leave, this);
20192             }
20193         }, this);
20194     },
20195     
20196     
20197     // private
20198     timeout : null,
20199     hoverState : null,
20200     
20201     toggle : function () {
20202         this.hoverState == 'in' ? this.leave() : this.enter();
20203     },
20204     
20205     enter : function () {
20206         
20207         clearTimeout(this.timeout);
20208     
20209         this.hoverState = 'in';
20210     
20211         if (!this.delay || !this.delay.show) {
20212             this.show();
20213             return;
20214         }
20215         var _t = this;
20216         this.timeout = setTimeout(function () {
20217             if (_t.hoverState == 'in') {
20218                 _t.show();
20219             }
20220         }, this.delay.show)
20221     },
20222     
20223     leave : function() {
20224         clearTimeout(this.timeout);
20225     
20226         this.hoverState = 'out';
20227     
20228         if (!this.delay || !this.delay.hide) {
20229             this.hide();
20230             return;
20231         }
20232         var _t = this;
20233         this.timeout = setTimeout(function () {
20234             if (_t.hoverState == 'out') {
20235                 _t.hide();
20236             }
20237         }, this.delay.hide)
20238     },
20239     /**
20240      * Show the popover
20241      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20242      * @param {string} (left|right|top|bottom) position
20243      */
20244     show : function (on_el, placement)
20245     {
20246         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20247         on_el = on_el || false; // default to false
20248          
20249         if (!on_el) {
20250             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20251                 on_el = this.parent().el;
20252             } else if (this.over) {
20253                 on_el = Roo.get(this.over);
20254             }
20255             
20256         }
20257         
20258         this.alignEl = Roo.get( on_el );
20259
20260         if (!this.el) {
20261             this.render(document.body);
20262         }
20263         
20264         
20265          
20266         
20267         if (this.title === false) {
20268             this.headerEl.hide();
20269         }
20270         
20271        
20272         this.el.show();
20273         this.el.dom.style.display = 'block';
20274          
20275  
20276         if (this.alignEl) {
20277             this.updatePosition(this.placement, true);
20278              
20279         } else {
20280             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20281             var es = this.el.getSize();
20282             var x = Roo.lib.Dom.getViewWidth()/2;
20283             var y = Roo.lib.Dom.getViewHeight()/2;
20284             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20285             
20286         }
20287
20288         
20289         //var arrow = this.el.select('.arrow',true).first();
20290         //arrow.set(align[2], 
20291         
20292         this.el.addClass('in');
20293         
20294          
20295         
20296         this.hoverState = 'in';
20297         
20298         if (this.modal) {
20299             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20300             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20301             this.maskEl.dom.style.display = 'block';
20302             this.maskEl.addClass('show');
20303         }
20304         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20305  
20306         this.fireEvent('show', this);
20307         
20308     },
20309     /**
20310      * fire this manually after loading a grid in the table for example
20311      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20312      * @param {Boolean} try and move it if we cant get right position.
20313      */
20314     updatePosition : function(placement, try_move)
20315     {
20316         // allow for calling with no parameters
20317         placement = placement   ? placement :  this.placement;
20318         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20319         
20320         this.el.removeClass([
20321             'fade','top','bottom', 'left', 'right','in',
20322             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20323         ]);
20324         this.el.addClass(placement + ' bs-popover-' + placement);
20325         
20326         if (!this.alignEl ) {
20327             return false;
20328         }
20329         
20330         switch (placement) {
20331             case 'right':
20332                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20333                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20334                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20335                     //normal display... or moved up/down.
20336                     this.el.setXY(offset);
20337                     var xy = this.alignEl.getAnchorXY('tr', false);
20338                     xy[0]+=2;xy[1]+=5;
20339                     this.arrowEl.setXY(xy);
20340                     return true;
20341                 }
20342                 // continue through...
20343                 return this.updatePosition('left', false);
20344                 
20345             
20346             case 'left':
20347                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20348                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20349                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20350                     //normal display... or moved up/down.
20351                     this.el.setXY(offset);
20352                     var xy = this.alignEl.getAnchorXY('tl', false);
20353                     xy[0]-=10;xy[1]+=5; // << fix me
20354                     this.arrowEl.setXY(xy);
20355                     return true;
20356                 }
20357                 // call self...
20358                 return this.updatePosition('right', false);
20359             
20360             case 'top':
20361                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20362                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20363                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20364                     //normal display... or moved up/down.
20365                     this.el.setXY(offset);
20366                     var xy = this.alignEl.getAnchorXY('t', false);
20367                     xy[1]-=10; // << fix me
20368                     this.arrowEl.setXY(xy);
20369                     return true;
20370                 }
20371                 // fall through
20372                return this.updatePosition('bottom', false);
20373             
20374             case 'bottom':
20375                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20376                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20377                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20378                     //normal display... or moved up/down.
20379                     this.el.setXY(offset);
20380                     var xy = this.alignEl.getAnchorXY('b', false);
20381                      xy[1]+=2; // << fix me
20382                     this.arrowEl.setXY(xy);
20383                     return true;
20384                 }
20385                 // fall through
20386                 return this.updatePosition('top', false);
20387                 
20388             
20389         }
20390         
20391         
20392         return false;
20393     },
20394     
20395     hide : function()
20396     {
20397         this.el.setXY([0,0]);
20398         this.el.removeClass('in');
20399         this.el.hide();
20400         this.hoverState = null;
20401         this.maskEl.hide(); // always..
20402         this.fireEvent('hide', this);
20403     }
20404     
20405 });
20406
20407
20408 Roo.apply(Roo.bootstrap.Popover, {
20409
20410     alignment : {
20411         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20412         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20413         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20414         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20415     },
20416     
20417     zIndex : 20001,
20418
20419     clickHander : false,
20420     
20421     
20422
20423     onMouseDown : function(e)
20424     {
20425         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20426             /// what is nothing is showing..
20427             this.hideAll();
20428         }
20429          
20430     },
20431     
20432     
20433     popups : [],
20434     
20435     register : function(popup)
20436     {
20437         if (!Roo.bootstrap.Popover.clickHandler) {
20438             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20439         }
20440         // hide other popups.
20441         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20442         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20443         this.hideAll(); //<< why?
20444         //this.popups.push(popup);
20445     },
20446     hideAll : function()
20447     {
20448         this.popups.forEach(function(p) {
20449             p.hide();
20450         });
20451     },
20452     onShow : function() {
20453         Roo.bootstrap.Popover.popups.push(this);
20454     },
20455     onHide : function() {
20456         Roo.bootstrap.Popover.popups.remove(this);
20457     } 
20458
20459 });/*
20460  * - LGPL
20461  *
20462  * Card header - holder for the card header elements.
20463  * 
20464  */
20465
20466 /**
20467  * @class Roo.bootstrap.PopoverNav
20468  * @extends Roo.bootstrap.NavGroup
20469  * Bootstrap Popover header navigation class
20470  * @constructor
20471  * Create a new Popover Header Navigation 
20472  * @param {Object} config The config object
20473  */
20474
20475 Roo.bootstrap.PopoverNav = function(config){
20476     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20477 };
20478
20479 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20480     
20481     
20482     container_method : 'getPopoverHeader' 
20483     
20484      
20485     
20486     
20487    
20488 });
20489
20490  
20491
20492  /*
20493  * - LGPL
20494  *
20495  * Progress
20496  * 
20497  */
20498
20499 /**
20500  * @class Roo.bootstrap.Progress
20501  * @extends Roo.bootstrap.Component
20502  * Bootstrap Progress class
20503  * @cfg {Boolean} striped striped of the progress bar
20504  * @cfg {Boolean} active animated of the progress bar
20505  * 
20506  * 
20507  * @constructor
20508  * Create a new Progress
20509  * @param {Object} config The config object
20510  */
20511
20512 Roo.bootstrap.Progress = function(config){
20513     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20514 };
20515
20516 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20517     
20518     striped : false,
20519     active: false,
20520     
20521     getAutoCreate : function(){
20522         var cfg = {
20523             tag: 'div',
20524             cls: 'progress'
20525         };
20526         
20527         
20528         if(this.striped){
20529             cfg.cls += ' progress-striped';
20530         }
20531       
20532         if(this.active){
20533             cfg.cls += ' active';
20534         }
20535         
20536         
20537         return cfg;
20538     }
20539    
20540 });
20541
20542  
20543
20544  /*
20545  * - LGPL
20546  *
20547  * ProgressBar
20548  * 
20549  */
20550
20551 /**
20552  * @class Roo.bootstrap.ProgressBar
20553  * @extends Roo.bootstrap.Component
20554  * Bootstrap ProgressBar class
20555  * @cfg {Number} aria_valuenow aria-value now
20556  * @cfg {Number} aria_valuemin aria-value min
20557  * @cfg {Number} aria_valuemax aria-value max
20558  * @cfg {String} label label for the progress bar
20559  * @cfg {String} panel (success | info | warning | danger )
20560  * @cfg {String} role role of the progress bar
20561  * @cfg {String} sr_only text
20562  * 
20563  * 
20564  * @constructor
20565  * Create a new ProgressBar
20566  * @param {Object} config The config object
20567  */
20568
20569 Roo.bootstrap.ProgressBar = function(config){
20570     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20571 };
20572
20573 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20574     
20575     aria_valuenow : 0,
20576     aria_valuemin : 0,
20577     aria_valuemax : 100,
20578     label : false,
20579     panel : false,
20580     role : false,
20581     sr_only: false,
20582     
20583     getAutoCreate : function()
20584     {
20585         
20586         var cfg = {
20587             tag: 'div',
20588             cls: 'progress-bar',
20589             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20590         };
20591         
20592         if(this.sr_only){
20593             cfg.cn = {
20594                 tag: 'span',
20595                 cls: 'sr-only',
20596                 html: this.sr_only
20597             }
20598         }
20599         
20600         if(this.role){
20601             cfg.role = this.role;
20602         }
20603         
20604         if(this.aria_valuenow){
20605             cfg['aria-valuenow'] = this.aria_valuenow;
20606         }
20607         
20608         if(this.aria_valuemin){
20609             cfg['aria-valuemin'] = this.aria_valuemin;
20610         }
20611         
20612         if(this.aria_valuemax){
20613             cfg['aria-valuemax'] = this.aria_valuemax;
20614         }
20615         
20616         if(this.label && !this.sr_only){
20617             cfg.html = this.label;
20618         }
20619         
20620         if(this.panel){
20621             cfg.cls += ' progress-bar-' + this.panel;
20622         }
20623         
20624         return cfg;
20625     },
20626     
20627     update : function(aria_valuenow)
20628     {
20629         this.aria_valuenow = aria_valuenow;
20630         
20631         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20632     }
20633    
20634 });
20635
20636  
20637
20638  /*
20639  * - LGPL
20640  *
20641  * column
20642  * 
20643  */
20644
20645 /**
20646  * @class Roo.bootstrap.TabGroup
20647  * @extends Roo.bootstrap.Column
20648  * Bootstrap Column class
20649  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20650  * @cfg {Boolean} carousel true to make the group behave like a carousel
20651  * @cfg {Boolean} bullets show bullets for the panels
20652  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20653  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20654  * @cfg {Boolean} showarrow (true|false) show arrow default true
20655  * 
20656  * @constructor
20657  * Create a new TabGroup
20658  * @param {Object} config The config object
20659  */
20660
20661 Roo.bootstrap.TabGroup = function(config){
20662     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20663     if (!this.navId) {
20664         this.navId = Roo.id();
20665     }
20666     this.tabs = [];
20667     Roo.bootstrap.TabGroup.register(this);
20668     
20669 };
20670
20671 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20672     
20673     carousel : false,
20674     transition : false,
20675     bullets : 0,
20676     timer : 0,
20677     autoslide : false,
20678     slideFn : false,
20679     slideOnTouch : false,
20680     showarrow : true,
20681     
20682     getAutoCreate : function()
20683     {
20684         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20685         
20686         cfg.cls += ' tab-content';
20687         
20688         if (this.carousel) {
20689             cfg.cls += ' carousel slide';
20690             
20691             cfg.cn = [{
20692                cls : 'carousel-inner',
20693                cn : []
20694             }];
20695         
20696             if(this.bullets  && !Roo.isTouch){
20697                 
20698                 var bullets = {
20699                     cls : 'carousel-bullets',
20700                     cn : []
20701                 };
20702                
20703                 if(this.bullets_cls){
20704                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20705                 }
20706                 
20707                 bullets.cn.push({
20708                     cls : 'clear'
20709                 });
20710                 
20711                 cfg.cn[0].cn.push(bullets);
20712             }
20713             
20714             if(this.showarrow){
20715                 cfg.cn[0].cn.push({
20716                     tag : 'div',
20717                     class : 'carousel-arrow',
20718                     cn : [
20719                         {
20720                             tag : 'div',
20721                             class : 'carousel-prev',
20722                             cn : [
20723                                 {
20724                                     tag : 'i',
20725                                     class : 'fa fa-chevron-left'
20726                                 }
20727                             ]
20728                         },
20729                         {
20730                             tag : 'div',
20731                             class : 'carousel-next',
20732                             cn : [
20733                                 {
20734                                     tag : 'i',
20735                                     class : 'fa fa-chevron-right'
20736                                 }
20737                             ]
20738                         }
20739                     ]
20740                 });
20741             }
20742             
20743         }
20744         
20745         return cfg;
20746     },
20747     
20748     initEvents:  function()
20749     {
20750 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20751 //            this.el.on("touchstart", this.onTouchStart, this);
20752 //        }
20753         
20754         if(this.autoslide){
20755             var _this = this;
20756             
20757             this.slideFn = window.setInterval(function() {
20758                 _this.showPanelNext();
20759             }, this.timer);
20760         }
20761         
20762         if(this.showarrow){
20763             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20764             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20765         }
20766         
20767         
20768     },
20769     
20770 //    onTouchStart : function(e, el, o)
20771 //    {
20772 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20773 //            return;
20774 //        }
20775 //        
20776 //        this.showPanelNext();
20777 //    },
20778     
20779     
20780     getChildContainer : function()
20781     {
20782         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20783     },
20784     
20785     /**
20786     * register a Navigation item
20787     * @param {Roo.bootstrap.NavItem} the navitem to add
20788     */
20789     register : function(item)
20790     {
20791         this.tabs.push( item);
20792         item.navId = this.navId; // not really needed..
20793         this.addBullet();
20794     
20795     },
20796     
20797     getActivePanel : function()
20798     {
20799         var r = false;
20800         Roo.each(this.tabs, function(t) {
20801             if (t.active) {
20802                 r = t;
20803                 return false;
20804             }
20805             return null;
20806         });
20807         return r;
20808         
20809     },
20810     getPanelByName : function(n)
20811     {
20812         var r = false;
20813         Roo.each(this.tabs, function(t) {
20814             if (t.tabId == n) {
20815                 r = t;
20816                 return false;
20817             }
20818             return null;
20819         });
20820         return r;
20821     },
20822     indexOfPanel : function(p)
20823     {
20824         var r = false;
20825         Roo.each(this.tabs, function(t,i) {
20826             if (t.tabId == p.tabId) {
20827                 r = i;
20828                 return false;
20829             }
20830             return null;
20831         });
20832         return r;
20833     },
20834     /**
20835      * show a specific panel
20836      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20837      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20838      */
20839     showPanel : function (pan)
20840     {
20841         if(this.transition || typeof(pan) == 'undefined'){
20842             Roo.log("waiting for the transitionend");
20843             return false;
20844         }
20845         
20846         if (typeof(pan) == 'number') {
20847             pan = this.tabs[pan];
20848         }
20849         
20850         if (typeof(pan) == 'string') {
20851             pan = this.getPanelByName(pan);
20852         }
20853         
20854         var cur = this.getActivePanel();
20855         
20856         if(!pan || !cur){
20857             Roo.log('pan or acitve pan is undefined');
20858             return false;
20859         }
20860         
20861         if (pan.tabId == this.getActivePanel().tabId) {
20862             return true;
20863         }
20864         
20865         if (false === cur.fireEvent('beforedeactivate')) {
20866             return false;
20867         }
20868         
20869         if(this.bullets > 0 && !Roo.isTouch){
20870             this.setActiveBullet(this.indexOfPanel(pan));
20871         }
20872         
20873         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20874             
20875             //class="carousel-item carousel-item-next carousel-item-left"
20876             
20877             this.transition = true;
20878             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20879             var lr = dir == 'next' ? 'left' : 'right';
20880             pan.el.addClass(dir); // or prev
20881             pan.el.addClass('carousel-item-' + dir); // or prev
20882             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20883             cur.el.addClass(lr); // or right
20884             pan.el.addClass(lr);
20885             cur.el.addClass('carousel-item-' +lr); // or right
20886             pan.el.addClass('carousel-item-' +lr);
20887             
20888             
20889             var _this = this;
20890             cur.el.on('transitionend', function() {
20891                 Roo.log("trans end?");
20892                 
20893                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20894                 pan.setActive(true);
20895                 
20896                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20897                 cur.setActive(false);
20898                 
20899                 _this.transition = false;
20900                 
20901             }, this, { single:  true } );
20902             
20903             return true;
20904         }
20905         
20906         cur.setActive(false);
20907         pan.setActive(true);
20908         
20909         return true;
20910         
20911     },
20912     showPanelNext : function()
20913     {
20914         var i = this.indexOfPanel(this.getActivePanel());
20915         
20916         if (i >= this.tabs.length - 1 && !this.autoslide) {
20917             return;
20918         }
20919         
20920         if (i >= this.tabs.length - 1 && this.autoslide) {
20921             i = -1;
20922         }
20923         
20924         this.showPanel(this.tabs[i+1]);
20925     },
20926     
20927     showPanelPrev : function()
20928     {
20929         var i = this.indexOfPanel(this.getActivePanel());
20930         
20931         if (i  < 1 && !this.autoslide) {
20932             return;
20933         }
20934         
20935         if (i < 1 && this.autoslide) {
20936             i = this.tabs.length;
20937         }
20938         
20939         this.showPanel(this.tabs[i-1]);
20940     },
20941     
20942     
20943     addBullet: function()
20944     {
20945         if(!this.bullets || Roo.isTouch){
20946             return;
20947         }
20948         var ctr = this.el.select('.carousel-bullets',true).first();
20949         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20950         var bullet = ctr.createChild({
20951             cls : 'bullet bullet-' + i
20952         },ctr.dom.lastChild);
20953         
20954         
20955         var _this = this;
20956         
20957         bullet.on('click', (function(e, el, o, ii, t){
20958
20959             e.preventDefault();
20960
20961             this.showPanel(ii);
20962
20963             if(this.autoslide && this.slideFn){
20964                 clearInterval(this.slideFn);
20965                 this.slideFn = window.setInterval(function() {
20966                     _this.showPanelNext();
20967                 }, this.timer);
20968             }
20969
20970         }).createDelegate(this, [i, bullet], true));
20971                 
20972         
20973     },
20974      
20975     setActiveBullet : function(i)
20976     {
20977         if(Roo.isTouch){
20978             return;
20979         }
20980         
20981         Roo.each(this.el.select('.bullet', true).elements, function(el){
20982             el.removeClass('selected');
20983         });
20984
20985         var bullet = this.el.select('.bullet-' + i, true).first();
20986         
20987         if(!bullet){
20988             return;
20989         }
20990         
20991         bullet.addClass('selected');
20992     }
20993     
20994     
20995   
20996 });
20997
20998  
20999
21000  
21001  
21002 Roo.apply(Roo.bootstrap.TabGroup, {
21003     
21004     groups: {},
21005      /**
21006     * register a Navigation Group
21007     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21008     */
21009     register : function(navgrp)
21010     {
21011         this.groups[navgrp.navId] = navgrp;
21012         
21013     },
21014     /**
21015     * fetch a Navigation Group based on the navigation ID
21016     * if one does not exist , it will get created.
21017     * @param {string} the navgroup to add
21018     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21019     */
21020     get: function(navId) {
21021         if (typeof(this.groups[navId]) == 'undefined') {
21022             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21023         }
21024         return this.groups[navId] ;
21025     }
21026     
21027     
21028     
21029 });
21030
21031  /*
21032  * - LGPL
21033  *
21034  * TabPanel
21035  * 
21036  */
21037
21038 /**
21039  * @class Roo.bootstrap.TabPanel
21040  * @extends Roo.bootstrap.Component
21041  * Bootstrap TabPanel class
21042  * @cfg {Boolean} active panel active
21043  * @cfg {String} html panel content
21044  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21045  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21046  * @cfg {String} href click to link..
21047  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21048  * 
21049  * 
21050  * @constructor
21051  * Create a new TabPanel
21052  * @param {Object} config The config object
21053  */
21054
21055 Roo.bootstrap.TabPanel = function(config){
21056     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21057     this.addEvents({
21058         /**
21059              * @event changed
21060              * Fires when the active status changes
21061              * @param {Roo.bootstrap.TabPanel} this
21062              * @param {Boolean} state the new state
21063             
21064          */
21065         'changed': true,
21066         /**
21067              * @event beforedeactivate
21068              * Fires before a tab is de-activated - can be used to do validation on a form.
21069              * @param {Roo.bootstrap.TabPanel} this
21070              * @return {Boolean} false if there is an error
21071             
21072          */
21073         'beforedeactivate': true
21074      });
21075     
21076     this.tabId = this.tabId || Roo.id();
21077   
21078 };
21079
21080 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21081     
21082     active: false,
21083     html: false,
21084     tabId: false,
21085     navId : false,
21086     href : '',
21087     touchSlide : false,
21088     getAutoCreate : function(){
21089         
21090         
21091         var cfg = {
21092             tag: 'div',
21093             // item is needed for carousel - not sure if it has any effect otherwise
21094             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21095             html: this.html || ''
21096         };
21097         
21098         if(this.active){
21099             cfg.cls += ' active';
21100         }
21101         
21102         if(this.tabId){
21103             cfg.tabId = this.tabId;
21104         }
21105         
21106         
21107         
21108         return cfg;
21109     },
21110     
21111     initEvents:  function()
21112     {
21113         var p = this.parent();
21114         
21115         this.navId = this.navId || p.navId;
21116         
21117         if (typeof(this.navId) != 'undefined') {
21118             // not really needed.. but just in case.. parent should be a NavGroup.
21119             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21120             
21121             tg.register(this);
21122             
21123             var i = tg.tabs.length - 1;
21124             
21125             if(this.active && tg.bullets > 0 && i < tg.bullets){
21126                 tg.setActiveBullet(i);
21127             }
21128         }
21129         
21130         this.el.on('click', this.onClick, this);
21131         
21132         if(Roo.isTouch && this.touchSlide){
21133             this.el.on("touchstart", this.onTouchStart, this);
21134             this.el.on("touchmove", this.onTouchMove, this);
21135             this.el.on("touchend", this.onTouchEnd, this);
21136         }
21137         
21138     },
21139     
21140     onRender : function(ct, position)
21141     {
21142         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21143     },
21144     
21145     setActive : function(state)
21146     {
21147         Roo.log("panel - set active " + this.tabId + "=" + state);
21148         
21149         this.active = state;
21150         if (!state) {
21151             this.el.removeClass('active');
21152             
21153         } else  if (!this.el.hasClass('active')) {
21154             this.el.addClass('active');
21155         }
21156         
21157         this.fireEvent('changed', this, state);
21158     },
21159     
21160     onClick : function(e)
21161     {
21162         e.preventDefault();
21163         
21164         if(!this.href.length){
21165             return;
21166         }
21167         
21168         window.location.href = this.href;
21169     },
21170     
21171     startX : 0,
21172     startY : 0,
21173     endX : 0,
21174     endY : 0,
21175     swiping : false,
21176     
21177     onTouchStart : function(e)
21178     {
21179         this.swiping = false;
21180         
21181         this.startX = e.browserEvent.touches[0].clientX;
21182         this.startY = e.browserEvent.touches[0].clientY;
21183     },
21184     
21185     onTouchMove : function(e)
21186     {
21187         this.swiping = true;
21188         
21189         this.endX = e.browserEvent.touches[0].clientX;
21190         this.endY = e.browserEvent.touches[0].clientY;
21191     },
21192     
21193     onTouchEnd : function(e)
21194     {
21195         if(!this.swiping){
21196             this.onClick(e);
21197             return;
21198         }
21199         
21200         var tabGroup = this.parent();
21201         
21202         if(this.endX > this.startX){ // swiping right
21203             tabGroup.showPanelPrev();
21204             return;
21205         }
21206         
21207         if(this.startX > this.endX){ // swiping left
21208             tabGroup.showPanelNext();
21209             return;
21210         }
21211     }
21212     
21213     
21214 });
21215  
21216
21217  
21218
21219  /*
21220  * - LGPL
21221  *
21222  * DateField
21223  * 
21224  */
21225
21226 /**
21227  * @class Roo.bootstrap.DateField
21228  * @extends Roo.bootstrap.Input
21229  * Bootstrap DateField class
21230  * @cfg {Number} weekStart default 0
21231  * @cfg {String} viewMode default empty, (months|years)
21232  * @cfg {String} minViewMode default empty, (months|years)
21233  * @cfg {Number} startDate default -Infinity
21234  * @cfg {Number} endDate default Infinity
21235  * @cfg {Boolean} todayHighlight default false
21236  * @cfg {Boolean} todayBtn default false
21237  * @cfg {Boolean} calendarWeeks default false
21238  * @cfg {Object} daysOfWeekDisabled default empty
21239  * @cfg {Boolean} singleMode default false (true | false)
21240  * 
21241  * @cfg {Boolean} keyboardNavigation default true
21242  * @cfg {String} language default en
21243  * 
21244  * @constructor
21245  * Create a new DateField
21246  * @param {Object} config The config object
21247  */
21248
21249 Roo.bootstrap.DateField = function(config){
21250     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21251      this.addEvents({
21252             /**
21253              * @event show
21254              * Fires when this field show.
21255              * @param {Roo.bootstrap.DateField} this
21256              * @param {Mixed} date The date value
21257              */
21258             show : true,
21259             /**
21260              * @event show
21261              * Fires when this field hide.
21262              * @param {Roo.bootstrap.DateField} this
21263              * @param {Mixed} date The date value
21264              */
21265             hide : true,
21266             /**
21267              * @event select
21268              * Fires when select a date.
21269              * @param {Roo.bootstrap.DateField} this
21270              * @param {Mixed} date The date value
21271              */
21272             select : true,
21273             /**
21274              * @event beforeselect
21275              * Fires when before select a date.
21276              * @param {Roo.bootstrap.DateField} this
21277              * @param {Mixed} date The date value
21278              */
21279             beforeselect : true
21280         });
21281 };
21282
21283 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21284     
21285     /**
21286      * @cfg {String} format
21287      * The default date format string which can be overriden for localization support.  The format must be
21288      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21289      */
21290     format : "m/d/y",
21291     /**
21292      * @cfg {String} altFormats
21293      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21294      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21295      */
21296     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21297     
21298     weekStart : 0,
21299     
21300     viewMode : '',
21301     
21302     minViewMode : '',
21303     
21304     todayHighlight : false,
21305     
21306     todayBtn: false,
21307     
21308     language: 'en',
21309     
21310     keyboardNavigation: true,
21311     
21312     calendarWeeks: false,
21313     
21314     startDate: -Infinity,
21315     
21316     endDate: Infinity,
21317     
21318     daysOfWeekDisabled: [],
21319     
21320     _events: [],
21321     
21322     singleMode : false,
21323     
21324     UTCDate: function()
21325     {
21326         return new Date(Date.UTC.apply(Date, arguments));
21327     },
21328     
21329     UTCToday: function()
21330     {
21331         var today = new Date();
21332         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21333     },
21334     
21335     getDate: function() {
21336             var d = this.getUTCDate();
21337             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21338     },
21339     
21340     getUTCDate: function() {
21341             return this.date;
21342     },
21343     
21344     setDate: function(d) {
21345             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21346     },
21347     
21348     setUTCDate: function(d) {
21349             this.date = d;
21350             this.setValue(this.formatDate(this.date));
21351     },
21352         
21353     onRender: function(ct, position)
21354     {
21355         
21356         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21357         
21358         this.language = this.language || 'en';
21359         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21360         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21361         
21362         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21363         this.format = this.format || 'm/d/y';
21364         this.isInline = false;
21365         this.isInput = true;
21366         this.component = this.el.select('.add-on', true).first() || false;
21367         this.component = (this.component && this.component.length === 0) ? false : this.component;
21368         this.hasInput = this.component && this.inputEl().length;
21369         
21370         if (typeof(this.minViewMode === 'string')) {
21371             switch (this.minViewMode) {
21372                 case 'months':
21373                     this.minViewMode = 1;
21374                     break;
21375                 case 'years':
21376                     this.minViewMode = 2;
21377                     break;
21378                 default:
21379                     this.minViewMode = 0;
21380                     break;
21381             }
21382         }
21383         
21384         if (typeof(this.viewMode === 'string')) {
21385             switch (this.viewMode) {
21386                 case 'months':
21387                     this.viewMode = 1;
21388                     break;
21389                 case 'years':
21390                     this.viewMode = 2;
21391                     break;
21392                 default:
21393                     this.viewMode = 0;
21394                     break;
21395             }
21396         }
21397                 
21398         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21399         
21400 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21401         
21402         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21403         
21404         this.picker().on('mousedown', this.onMousedown, this);
21405         this.picker().on('click', this.onClick, this);
21406         
21407         this.picker().addClass('datepicker-dropdown');
21408         
21409         this.startViewMode = this.viewMode;
21410         
21411         if(this.singleMode){
21412             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21413                 v.setVisibilityMode(Roo.Element.DISPLAY);
21414                 v.hide();
21415             });
21416             
21417             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21418                 v.setStyle('width', '189px');
21419             });
21420         }
21421         
21422         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21423             if(!this.calendarWeeks){
21424                 v.remove();
21425                 return;
21426             }
21427             
21428             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21429             v.attr('colspan', function(i, val){
21430                 return parseInt(val) + 1;
21431             });
21432         });
21433                         
21434         
21435         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21436         
21437         this.setStartDate(this.startDate);
21438         this.setEndDate(this.endDate);
21439         
21440         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21441         
21442         this.fillDow();
21443         this.fillMonths();
21444         this.update();
21445         this.showMode();
21446         
21447         if(this.isInline) {
21448             this.showPopup();
21449         }
21450     },
21451     
21452     picker : function()
21453     {
21454         return this.pickerEl;
21455 //        return this.el.select('.datepicker', true).first();
21456     },
21457     
21458     fillDow: function()
21459     {
21460         var dowCnt = this.weekStart;
21461         
21462         var dow = {
21463             tag: 'tr',
21464             cn: [
21465                 
21466             ]
21467         };
21468         
21469         if(this.calendarWeeks){
21470             dow.cn.push({
21471                 tag: 'th',
21472                 cls: 'cw',
21473                 html: '&nbsp;'
21474             })
21475         }
21476         
21477         while (dowCnt < this.weekStart + 7) {
21478             dow.cn.push({
21479                 tag: 'th',
21480                 cls: 'dow',
21481                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21482             });
21483         }
21484         
21485         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21486     },
21487     
21488     fillMonths: function()
21489     {    
21490         var i = 0;
21491         var months = this.picker().select('>.datepicker-months td', true).first();
21492         
21493         months.dom.innerHTML = '';
21494         
21495         while (i < 12) {
21496             var month = {
21497                 tag: 'span',
21498                 cls: 'month',
21499                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21500             };
21501             
21502             months.createChild(month);
21503         }
21504         
21505     },
21506     
21507     update: function()
21508     {
21509         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;
21510         
21511         if (this.date < this.startDate) {
21512             this.viewDate = new Date(this.startDate);
21513         } else if (this.date > this.endDate) {
21514             this.viewDate = new Date(this.endDate);
21515         } else {
21516             this.viewDate = new Date(this.date);
21517         }
21518         
21519         this.fill();
21520     },
21521     
21522     fill: function() 
21523     {
21524         var d = new Date(this.viewDate),
21525                 year = d.getUTCFullYear(),
21526                 month = d.getUTCMonth(),
21527                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21528                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21529                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21530                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21531                 currentDate = this.date && this.date.valueOf(),
21532                 today = this.UTCToday();
21533         
21534         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21535         
21536 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21537         
21538 //        this.picker.select('>tfoot th.today').
21539 //                                              .text(dates[this.language].today)
21540 //                                              .toggle(this.todayBtn !== false);
21541     
21542         this.updateNavArrows();
21543         this.fillMonths();
21544                                                 
21545         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21546         
21547         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21548          
21549         prevMonth.setUTCDate(day);
21550         
21551         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21552         
21553         var nextMonth = new Date(prevMonth);
21554         
21555         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21556         
21557         nextMonth = nextMonth.valueOf();
21558         
21559         var fillMonths = false;
21560         
21561         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21562         
21563         while(prevMonth.valueOf() <= nextMonth) {
21564             var clsName = '';
21565             
21566             if (prevMonth.getUTCDay() === this.weekStart) {
21567                 if(fillMonths){
21568                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21569                 }
21570                     
21571                 fillMonths = {
21572                     tag: 'tr',
21573                     cn: []
21574                 };
21575                 
21576                 if(this.calendarWeeks){
21577                     // ISO 8601: First week contains first thursday.
21578                     // ISO also states week starts on Monday, but we can be more abstract here.
21579                     var
21580                     // Start of current week: based on weekstart/current date
21581                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21582                     // Thursday of this week
21583                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21584                     // First Thursday of year, year from thursday
21585                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21586                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21587                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21588                     
21589                     fillMonths.cn.push({
21590                         tag: 'td',
21591                         cls: 'cw',
21592                         html: calWeek
21593                     });
21594                 }
21595             }
21596             
21597             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21598                 clsName += ' old';
21599             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21600                 clsName += ' new';
21601             }
21602             if (this.todayHighlight &&
21603                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21604                 prevMonth.getUTCMonth() == today.getMonth() &&
21605                 prevMonth.getUTCDate() == today.getDate()) {
21606                 clsName += ' today';
21607             }
21608             
21609             if (currentDate && prevMonth.valueOf() === currentDate) {
21610                 clsName += ' active';
21611             }
21612             
21613             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21614                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21615                     clsName += ' disabled';
21616             }
21617             
21618             fillMonths.cn.push({
21619                 tag: 'td',
21620                 cls: 'day ' + clsName,
21621                 html: prevMonth.getDate()
21622             });
21623             
21624             prevMonth.setDate(prevMonth.getDate()+1);
21625         }
21626           
21627         var currentYear = this.date && this.date.getUTCFullYear();
21628         var currentMonth = this.date && this.date.getUTCMonth();
21629         
21630         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21631         
21632         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21633             v.removeClass('active');
21634             
21635             if(currentYear === year && k === currentMonth){
21636                 v.addClass('active');
21637             }
21638             
21639             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21640                 v.addClass('disabled');
21641             }
21642             
21643         });
21644         
21645         
21646         year = parseInt(year/10, 10) * 10;
21647         
21648         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21649         
21650         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21651         
21652         year -= 1;
21653         for (var i = -1; i < 11; i++) {
21654             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21655                 tag: 'span',
21656                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21657                 html: year
21658             });
21659             
21660             year += 1;
21661         }
21662     },
21663     
21664     showMode: function(dir) 
21665     {
21666         if (dir) {
21667             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21668         }
21669         
21670         Roo.each(this.picker().select('>div',true).elements, function(v){
21671             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21672             v.hide();
21673         });
21674         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21675     },
21676     
21677     place: function()
21678     {
21679         if(this.isInline) {
21680             return;
21681         }
21682         
21683         this.picker().removeClass(['bottom', 'top']);
21684         
21685         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21686             /*
21687              * place to the top of element!
21688              *
21689              */
21690             
21691             this.picker().addClass('top');
21692             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21693             
21694             return;
21695         }
21696         
21697         this.picker().addClass('bottom');
21698         
21699         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21700     },
21701     
21702     parseDate : function(value)
21703     {
21704         if(!value || value instanceof Date){
21705             return value;
21706         }
21707         var v = Date.parseDate(value, this.format);
21708         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21709             v = Date.parseDate(value, 'Y-m-d');
21710         }
21711         if(!v && this.altFormats){
21712             if(!this.altFormatsArray){
21713                 this.altFormatsArray = this.altFormats.split("|");
21714             }
21715             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21716                 v = Date.parseDate(value, this.altFormatsArray[i]);
21717             }
21718         }
21719         return v;
21720     },
21721     
21722     formatDate : function(date, fmt)
21723     {   
21724         return (!date || !(date instanceof Date)) ?
21725         date : date.dateFormat(fmt || this.format);
21726     },
21727     
21728     onFocus : function()
21729     {
21730         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21731         this.showPopup();
21732     },
21733     
21734     onBlur : function()
21735     {
21736         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21737         
21738         var d = this.inputEl().getValue();
21739         
21740         this.setValue(d);
21741                 
21742         this.hidePopup();
21743     },
21744     
21745     showPopup : function()
21746     {
21747         this.picker().show();
21748         this.update();
21749         this.place();
21750         
21751         this.fireEvent('showpopup', this, this.date);
21752     },
21753     
21754     hidePopup : function()
21755     {
21756         if(this.isInline) {
21757             return;
21758         }
21759         this.picker().hide();
21760         this.viewMode = this.startViewMode;
21761         this.showMode();
21762         
21763         this.fireEvent('hidepopup', this, this.date);
21764         
21765     },
21766     
21767     onMousedown: function(e)
21768     {
21769         e.stopPropagation();
21770         e.preventDefault();
21771     },
21772     
21773     keyup: function(e)
21774     {
21775         Roo.bootstrap.DateField.superclass.keyup.call(this);
21776         this.update();
21777     },
21778
21779     setValue: function(v)
21780     {
21781         if(this.fireEvent('beforeselect', this, v) !== false){
21782             var d = new Date(this.parseDate(v) ).clearTime();
21783         
21784             if(isNaN(d.getTime())){
21785                 this.date = this.viewDate = '';
21786                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21787                 return;
21788             }
21789
21790             v = this.formatDate(d);
21791
21792             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21793
21794             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21795
21796             this.update();
21797
21798             this.fireEvent('select', this, this.date);
21799         }
21800     },
21801     
21802     getValue: function()
21803     {
21804         return this.formatDate(this.date);
21805     },
21806     
21807     fireKey: function(e)
21808     {
21809         if (!this.picker().isVisible()){
21810             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21811                 this.showPopup();
21812             }
21813             return;
21814         }
21815         
21816         var dateChanged = false,
21817         dir, day, month,
21818         newDate, newViewDate;
21819         
21820         switch(e.keyCode){
21821             case 27: // escape
21822                 this.hidePopup();
21823                 e.preventDefault();
21824                 break;
21825             case 37: // left
21826             case 39: // right
21827                 if (!this.keyboardNavigation) {
21828                     break;
21829                 }
21830                 dir = e.keyCode == 37 ? -1 : 1;
21831                 
21832                 if (e.ctrlKey){
21833                     newDate = this.moveYear(this.date, dir);
21834                     newViewDate = this.moveYear(this.viewDate, dir);
21835                 } else if (e.shiftKey){
21836                     newDate = this.moveMonth(this.date, dir);
21837                     newViewDate = this.moveMonth(this.viewDate, dir);
21838                 } else {
21839                     newDate = new Date(this.date);
21840                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21841                     newViewDate = new Date(this.viewDate);
21842                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21843                 }
21844                 if (this.dateWithinRange(newDate)){
21845                     this.date = newDate;
21846                     this.viewDate = newViewDate;
21847                     this.setValue(this.formatDate(this.date));
21848 //                    this.update();
21849                     e.preventDefault();
21850                     dateChanged = true;
21851                 }
21852                 break;
21853             case 38: // up
21854             case 40: // down
21855                 if (!this.keyboardNavigation) {
21856                     break;
21857                 }
21858                 dir = e.keyCode == 38 ? -1 : 1;
21859                 if (e.ctrlKey){
21860                     newDate = this.moveYear(this.date, dir);
21861                     newViewDate = this.moveYear(this.viewDate, dir);
21862                 } else if (e.shiftKey){
21863                     newDate = this.moveMonth(this.date, dir);
21864                     newViewDate = this.moveMonth(this.viewDate, dir);
21865                 } else {
21866                     newDate = new Date(this.date);
21867                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21868                     newViewDate = new Date(this.viewDate);
21869                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21870                 }
21871                 if (this.dateWithinRange(newDate)){
21872                     this.date = newDate;
21873                     this.viewDate = newViewDate;
21874                     this.setValue(this.formatDate(this.date));
21875 //                    this.update();
21876                     e.preventDefault();
21877                     dateChanged = true;
21878                 }
21879                 break;
21880             case 13: // enter
21881                 this.setValue(this.formatDate(this.date));
21882                 this.hidePopup();
21883                 e.preventDefault();
21884                 break;
21885             case 9: // tab
21886                 this.setValue(this.formatDate(this.date));
21887                 this.hidePopup();
21888                 break;
21889             case 16: // shift
21890             case 17: // ctrl
21891             case 18: // alt
21892                 break;
21893             default :
21894                 this.hidePopup();
21895                 
21896         }
21897     },
21898     
21899     
21900     onClick: function(e) 
21901     {
21902         e.stopPropagation();
21903         e.preventDefault();
21904         
21905         var target = e.getTarget();
21906         
21907         if(target.nodeName.toLowerCase() === 'i'){
21908             target = Roo.get(target).dom.parentNode;
21909         }
21910         
21911         var nodeName = target.nodeName;
21912         var className = target.className;
21913         var html = target.innerHTML;
21914         //Roo.log(nodeName);
21915         
21916         switch(nodeName.toLowerCase()) {
21917             case 'th':
21918                 switch(className) {
21919                     case 'switch':
21920                         this.showMode(1);
21921                         break;
21922                     case 'prev':
21923                     case 'next':
21924                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21925                         switch(this.viewMode){
21926                                 case 0:
21927                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21928                                         break;
21929                                 case 1:
21930                                 case 2:
21931                                         this.viewDate = this.moveYear(this.viewDate, dir);
21932                                         break;
21933                         }
21934                         this.fill();
21935                         break;
21936                     case 'today':
21937                         var date = new Date();
21938                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21939 //                        this.fill()
21940                         this.setValue(this.formatDate(this.date));
21941                         
21942                         this.hidePopup();
21943                         break;
21944                 }
21945                 break;
21946             case 'span':
21947                 if (className.indexOf('disabled') < 0) {
21948                     this.viewDate.setUTCDate(1);
21949                     if (className.indexOf('month') > -1) {
21950                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21951                     } else {
21952                         var year = parseInt(html, 10) || 0;
21953                         this.viewDate.setUTCFullYear(year);
21954                         
21955                     }
21956                     
21957                     if(this.singleMode){
21958                         this.setValue(this.formatDate(this.viewDate));
21959                         this.hidePopup();
21960                         return;
21961                     }
21962                     
21963                     this.showMode(-1);
21964                     this.fill();
21965                 }
21966                 break;
21967                 
21968             case 'td':
21969                 //Roo.log(className);
21970                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21971                     var day = parseInt(html, 10) || 1;
21972                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21973                         month = (this.viewDate || new Date()).getUTCMonth();
21974
21975                     if (className.indexOf('old') > -1) {
21976                         if(month === 0 ){
21977                             month = 11;
21978                             year -= 1;
21979                         }else{
21980                             month -= 1;
21981                         }
21982                     } else if (className.indexOf('new') > -1) {
21983                         if (month == 11) {
21984                             month = 0;
21985                             year += 1;
21986                         } else {
21987                             month += 1;
21988                         }
21989                     }
21990                     //Roo.log([year,month,day]);
21991                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21992                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21993 //                    this.fill();
21994                     //Roo.log(this.formatDate(this.date));
21995                     this.setValue(this.formatDate(this.date));
21996                     this.hidePopup();
21997                 }
21998                 break;
21999         }
22000     },
22001     
22002     setStartDate: function(startDate)
22003     {
22004         this.startDate = startDate || -Infinity;
22005         if (this.startDate !== -Infinity) {
22006             this.startDate = this.parseDate(this.startDate);
22007         }
22008         this.update();
22009         this.updateNavArrows();
22010     },
22011
22012     setEndDate: function(endDate)
22013     {
22014         this.endDate = endDate || Infinity;
22015         if (this.endDate !== Infinity) {
22016             this.endDate = this.parseDate(this.endDate);
22017         }
22018         this.update();
22019         this.updateNavArrows();
22020     },
22021     
22022     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22023     {
22024         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22025         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22026             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22027         }
22028         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22029             return parseInt(d, 10);
22030         });
22031         this.update();
22032         this.updateNavArrows();
22033     },
22034     
22035     updateNavArrows: function() 
22036     {
22037         if(this.singleMode){
22038             return;
22039         }
22040         
22041         var d = new Date(this.viewDate),
22042         year = d.getUTCFullYear(),
22043         month = d.getUTCMonth();
22044         
22045         Roo.each(this.picker().select('.prev', true).elements, function(v){
22046             v.show();
22047             switch (this.viewMode) {
22048                 case 0:
22049
22050                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22051                         v.hide();
22052                     }
22053                     break;
22054                 case 1:
22055                 case 2:
22056                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22057                         v.hide();
22058                     }
22059                     break;
22060             }
22061         });
22062         
22063         Roo.each(this.picker().select('.next', true).elements, function(v){
22064             v.show();
22065             switch (this.viewMode) {
22066                 case 0:
22067
22068                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22069                         v.hide();
22070                     }
22071                     break;
22072                 case 1:
22073                 case 2:
22074                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22075                         v.hide();
22076                     }
22077                     break;
22078             }
22079         })
22080     },
22081     
22082     moveMonth: function(date, dir)
22083     {
22084         if (!dir) {
22085             return date;
22086         }
22087         var new_date = new Date(date.valueOf()),
22088         day = new_date.getUTCDate(),
22089         month = new_date.getUTCMonth(),
22090         mag = Math.abs(dir),
22091         new_month, test;
22092         dir = dir > 0 ? 1 : -1;
22093         if (mag == 1){
22094             test = dir == -1
22095             // If going back one month, make sure month is not current month
22096             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22097             ? function(){
22098                 return new_date.getUTCMonth() == month;
22099             }
22100             // If going forward one month, make sure month is as expected
22101             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22102             : function(){
22103                 return new_date.getUTCMonth() != new_month;
22104             };
22105             new_month = month + dir;
22106             new_date.setUTCMonth(new_month);
22107             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22108             if (new_month < 0 || new_month > 11) {
22109                 new_month = (new_month + 12) % 12;
22110             }
22111         } else {
22112             // For magnitudes >1, move one month at a time...
22113             for (var i=0; i<mag; i++) {
22114                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22115                 new_date = this.moveMonth(new_date, dir);
22116             }
22117             // ...then reset the day, keeping it in the new month
22118             new_month = new_date.getUTCMonth();
22119             new_date.setUTCDate(day);
22120             test = function(){
22121                 return new_month != new_date.getUTCMonth();
22122             };
22123         }
22124         // Common date-resetting loop -- if date is beyond end of month, make it
22125         // end of month
22126         while (test()){
22127             new_date.setUTCDate(--day);
22128             new_date.setUTCMonth(new_month);
22129         }
22130         return new_date;
22131     },
22132
22133     moveYear: function(date, dir)
22134     {
22135         return this.moveMonth(date, dir*12);
22136     },
22137
22138     dateWithinRange: function(date)
22139     {
22140         return date >= this.startDate && date <= this.endDate;
22141     },
22142
22143     
22144     remove: function() 
22145     {
22146         this.picker().remove();
22147     },
22148     
22149     validateValue : function(value)
22150     {
22151         if(this.getVisibilityEl().hasClass('hidden')){
22152             return true;
22153         }
22154         
22155         if(value.length < 1)  {
22156             if(this.allowBlank){
22157                 return true;
22158             }
22159             return false;
22160         }
22161         
22162         if(value.length < this.minLength){
22163             return false;
22164         }
22165         if(value.length > this.maxLength){
22166             return false;
22167         }
22168         if(this.vtype){
22169             var vt = Roo.form.VTypes;
22170             if(!vt[this.vtype](value, this)){
22171                 return false;
22172             }
22173         }
22174         if(typeof this.validator == "function"){
22175             var msg = this.validator(value);
22176             if(msg !== true){
22177                 return false;
22178             }
22179         }
22180         
22181         if(this.regex && !this.regex.test(value)){
22182             return false;
22183         }
22184         
22185         if(typeof(this.parseDate(value)) == 'undefined'){
22186             return false;
22187         }
22188         
22189         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22190             return false;
22191         }      
22192         
22193         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22194             return false;
22195         } 
22196         
22197         
22198         return true;
22199     },
22200     
22201     reset : function()
22202     {
22203         this.date = this.viewDate = '';
22204         
22205         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22206     }
22207    
22208 });
22209
22210 Roo.apply(Roo.bootstrap.DateField,  {
22211     
22212     head : {
22213         tag: 'thead',
22214         cn: [
22215         {
22216             tag: 'tr',
22217             cn: [
22218             {
22219                 tag: 'th',
22220                 cls: 'prev',
22221                 html: '<i class="fa fa-arrow-left"/>'
22222             },
22223             {
22224                 tag: 'th',
22225                 cls: 'switch',
22226                 colspan: '5'
22227             },
22228             {
22229                 tag: 'th',
22230                 cls: 'next',
22231                 html: '<i class="fa fa-arrow-right"/>'
22232             }
22233
22234             ]
22235         }
22236         ]
22237     },
22238     
22239     content : {
22240         tag: 'tbody',
22241         cn: [
22242         {
22243             tag: 'tr',
22244             cn: [
22245             {
22246                 tag: 'td',
22247                 colspan: '7'
22248             }
22249             ]
22250         }
22251         ]
22252     },
22253     
22254     footer : {
22255         tag: 'tfoot',
22256         cn: [
22257         {
22258             tag: 'tr',
22259             cn: [
22260             {
22261                 tag: 'th',
22262                 colspan: '7',
22263                 cls: 'today'
22264             }
22265                     
22266             ]
22267         }
22268         ]
22269     },
22270     
22271     dates:{
22272         en: {
22273             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22274             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22275             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22276             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22277             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22278             today: "Today"
22279         }
22280     },
22281     
22282     modes: [
22283     {
22284         clsName: 'days',
22285         navFnc: 'Month',
22286         navStep: 1
22287     },
22288     {
22289         clsName: 'months',
22290         navFnc: 'FullYear',
22291         navStep: 1
22292     },
22293     {
22294         clsName: 'years',
22295         navFnc: 'FullYear',
22296         navStep: 10
22297     }]
22298 });
22299
22300 Roo.apply(Roo.bootstrap.DateField,  {
22301   
22302     template : {
22303         tag: 'div',
22304         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22305         cn: [
22306         {
22307             tag: 'div',
22308             cls: 'datepicker-days',
22309             cn: [
22310             {
22311                 tag: 'table',
22312                 cls: 'table-condensed',
22313                 cn:[
22314                 Roo.bootstrap.DateField.head,
22315                 {
22316                     tag: 'tbody'
22317                 },
22318                 Roo.bootstrap.DateField.footer
22319                 ]
22320             }
22321             ]
22322         },
22323         {
22324             tag: 'div',
22325             cls: 'datepicker-months',
22326             cn: [
22327             {
22328                 tag: 'table',
22329                 cls: 'table-condensed',
22330                 cn:[
22331                 Roo.bootstrap.DateField.head,
22332                 Roo.bootstrap.DateField.content,
22333                 Roo.bootstrap.DateField.footer
22334                 ]
22335             }
22336             ]
22337         },
22338         {
22339             tag: 'div',
22340             cls: 'datepicker-years',
22341             cn: [
22342             {
22343                 tag: 'table',
22344                 cls: 'table-condensed',
22345                 cn:[
22346                 Roo.bootstrap.DateField.head,
22347                 Roo.bootstrap.DateField.content,
22348                 Roo.bootstrap.DateField.footer
22349                 ]
22350             }
22351             ]
22352         }
22353         ]
22354     }
22355 });
22356
22357  
22358
22359  /*
22360  * - LGPL
22361  *
22362  * TimeField
22363  * 
22364  */
22365
22366 /**
22367  * @class Roo.bootstrap.TimeField
22368  * @extends Roo.bootstrap.Input
22369  * Bootstrap DateField class
22370  * 
22371  * 
22372  * @constructor
22373  * Create a new TimeField
22374  * @param {Object} config The config object
22375  */
22376
22377 Roo.bootstrap.TimeField = function(config){
22378     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22379     this.addEvents({
22380             /**
22381              * @event show
22382              * Fires when this field show.
22383              * @param {Roo.bootstrap.DateField} thisthis
22384              * @param {Mixed} date The date value
22385              */
22386             show : true,
22387             /**
22388              * @event show
22389              * Fires when this field hide.
22390              * @param {Roo.bootstrap.DateField} this
22391              * @param {Mixed} date The date value
22392              */
22393             hide : true,
22394             /**
22395              * @event select
22396              * Fires when select a date.
22397              * @param {Roo.bootstrap.DateField} this
22398              * @param {Mixed} date The date value
22399              */
22400             select : true
22401         });
22402 };
22403
22404 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22405     
22406     /**
22407      * @cfg {String} format
22408      * The default time format string which can be overriden for localization support.  The format must be
22409      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22410      */
22411     format : "H:i",
22412
22413     getAutoCreate : function()
22414     {
22415         this.after = '<i class="fa far fa-clock"></i>';
22416         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22417         
22418          
22419     },
22420     onRender: function(ct, position)
22421     {
22422         
22423         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22424                 
22425         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22426         
22427         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22428         
22429         this.pop = this.picker().select('>.datepicker-time',true).first();
22430         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22431         
22432         this.picker().on('mousedown', this.onMousedown, this);
22433         this.picker().on('click', this.onClick, this);
22434         
22435         this.picker().addClass('datepicker-dropdown');
22436     
22437         this.fillTime();
22438         this.update();
22439             
22440         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22441         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22442         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22443         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22444         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22445         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22446
22447     },
22448     
22449     fireKey: function(e){
22450         if (!this.picker().isVisible()){
22451             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22452                 this.show();
22453             }
22454             return;
22455         }
22456
22457         e.preventDefault();
22458         
22459         switch(e.keyCode){
22460             case 27: // escape
22461                 this.hide();
22462                 break;
22463             case 37: // left
22464             case 39: // right
22465                 this.onTogglePeriod();
22466                 break;
22467             case 38: // up
22468                 this.onIncrementMinutes();
22469                 break;
22470             case 40: // down
22471                 this.onDecrementMinutes();
22472                 break;
22473             case 13: // enter
22474             case 9: // tab
22475                 this.setTime();
22476                 break;
22477         }
22478     },
22479     
22480     onClick: function(e) {
22481         e.stopPropagation();
22482         e.preventDefault();
22483     },
22484     
22485     picker : function()
22486     {
22487         return this.pickerEl;
22488     },
22489     
22490     fillTime: function()
22491     {    
22492         var time = this.pop.select('tbody', true).first();
22493         
22494         time.dom.innerHTML = '';
22495         
22496         time.createChild({
22497             tag: 'tr',
22498             cn: [
22499                 {
22500                     tag: 'td',
22501                     cn: [
22502                         {
22503                             tag: 'a',
22504                             href: '#',
22505                             cls: 'btn',
22506                             cn: [
22507                                 {
22508                                     tag: 'i',
22509                                     cls: 'hours-up fa fas fa-chevron-up'
22510                                 }
22511                             ]
22512                         } 
22513                     ]
22514                 },
22515                 {
22516                     tag: 'td',
22517                     cls: 'separator'
22518                 },
22519                 {
22520                     tag: 'td',
22521                     cn: [
22522                         {
22523                             tag: 'a',
22524                             href: '#',
22525                             cls: 'btn',
22526                             cn: [
22527                                 {
22528                                     tag: 'i',
22529                                     cls: 'minutes-up fa fas fa-chevron-up'
22530                                 }
22531                             ]
22532                         }
22533                     ]
22534                 },
22535                 {
22536                     tag: 'td',
22537                     cls: 'separator'
22538                 }
22539             ]
22540         });
22541         
22542         time.createChild({
22543             tag: 'tr',
22544             cn: [
22545                 {
22546                     tag: 'td',
22547                     cn: [
22548                         {
22549                             tag: 'span',
22550                             cls: 'timepicker-hour',
22551                             html: '00'
22552                         }  
22553                     ]
22554                 },
22555                 {
22556                     tag: 'td',
22557                     cls: 'separator',
22558                     html: ':'
22559                 },
22560                 {
22561                     tag: 'td',
22562                     cn: [
22563                         {
22564                             tag: 'span',
22565                             cls: 'timepicker-minute',
22566                             html: '00'
22567                         }  
22568                     ]
22569                 },
22570                 {
22571                     tag: 'td',
22572                     cls: 'separator'
22573                 },
22574                 {
22575                     tag: 'td',
22576                     cn: [
22577                         {
22578                             tag: 'button',
22579                             type: 'button',
22580                             cls: 'btn btn-primary period',
22581                             html: 'AM'
22582                             
22583                         }
22584                     ]
22585                 }
22586             ]
22587         });
22588         
22589         time.createChild({
22590             tag: 'tr',
22591             cn: [
22592                 {
22593                     tag: 'td',
22594                     cn: [
22595                         {
22596                             tag: 'a',
22597                             href: '#',
22598                             cls: 'btn',
22599                             cn: [
22600                                 {
22601                                     tag: 'span',
22602                                     cls: 'hours-down fa fas fa-chevron-down'
22603                                 }
22604                             ]
22605                         }
22606                     ]
22607                 },
22608                 {
22609                     tag: 'td',
22610                     cls: 'separator'
22611                 },
22612                 {
22613                     tag: 'td',
22614                     cn: [
22615                         {
22616                             tag: 'a',
22617                             href: '#',
22618                             cls: 'btn',
22619                             cn: [
22620                                 {
22621                                     tag: 'span',
22622                                     cls: 'minutes-down fa fas fa-chevron-down'
22623                                 }
22624                             ]
22625                         }
22626                     ]
22627                 },
22628                 {
22629                     tag: 'td',
22630                     cls: 'separator'
22631                 }
22632             ]
22633         });
22634         
22635     },
22636     
22637     update: function()
22638     {
22639         
22640         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22641         
22642         this.fill();
22643     },
22644     
22645     fill: function() 
22646     {
22647         var hours = this.time.getHours();
22648         var minutes = this.time.getMinutes();
22649         var period = 'AM';
22650         
22651         if(hours > 11){
22652             period = 'PM';
22653         }
22654         
22655         if(hours == 0){
22656             hours = 12;
22657         }
22658         
22659         
22660         if(hours > 12){
22661             hours = hours - 12;
22662         }
22663         
22664         if(hours < 10){
22665             hours = '0' + hours;
22666         }
22667         
22668         if(minutes < 10){
22669             minutes = '0' + minutes;
22670         }
22671         
22672         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22673         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22674         this.pop.select('button', true).first().dom.innerHTML = period;
22675         
22676     },
22677     
22678     place: function()
22679     {   
22680         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22681         
22682         var cls = ['bottom'];
22683         
22684         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22685             cls.pop();
22686             cls.push('top');
22687         }
22688         
22689         cls.push('right');
22690         
22691         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22692             cls.pop();
22693             cls.push('left');
22694         }
22695         //this.picker().setXY(20000,20000);
22696         this.picker().addClass(cls.join('-'));
22697         
22698         var _this = this;
22699         
22700         Roo.each(cls, function(c){
22701             if(c == 'bottom'){
22702                 (function() {
22703                  //  
22704                 }).defer(200);
22705                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22706                 //_this.picker().setTop(_this.inputEl().getHeight());
22707                 return;
22708             }
22709             if(c == 'top'){
22710                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22711                 
22712                 //_this.picker().setTop(0 - _this.picker().getHeight());
22713                 return;
22714             }
22715             /*
22716             if(c == 'left'){
22717                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22718                 return;
22719             }
22720             if(c == 'right'){
22721                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22722                 return;
22723             }
22724             */
22725         });
22726         
22727     },
22728   
22729     onFocus : function()
22730     {
22731         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22732         this.show();
22733     },
22734     
22735     onBlur : function()
22736     {
22737         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22738         this.hide();
22739     },
22740     
22741     show : function()
22742     {
22743         this.picker().show();
22744         this.pop.show();
22745         this.update();
22746         this.place();
22747         
22748         this.fireEvent('show', this, this.date);
22749     },
22750     
22751     hide : function()
22752     {
22753         this.picker().hide();
22754         this.pop.hide();
22755         
22756         this.fireEvent('hide', this, this.date);
22757     },
22758     
22759     setTime : function()
22760     {
22761         this.hide();
22762         this.setValue(this.time.format(this.format));
22763         
22764         this.fireEvent('select', this, this.date);
22765         
22766         
22767     },
22768     
22769     onMousedown: function(e){
22770         e.stopPropagation();
22771         e.preventDefault();
22772     },
22773     
22774     onIncrementHours: function()
22775     {
22776         Roo.log('onIncrementHours');
22777         this.time = this.time.add(Date.HOUR, 1);
22778         this.update();
22779         
22780     },
22781     
22782     onDecrementHours: function()
22783     {
22784         Roo.log('onDecrementHours');
22785         this.time = this.time.add(Date.HOUR, -1);
22786         this.update();
22787     },
22788     
22789     onIncrementMinutes: function()
22790     {
22791         Roo.log('onIncrementMinutes');
22792         this.time = this.time.add(Date.MINUTE, 1);
22793         this.update();
22794     },
22795     
22796     onDecrementMinutes: function()
22797     {
22798         Roo.log('onDecrementMinutes');
22799         this.time = this.time.add(Date.MINUTE, -1);
22800         this.update();
22801     },
22802     
22803     onTogglePeriod: function()
22804     {
22805         Roo.log('onTogglePeriod');
22806         this.time = this.time.add(Date.HOUR, 12);
22807         this.update();
22808     }
22809     
22810    
22811 });
22812  
22813
22814 Roo.apply(Roo.bootstrap.TimeField,  {
22815   
22816     template : {
22817         tag: 'div',
22818         cls: 'datepicker dropdown-menu',
22819         cn: [
22820             {
22821                 tag: 'div',
22822                 cls: 'datepicker-time',
22823                 cn: [
22824                 {
22825                     tag: 'table',
22826                     cls: 'table-condensed',
22827                     cn:[
22828                         {
22829                             tag: 'tbody',
22830                             cn: [
22831                                 {
22832                                     tag: 'tr',
22833                                     cn: [
22834                                     {
22835                                         tag: 'td',
22836                                         colspan: '7'
22837                                     }
22838                                     ]
22839                                 }
22840                             ]
22841                         },
22842                         {
22843                             tag: 'tfoot',
22844                             cn: [
22845                                 {
22846                                     tag: 'tr',
22847                                     cn: [
22848                                     {
22849                                         tag: 'th',
22850                                         colspan: '7',
22851                                         cls: '',
22852                                         cn: [
22853                                             {
22854                                                 tag: 'button',
22855                                                 cls: 'btn btn-info ok',
22856                                                 html: 'OK'
22857                                             }
22858                                         ]
22859                                     }
22860                     
22861                                     ]
22862                                 }
22863                             ]
22864                         }
22865                     ]
22866                 }
22867                 ]
22868             }
22869         ]
22870     }
22871 });
22872
22873  
22874
22875  /*
22876  * - LGPL
22877  *
22878  * MonthField
22879  * 
22880  */
22881
22882 /**
22883  * @class Roo.bootstrap.MonthField
22884  * @extends Roo.bootstrap.Input
22885  * Bootstrap MonthField class
22886  * 
22887  * @cfg {String} language default en
22888  * 
22889  * @constructor
22890  * Create a new MonthField
22891  * @param {Object} config The config object
22892  */
22893
22894 Roo.bootstrap.MonthField = function(config){
22895     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22896     
22897     this.addEvents({
22898         /**
22899          * @event show
22900          * Fires when this field show.
22901          * @param {Roo.bootstrap.MonthField} this
22902          * @param {Mixed} date The date value
22903          */
22904         show : true,
22905         /**
22906          * @event show
22907          * Fires when this field hide.
22908          * @param {Roo.bootstrap.MonthField} this
22909          * @param {Mixed} date The date value
22910          */
22911         hide : true,
22912         /**
22913          * @event select
22914          * Fires when select a date.
22915          * @param {Roo.bootstrap.MonthField} this
22916          * @param {String} oldvalue The old value
22917          * @param {String} newvalue The new value
22918          */
22919         select : true
22920     });
22921 };
22922
22923 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22924     
22925     onRender: function(ct, position)
22926     {
22927         
22928         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22929         
22930         this.language = this.language || 'en';
22931         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22932         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22933         
22934         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22935         this.isInline = false;
22936         this.isInput = true;
22937         this.component = this.el.select('.add-on', true).first() || false;
22938         this.component = (this.component && this.component.length === 0) ? false : this.component;
22939         this.hasInput = this.component && this.inputEL().length;
22940         
22941         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22942         
22943         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22944         
22945         this.picker().on('mousedown', this.onMousedown, this);
22946         this.picker().on('click', this.onClick, this);
22947         
22948         this.picker().addClass('datepicker-dropdown');
22949         
22950         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22951             v.setStyle('width', '189px');
22952         });
22953         
22954         this.fillMonths();
22955         
22956         this.update();
22957         
22958         if(this.isInline) {
22959             this.show();
22960         }
22961         
22962     },
22963     
22964     setValue: function(v, suppressEvent)
22965     {   
22966         var o = this.getValue();
22967         
22968         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22969         
22970         this.update();
22971
22972         if(suppressEvent !== true){
22973             this.fireEvent('select', this, o, v);
22974         }
22975         
22976     },
22977     
22978     getValue: function()
22979     {
22980         return this.value;
22981     },
22982     
22983     onClick: function(e) 
22984     {
22985         e.stopPropagation();
22986         e.preventDefault();
22987         
22988         var target = e.getTarget();
22989         
22990         if(target.nodeName.toLowerCase() === 'i'){
22991             target = Roo.get(target).dom.parentNode;
22992         }
22993         
22994         var nodeName = target.nodeName;
22995         var className = target.className;
22996         var html = target.innerHTML;
22997         
22998         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22999             return;
23000         }
23001         
23002         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23003         
23004         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23005         
23006         this.hide();
23007                         
23008     },
23009     
23010     picker : function()
23011     {
23012         return this.pickerEl;
23013     },
23014     
23015     fillMonths: function()
23016     {    
23017         var i = 0;
23018         var months = this.picker().select('>.datepicker-months td', true).first();
23019         
23020         months.dom.innerHTML = '';
23021         
23022         while (i < 12) {
23023             var month = {
23024                 tag: 'span',
23025                 cls: 'month',
23026                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23027             };
23028             
23029             months.createChild(month);
23030         }
23031         
23032     },
23033     
23034     update: function()
23035     {
23036         var _this = this;
23037         
23038         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23039             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23040         }
23041         
23042         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23043             e.removeClass('active');
23044             
23045             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23046                 e.addClass('active');
23047             }
23048         })
23049     },
23050     
23051     place: function()
23052     {
23053         if(this.isInline) {
23054             return;
23055         }
23056         
23057         this.picker().removeClass(['bottom', 'top']);
23058         
23059         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23060             /*
23061              * place to the top of element!
23062              *
23063              */
23064             
23065             this.picker().addClass('top');
23066             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23067             
23068             return;
23069         }
23070         
23071         this.picker().addClass('bottom');
23072         
23073         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23074     },
23075     
23076     onFocus : function()
23077     {
23078         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23079         this.show();
23080     },
23081     
23082     onBlur : function()
23083     {
23084         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23085         
23086         var d = this.inputEl().getValue();
23087         
23088         this.setValue(d);
23089                 
23090         this.hide();
23091     },
23092     
23093     show : function()
23094     {
23095         this.picker().show();
23096         this.picker().select('>.datepicker-months', true).first().show();
23097         this.update();
23098         this.place();
23099         
23100         this.fireEvent('show', this, this.date);
23101     },
23102     
23103     hide : function()
23104     {
23105         if(this.isInline) {
23106             return;
23107         }
23108         this.picker().hide();
23109         this.fireEvent('hide', this, this.date);
23110         
23111     },
23112     
23113     onMousedown: function(e)
23114     {
23115         e.stopPropagation();
23116         e.preventDefault();
23117     },
23118     
23119     keyup: function(e)
23120     {
23121         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23122         this.update();
23123     },
23124
23125     fireKey: function(e)
23126     {
23127         if (!this.picker().isVisible()){
23128             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23129                 this.show();
23130             }
23131             return;
23132         }
23133         
23134         var dir;
23135         
23136         switch(e.keyCode){
23137             case 27: // escape
23138                 this.hide();
23139                 e.preventDefault();
23140                 break;
23141             case 37: // left
23142             case 39: // right
23143                 dir = e.keyCode == 37 ? -1 : 1;
23144                 
23145                 this.vIndex = this.vIndex + dir;
23146                 
23147                 if(this.vIndex < 0){
23148                     this.vIndex = 0;
23149                 }
23150                 
23151                 if(this.vIndex > 11){
23152                     this.vIndex = 11;
23153                 }
23154                 
23155                 if(isNaN(this.vIndex)){
23156                     this.vIndex = 0;
23157                 }
23158                 
23159                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23160                 
23161                 break;
23162             case 38: // up
23163             case 40: // down
23164                 
23165                 dir = e.keyCode == 38 ? -1 : 1;
23166                 
23167                 this.vIndex = this.vIndex + dir * 4;
23168                 
23169                 if(this.vIndex < 0){
23170                     this.vIndex = 0;
23171                 }
23172                 
23173                 if(this.vIndex > 11){
23174                     this.vIndex = 11;
23175                 }
23176                 
23177                 if(isNaN(this.vIndex)){
23178                     this.vIndex = 0;
23179                 }
23180                 
23181                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23182                 break;
23183                 
23184             case 13: // enter
23185                 
23186                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23187                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23188                 }
23189                 
23190                 this.hide();
23191                 e.preventDefault();
23192                 break;
23193             case 9: // tab
23194                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23195                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23196                 }
23197                 this.hide();
23198                 break;
23199             case 16: // shift
23200             case 17: // ctrl
23201             case 18: // alt
23202                 break;
23203             default :
23204                 this.hide();
23205                 
23206         }
23207     },
23208     
23209     remove: function() 
23210     {
23211         this.picker().remove();
23212     }
23213    
23214 });
23215
23216 Roo.apply(Roo.bootstrap.MonthField,  {
23217     
23218     content : {
23219         tag: 'tbody',
23220         cn: [
23221         {
23222             tag: 'tr',
23223             cn: [
23224             {
23225                 tag: 'td',
23226                 colspan: '7'
23227             }
23228             ]
23229         }
23230         ]
23231     },
23232     
23233     dates:{
23234         en: {
23235             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23236             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23237         }
23238     }
23239 });
23240
23241 Roo.apply(Roo.bootstrap.MonthField,  {
23242   
23243     template : {
23244         tag: 'div',
23245         cls: 'datepicker dropdown-menu roo-dynamic',
23246         cn: [
23247             {
23248                 tag: 'div',
23249                 cls: 'datepicker-months',
23250                 cn: [
23251                 {
23252                     tag: 'table',
23253                     cls: 'table-condensed',
23254                     cn:[
23255                         Roo.bootstrap.DateField.content
23256                     ]
23257                 }
23258                 ]
23259             }
23260         ]
23261     }
23262 });
23263
23264  
23265
23266  
23267  /*
23268  * - LGPL
23269  *
23270  * CheckBox
23271  * 
23272  */
23273
23274 /**
23275  * @class Roo.bootstrap.CheckBox
23276  * @extends Roo.bootstrap.Input
23277  * Bootstrap CheckBox class
23278  * 
23279  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23280  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23281  * @cfg {String} boxLabel The text that appears beside the checkbox
23282  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23283  * @cfg {Boolean} checked initnal the element
23284  * @cfg {Boolean} inline inline the element (default false)
23285  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23286  * @cfg {String} tooltip label tooltip
23287  * 
23288  * @constructor
23289  * Create a new CheckBox
23290  * @param {Object} config The config object
23291  */
23292
23293 Roo.bootstrap.CheckBox = function(config){
23294     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23295    
23296     this.addEvents({
23297         /**
23298         * @event check
23299         * Fires when the element is checked or unchecked.
23300         * @param {Roo.bootstrap.CheckBox} this This input
23301         * @param {Boolean} checked The new checked value
23302         */
23303        check : true,
23304        /**
23305         * @event click
23306         * Fires when the element is click.
23307         * @param {Roo.bootstrap.CheckBox} this This input
23308         */
23309        click : true
23310     });
23311     
23312 };
23313
23314 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23315   
23316     inputType: 'checkbox',
23317     inputValue: 1,
23318     valueOff: 0,
23319     boxLabel: false,
23320     checked: false,
23321     weight : false,
23322     inline: false,
23323     tooltip : '',
23324     
23325     // checkbox success does not make any sense really.. 
23326     invalidClass : "",
23327     validClass : "",
23328     
23329     
23330     getAutoCreate : function()
23331     {
23332         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23333         
23334         var id = Roo.id();
23335         
23336         var cfg = {};
23337         
23338         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23339         
23340         if(this.inline){
23341             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23342         }
23343         
23344         var input =  {
23345             tag: 'input',
23346             id : id,
23347             type : this.inputType,
23348             value : this.inputValue,
23349             cls : 'roo-' + this.inputType, //'form-box',
23350             placeholder : this.placeholder || ''
23351             
23352         };
23353         
23354         if(this.inputType != 'radio'){
23355             var hidden =  {
23356                 tag: 'input',
23357                 type : 'hidden',
23358                 cls : 'roo-hidden-value',
23359                 value : this.checked ? this.inputValue : this.valueOff
23360             };
23361         }
23362         
23363             
23364         if (this.weight) { // Validity check?
23365             cfg.cls += " " + this.inputType + "-" + this.weight;
23366         }
23367         
23368         if (this.disabled) {
23369             input.disabled=true;
23370         }
23371         
23372         if(this.checked){
23373             input.checked = this.checked;
23374         }
23375         
23376         if (this.name) {
23377             
23378             input.name = this.name;
23379             
23380             if(this.inputType != 'radio'){
23381                 hidden.name = this.name;
23382                 input.name = '_hidden_' + this.name;
23383             }
23384         }
23385         
23386         if (this.size) {
23387             input.cls += ' input-' + this.size;
23388         }
23389         
23390         var settings=this;
23391         
23392         ['xs','sm','md','lg'].map(function(size){
23393             if (settings[size]) {
23394                 cfg.cls += ' col-' + size + '-' + settings[size];
23395             }
23396         });
23397         
23398         var inputblock = input;
23399          
23400         if (this.before || this.after) {
23401             
23402             inputblock = {
23403                 cls : 'input-group',
23404                 cn :  [] 
23405             };
23406             
23407             if (this.before) {
23408                 inputblock.cn.push({
23409                     tag :'span',
23410                     cls : 'input-group-addon',
23411                     html : this.before
23412                 });
23413             }
23414             
23415             inputblock.cn.push(input);
23416             
23417             if(this.inputType != 'radio'){
23418                 inputblock.cn.push(hidden);
23419             }
23420             
23421             if (this.after) {
23422                 inputblock.cn.push({
23423                     tag :'span',
23424                     cls : 'input-group-addon',
23425                     html : this.after
23426                 });
23427             }
23428             
23429         }
23430         var boxLabelCfg = false;
23431         
23432         if(this.boxLabel){
23433            
23434             boxLabelCfg = {
23435                 tag: 'label',
23436                 //'for': id, // box label is handled by onclick - so no for...
23437                 cls: 'box-label',
23438                 html: this.boxLabel
23439             };
23440             if(this.tooltip){
23441                 boxLabelCfg.tooltip = this.tooltip;
23442             }
23443              
23444         }
23445         
23446         
23447         if (align ==='left' && this.fieldLabel.length) {
23448 //                Roo.log("left and has label");
23449             cfg.cn = [
23450                 {
23451                     tag: 'label',
23452                     'for' :  id,
23453                     cls : 'control-label',
23454                     html : this.fieldLabel
23455                 },
23456                 {
23457                     cls : "", 
23458                     cn: [
23459                         inputblock
23460                     ]
23461                 }
23462             ];
23463             
23464             if (boxLabelCfg) {
23465                 cfg.cn[1].cn.push(boxLabelCfg);
23466             }
23467             
23468             if(this.labelWidth > 12){
23469                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23470             }
23471             
23472             if(this.labelWidth < 13 && this.labelmd == 0){
23473                 this.labelmd = this.labelWidth;
23474             }
23475             
23476             if(this.labellg > 0){
23477                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23478                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23479             }
23480             
23481             if(this.labelmd > 0){
23482                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23483                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23484             }
23485             
23486             if(this.labelsm > 0){
23487                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23488                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23489             }
23490             
23491             if(this.labelxs > 0){
23492                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23493                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23494             }
23495             
23496         } else if ( this.fieldLabel.length) {
23497 //                Roo.log(" label");
23498                 cfg.cn = [
23499                    
23500                     {
23501                         tag: this.boxLabel ? 'span' : 'label',
23502                         'for': id,
23503                         cls: 'control-label box-input-label',
23504                         //cls : 'input-group-addon',
23505                         html : this.fieldLabel
23506                     },
23507                     
23508                     inputblock
23509                     
23510                 ];
23511                 if (boxLabelCfg) {
23512                     cfg.cn.push(boxLabelCfg);
23513                 }
23514
23515         } else {
23516             
23517 //                Roo.log(" no label && no align");
23518                 cfg.cn = [  inputblock ] ;
23519                 if (boxLabelCfg) {
23520                     cfg.cn.push(boxLabelCfg);
23521                 }
23522
23523                 
23524         }
23525         
23526        
23527         
23528         if(this.inputType != 'radio'){
23529             cfg.cn.push(hidden);
23530         }
23531         
23532         return cfg;
23533         
23534     },
23535     
23536     /**
23537      * return the real input element.
23538      */
23539     inputEl: function ()
23540     {
23541         return this.el.select('input.roo-' + this.inputType,true).first();
23542     },
23543     hiddenEl: function ()
23544     {
23545         return this.el.select('input.roo-hidden-value',true).first();
23546     },
23547     
23548     labelEl: function()
23549     {
23550         return this.el.select('label.control-label',true).first();
23551     },
23552     /* depricated... */
23553     
23554     label: function()
23555     {
23556         return this.labelEl();
23557     },
23558     
23559     boxLabelEl: function()
23560     {
23561         return this.el.select('label.box-label',true).first();
23562     },
23563     
23564     initEvents : function()
23565     {
23566 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23567         
23568         this.inputEl().on('click', this.onClick,  this);
23569         
23570         if (this.boxLabel) { 
23571             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23572         }
23573         
23574         this.startValue = this.getValue();
23575         
23576         if(this.groupId){
23577             Roo.bootstrap.CheckBox.register(this);
23578         }
23579     },
23580     
23581     onClick : function(e)
23582     {   
23583         if(this.fireEvent('click', this, e) !== false){
23584             this.setChecked(!this.checked);
23585         }
23586         
23587     },
23588     
23589     setChecked : function(state,suppressEvent)
23590     {
23591         this.startValue = this.getValue();
23592
23593         if(this.inputType == 'radio'){
23594             
23595             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23596                 e.dom.checked = false;
23597             });
23598             
23599             this.inputEl().dom.checked = true;
23600             
23601             this.inputEl().dom.value = this.inputValue;
23602             
23603             if(suppressEvent !== true){
23604                 this.fireEvent('check', this, true);
23605             }
23606             
23607             this.validate();
23608             
23609             return;
23610         }
23611         
23612         this.checked = state;
23613         
23614         this.inputEl().dom.checked = state;
23615         
23616         
23617         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23618         
23619         if(suppressEvent !== true){
23620             this.fireEvent('check', this, state);
23621         }
23622         
23623         this.validate();
23624     },
23625     
23626     getValue : function()
23627     {
23628         if(this.inputType == 'radio'){
23629             return this.getGroupValue();
23630         }
23631         
23632         return this.hiddenEl().dom.value;
23633         
23634     },
23635     
23636     getGroupValue : function()
23637     {
23638         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23639             return '';
23640         }
23641         
23642         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23643     },
23644     
23645     setValue : function(v,suppressEvent)
23646     {
23647         if(this.inputType == 'radio'){
23648             this.setGroupValue(v, suppressEvent);
23649             return;
23650         }
23651         
23652         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23653         
23654         this.validate();
23655     },
23656     
23657     setGroupValue : function(v, suppressEvent)
23658     {
23659         this.startValue = this.getValue();
23660         
23661         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23662             e.dom.checked = false;
23663             
23664             if(e.dom.value == v){
23665                 e.dom.checked = true;
23666             }
23667         });
23668         
23669         if(suppressEvent !== true){
23670             this.fireEvent('check', this, true);
23671         }
23672
23673         this.validate();
23674         
23675         return;
23676     },
23677     
23678     validate : function()
23679     {
23680         if(this.getVisibilityEl().hasClass('hidden')){
23681             return true;
23682         }
23683         
23684         if(
23685                 this.disabled || 
23686                 (this.inputType == 'radio' && this.validateRadio()) ||
23687                 (this.inputType == 'checkbox' && this.validateCheckbox())
23688         ){
23689             this.markValid();
23690             return true;
23691         }
23692         
23693         this.markInvalid();
23694         return false;
23695     },
23696     
23697     validateRadio : function()
23698     {
23699         if(this.getVisibilityEl().hasClass('hidden')){
23700             return true;
23701         }
23702         
23703         if(this.allowBlank){
23704             return true;
23705         }
23706         
23707         var valid = false;
23708         
23709         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23710             if(!e.dom.checked){
23711                 return;
23712             }
23713             
23714             valid = true;
23715             
23716             return false;
23717         });
23718         
23719         return valid;
23720     },
23721     
23722     validateCheckbox : function()
23723     {
23724         if(!this.groupId){
23725             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23726             //return (this.getValue() == this.inputValue) ? true : false;
23727         }
23728         
23729         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23730         
23731         if(!group){
23732             return false;
23733         }
23734         
23735         var r = false;
23736         
23737         for(var i in group){
23738             if(group[i].el.isVisible(true)){
23739                 r = false;
23740                 break;
23741             }
23742             
23743             r = true;
23744         }
23745         
23746         for(var i in group){
23747             if(r){
23748                 break;
23749             }
23750             
23751             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23752         }
23753         
23754         return r;
23755     },
23756     
23757     /**
23758      * Mark this field as valid
23759      */
23760     markValid : function()
23761     {
23762         var _this = this;
23763         
23764         this.fireEvent('valid', this);
23765         
23766         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23767         
23768         if(this.groupId){
23769             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23770         }
23771         
23772         if(label){
23773             label.markValid();
23774         }
23775
23776         if(this.inputType == 'radio'){
23777             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23778                 var fg = e.findParent('.form-group', false, true);
23779                 if (Roo.bootstrap.version == 3) {
23780                     fg.removeClass([_this.invalidClass, _this.validClass]);
23781                     fg.addClass(_this.validClass);
23782                 } else {
23783                     fg.removeClass(['is-valid', 'is-invalid']);
23784                     fg.addClass('is-valid');
23785                 }
23786             });
23787             
23788             return;
23789         }
23790
23791         if(!this.groupId){
23792             var fg = this.el.findParent('.form-group', false, true);
23793             if (Roo.bootstrap.version == 3) {
23794                 fg.removeClass([this.invalidClass, this.validClass]);
23795                 fg.addClass(this.validClass);
23796             } else {
23797                 fg.removeClass(['is-valid', 'is-invalid']);
23798                 fg.addClass('is-valid');
23799             }
23800             return;
23801         }
23802         
23803         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23804         
23805         if(!group){
23806             return;
23807         }
23808         
23809         for(var i in group){
23810             var fg = group[i].el.findParent('.form-group', false, true);
23811             if (Roo.bootstrap.version == 3) {
23812                 fg.removeClass([this.invalidClass, this.validClass]);
23813                 fg.addClass(this.validClass);
23814             } else {
23815                 fg.removeClass(['is-valid', 'is-invalid']);
23816                 fg.addClass('is-valid');
23817             }
23818         }
23819     },
23820     
23821      /**
23822      * Mark this field as invalid
23823      * @param {String} msg The validation message
23824      */
23825     markInvalid : function(msg)
23826     {
23827         if(this.allowBlank){
23828             return;
23829         }
23830         
23831         var _this = this;
23832         
23833         this.fireEvent('invalid', this, msg);
23834         
23835         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23836         
23837         if(this.groupId){
23838             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23839         }
23840         
23841         if(label){
23842             label.markInvalid();
23843         }
23844             
23845         if(this.inputType == 'radio'){
23846             
23847             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23848                 var fg = e.findParent('.form-group', false, true);
23849                 if (Roo.bootstrap.version == 3) {
23850                     fg.removeClass([_this.invalidClass, _this.validClass]);
23851                     fg.addClass(_this.invalidClass);
23852                 } else {
23853                     fg.removeClass(['is-invalid', 'is-valid']);
23854                     fg.addClass('is-invalid');
23855                 }
23856             });
23857             
23858             return;
23859         }
23860         
23861         if(!this.groupId){
23862             var fg = this.el.findParent('.form-group', false, true);
23863             if (Roo.bootstrap.version == 3) {
23864                 fg.removeClass([_this.invalidClass, _this.validClass]);
23865                 fg.addClass(_this.invalidClass);
23866             } else {
23867                 fg.removeClass(['is-invalid', 'is-valid']);
23868                 fg.addClass('is-invalid');
23869             }
23870             return;
23871         }
23872         
23873         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23874         
23875         if(!group){
23876             return;
23877         }
23878         
23879         for(var i in group){
23880             var fg = group[i].el.findParent('.form-group', false, true);
23881             if (Roo.bootstrap.version == 3) {
23882                 fg.removeClass([_this.invalidClass, _this.validClass]);
23883                 fg.addClass(_this.invalidClass);
23884             } else {
23885                 fg.removeClass(['is-invalid', 'is-valid']);
23886                 fg.addClass('is-invalid');
23887             }
23888         }
23889         
23890     },
23891     
23892     clearInvalid : function()
23893     {
23894         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23895         
23896         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23897         
23898         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23899         
23900         if (label && label.iconEl) {
23901             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23902             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23903         }
23904     },
23905     
23906     disable : function()
23907     {
23908         if(this.inputType != 'radio'){
23909             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23910             return;
23911         }
23912         
23913         var _this = this;
23914         
23915         if(this.rendered){
23916             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23917                 _this.getActionEl().addClass(this.disabledClass);
23918                 e.dom.disabled = true;
23919             });
23920         }
23921         
23922         this.disabled = true;
23923         this.fireEvent("disable", this);
23924         return this;
23925     },
23926
23927     enable : function()
23928     {
23929         if(this.inputType != 'radio'){
23930             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23931             return;
23932         }
23933         
23934         var _this = this;
23935         
23936         if(this.rendered){
23937             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23938                 _this.getActionEl().removeClass(this.disabledClass);
23939                 e.dom.disabled = false;
23940             });
23941         }
23942         
23943         this.disabled = false;
23944         this.fireEvent("enable", this);
23945         return this;
23946     },
23947     
23948     setBoxLabel : function(v)
23949     {
23950         this.boxLabel = v;
23951         
23952         if(this.rendered){
23953             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23954         }
23955     }
23956
23957 });
23958
23959 Roo.apply(Roo.bootstrap.CheckBox, {
23960     
23961     groups: {},
23962     
23963      /**
23964     * register a CheckBox Group
23965     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23966     */
23967     register : function(checkbox)
23968     {
23969         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23970             this.groups[checkbox.groupId] = {};
23971         }
23972         
23973         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23974             return;
23975         }
23976         
23977         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23978         
23979     },
23980     /**
23981     * fetch a CheckBox Group based on the group ID
23982     * @param {string} the group ID
23983     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23984     */
23985     get: function(groupId) {
23986         if (typeof(this.groups[groupId]) == 'undefined') {
23987             return false;
23988         }
23989         
23990         return this.groups[groupId] ;
23991     }
23992     
23993     
23994 });
23995 /*
23996  * - LGPL
23997  *
23998  * RadioItem
23999  * 
24000  */
24001
24002 /**
24003  * @class Roo.bootstrap.Radio
24004  * @extends Roo.bootstrap.Component
24005  * Bootstrap Radio class
24006  * @cfg {String} boxLabel - the label associated
24007  * @cfg {String} value - the value of radio
24008  * 
24009  * @constructor
24010  * Create a new Radio
24011  * @param {Object} config The config object
24012  */
24013 Roo.bootstrap.Radio = function(config){
24014     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24015     
24016 };
24017
24018 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24019     
24020     boxLabel : '',
24021     
24022     value : '',
24023     
24024     getAutoCreate : function()
24025     {
24026         var cfg = {
24027             tag : 'div',
24028             cls : 'form-group radio',
24029             cn : [
24030                 {
24031                     tag : 'label',
24032                     cls : 'box-label',
24033                     html : this.boxLabel
24034                 }
24035             ]
24036         };
24037         
24038         return cfg;
24039     },
24040     
24041     initEvents : function() 
24042     {
24043         this.parent().register(this);
24044         
24045         this.el.on('click', this.onClick, this);
24046         
24047     },
24048     
24049     onClick : function(e)
24050     {
24051         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24052             this.setChecked(true);
24053         }
24054     },
24055     
24056     setChecked : function(state, suppressEvent)
24057     {
24058         this.parent().setValue(this.value, suppressEvent);
24059         
24060     },
24061     
24062     setBoxLabel : function(v)
24063     {
24064         this.boxLabel = v;
24065         
24066         if(this.rendered){
24067             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24068         }
24069     }
24070     
24071 });
24072  
24073
24074  /*
24075  * - LGPL
24076  *
24077  * Input
24078  * 
24079  */
24080
24081 /**
24082  * @class Roo.bootstrap.SecurePass
24083  * @extends Roo.bootstrap.Input
24084  * Bootstrap SecurePass class
24085  *
24086  * 
24087  * @constructor
24088  * Create a new SecurePass
24089  * @param {Object} config The config object
24090  */
24091  
24092 Roo.bootstrap.SecurePass = function (config) {
24093     // these go here, so the translation tool can replace them..
24094     this.errors = {
24095         PwdEmpty: "Please type a password, and then retype it to confirm.",
24096         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24097         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24098         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24099         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24100         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24101         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24102         TooWeak: "Your password is Too Weak."
24103     },
24104     this.meterLabel = "Password strength:";
24105     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24106     this.meterClass = [
24107         "roo-password-meter-tooweak", 
24108         "roo-password-meter-weak", 
24109         "roo-password-meter-medium", 
24110         "roo-password-meter-strong", 
24111         "roo-password-meter-grey"
24112     ];
24113     
24114     this.errors = {};
24115     
24116     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24117 }
24118
24119 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24120     /**
24121      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24122      * {
24123      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24124      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24125      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24126      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24127      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24128      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24129      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24130      * })
24131      */
24132     // private
24133     
24134     meterWidth: 300,
24135     errorMsg :'',    
24136     errors: false,
24137     imageRoot: '/',
24138     /**
24139      * @cfg {String/Object} Label for the strength meter (defaults to
24140      * 'Password strength:')
24141      */
24142     // private
24143     meterLabel: '',
24144     /**
24145      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24146      * ['Weak', 'Medium', 'Strong'])
24147      */
24148     // private    
24149     pwdStrengths: false,    
24150     // private
24151     strength: 0,
24152     // private
24153     _lastPwd: null,
24154     // private
24155     kCapitalLetter: 0,
24156     kSmallLetter: 1,
24157     kDigit: 2,
24158     kPunctuation: 3,
24159     
24160     insecure: false,
24161     // private
24162     initEvents: function ()
24163     {
24164         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24165
24166         if (this.el.is('input[type=password]') && Roo.isSafari) {
24167             this.el.on('keydown', this.SafariOnKeyDown, this);
24168         }
24169
24170         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24171     },
24172     // private
24173     onRender: function (ct, position)
24174     {
24175         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24176         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24177         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24178
24179         this.trigger.createChild({
24180                    cn: [
24181                     {
24182                     //id: 'PwdMeter',
24183                     tag: 'div',
24184                     cls: 'roo-password-meter-grey col-xs-12',
24185                     style: {
24186                         //width: 0,
24187                         //width: this.meterWidth + 'px'                                                
24188                         }
24189                     },
24190                     {                            
24191                          cls: 'roo-password-meter-text'                          
24192                     }
24193                 ]            
24194         });
24195
24196          
24197         if (this.hideTrigger) {
24198             this.trigger.setDisplayed(false);
24199         }
24200         this.setSize(this.width || '', this.height || '');
24201     },
24202     // private
24203     onDestroy: function ()
24204     {
24205         if (this.trigger) {
24206             this.trigger.removeAllListeners();
24207             this.trigger.remove();
24208         }
24209         if (this.wrap) {
24210             this.wrap.remove();
24211         }
24212         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24213     },
24214     // private
24215     checkStrength: function ()
24216     {
24217         var pwd = this.inputEl().getValue();
24218         if (pwd == this._lastPwd) {
24219             return;
24220         }
24221
24222         var strength;
24223         if (this.ClientSideStrongPassword(pwd)) {
24224             strength = 3;
24225         } else if (this.ClientSideMediumPassword(pwd)) {
24226             strength = 2;
24227         } else if (this.ClientSideWeakPassword(pwd)) {
24228             strength = 1;
24229         } else {
24230             strength = 0;
24231         }
24232         
24233         Roo.log('strength1: ' + strength);
24234         
24235         //var pm = this.trigger.child('div/div/div').dom;
24236         var pm = this.trigger.child('div/div');
24237         pm.removeClass(this.meterClass);
24238         pm.addClass(this.meterClass[strength]);
24239                 
24240         
24241         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24242                 
24243         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24244         
24245         this._lastPwd = pwd;
24246     },
24247     reset: function ()
24248     {
24249         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24250         
24251         this._lastPwd = '';
24252         
24253         var pm = this.trigger.child('div/div');
24254         pm.removeClass(this.meterClass);
24255         pm.addClass('roo-password-meter-grey');        
24256         
24257         
24258         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24259         
24260         pt.innerHTML = '';
24261         this.inputEl().dom.type='password';
24262     },
24263     // private
24264     validateValue: function (value)
24265     {
24266         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24267             return false;
24268         }
24269         if (value.length == 0) {
24270             if (this.allowBlank) {
24271                 this.clearInvalid();
24272                 return true;
24273             }
24274
24275             this.markInvalid(this.errors.PwdEmpty);
24276             this.errorMsg = this.errors.PwdEmpty;
24277             return false;
24278         }
24279         
24280         if(this.insecure){
24281             return true;
24282         }
24283         
24284         if (!value.match(/[\x21-\x7e]+/)) {
24285             this.markInvalid(this.errors.PwdBadChar);
24286             this.errorMsg = this.errors.PwdBadChar;
24287             return false;
24288         }
24289         if (value.length < 6) {
24290             this.markInvalid(this.errors.PwdShort);
24291             this.errorMsg = this.errors.PwdShort;
24292             return false;
24293         }
24294         if (value.length > 16) {
24295             this.markInvalid(this.errors.PwdLong);
24296             this.errorMsg = this.errors.PwdLong;
24297             return false;
24298         }
24299         var strength;
24300         if (this.ClientSideStrongPassword(value)) {
24301             strength = 3;
24302         } else if (this.ClientSideMediumPassword(value)) {
24303             strength = 2;
24304         } else if (this.ClientSideWeakPassword(value)) {
24305             strength = 1;
24306         } else {
24307             strength = 0;
24308         }
24309
24310         
24311         if (strength < 2) {
24312             //this.markInvalid(this.errors.TooWeak);
24313             this.errorMsg = this.errors.TooWeak;
24314             //return false;
24315         }
24316         
24317         
24318         console.log('strength2: ' + strength);
24319         
24320         //var pm = this.trigger.child('div/div/div').dom;
24321         
24322         var pm = this.trigger.child('div/div');
24323         pm.removeClass(this.meterClass);
24324         pm.addClass(this.meterClass[strength]);
24325                 
24326         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24327                 
24328         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24329         
24330         this.errorMsg = ''; 
24331         return true;
24332     },
24333     // private
24334     CharacterSetChecks: function (type)
24335     {
24336         this.type = type;
24337         this.fResult = false;
24338     },
24339     // private
24340     isctype: function (character, type)
24341     {
24342         switch (type) {  
24343             case this.kCapitalLetter:
24344                 if (character >= 'A' && character <= 'Z') {
24345                     return true;
24346                 }
24347                 break;
24348             
24349             case this.kSmallLetter:
24350                 if (character >= 'a' && character <= 'z') {
24351                     return true;
24352                 }
24353                 break;
24354             
24355             case this.kDigit:
24356                 if (character >= '0' && character <= '9') {
24357                     return true;
24358                 }
24359                 break;
24360             
24361             case this.kPunctuation:
24362                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24363                     return true;
24364                 }
24365                 break;
24366             
24367             default:
24368                 return false;
24369         }
24370
24371     },
24372     // private
24373     IsLongEnough: function (pwd, size)
24374     {
24375         return !(pwd == null || isNaN(size) || pwd.length < size);
24376     },
24377     // private
24378     SpansEnoughCharacterSets: function (word, nb)
24379     {
24380         if (!this.IsLongEnough(word, nb))
24381         {
24382             return false;
24383         }
24384
24385         var characterSetChecks = new Array(
24386             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24387             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24388         );
24389         
24390         for (var index = 0; index < word.length; ++index) {
24391             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24392                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24393                     characterSetChecks[nCharSet].fResult = true;
24394                     break;
24395                 }
24396             }
24397         }
24398
24399         var nCharSets = 0;
24400         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24401             if (characterSetChecks[nCharSet].fResult) {
24402                 ++nCharSets;
24403             }
24404         }
24405
24406         if (nCharSets < nb) {
24407             return false;
24408         }
24409         return true;
24410     },
24411     // private
24412     ClientSideStrongPassword: function (pwd)
24413     {
24414         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24415     },
24416     // private
24417     ClientSideMediumPassword: function (pwd)
24418     {
24419         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24420     },
24421     // private
24422     ClientSideWeakPassword: function (pwd)
24423     {
24424         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24425     }
24426           
24427 })//<script type="text/javascript">
24428
24429 /*
24430  * Based  Ext JS Library 1.1.1
24431  * Copyright(c) 2006-2007, Ext JS, LLC.
24432  * LGPL
24433  *
24434  */
24435  
24436 /**
24437  * @class Roo.HtmlEditorCore
24438  * @extends Roo.Component
24439  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24440  *
24441  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24442  */
24443
24444 Roo.HtmlEditorCore = function(config){
24445     
24446     
24447     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24448     
24449     
24450     this.addEvents({
24451         /**
24452          * @event initialize
24453          * Fires when the editor is fully initialized (including the iframe)
24454          * @param {Roo.HtmlEditorCore} this
24455          */
24456         initialize: true,
24457         /**
24458          * @event activate
24459          * Fires when the editor is first receives the focus. Any insertion must wait
24460          * until after this event.
24461          * @param {Roo.HtmlEditorCore} this
24462          */
24463         activate: true,
24464          /**
24465          * @event beforesync
24466          * Fires before the textarea is updated with content from the editor iframe. Return false
24467          * to cancel the sync.
24468          * @param {Roo.HtmlEditorCore} this
24469          * @param {String} html
24470          */
24471         beforesync: true,
24472          /**
24473          * @event beforepush
24474          * Fires before the iframe editor is updated with content from the textarea. Return false
24475          * to cancel the push.
24476          * @param {Roo.HtmlEditorCore} this
24477          * @param {String} html
24478          */
24479         beforepush: true,
24480          /**
24481          * @event sync
24482          * Fires when the textarea is updated with content from the editor iframe.
24483          * @param {Roo.HtmlEditorCore} this
24484          * @param {String} html
24485          */
24486         sync: true,
24487          /**
24488          * @event push
24489          * Fires when the iframe editor is updated with content from the textarea.
24490          * @param {Roo.HtmlEditorCore} this
24491          * @param {String} html
24492          */
24493         push: true,
24494         
24495         /**
24496          * @event editorevent
24497          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24498          * @param {Roo.HtmlEditorCore} this
24499          */
24500         editorevent: true
24501         
24502     });
24503     
24504     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24505     
24506     // defaults : white / black...
24507     this.applyBlacklists();
24508     
24509     
24510     
24511 };
24512
24513
24514 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24515
24516
24517      /**
24518      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24519      */
24520     
24521     owner : false,
24522     
24523      /**
24524      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24525      *                        Roo.resizable.
24526      */
24527     resizable : false,
24528      /**
24529      * @cfg {Number} height (in pixels)
24530      */   
24531     height: 300,
24532    /**
24533      * @cfg {Number} width (in pixels)
24534      */   
24535     width: 500,
24536     
24537     /**
24538      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24539      * 
24540      */
24541     stylesheets: false,
24542     
24543     // id of frame..
24544     frameId: false,
24545     
24546     // private properties
24547     validationEvent : false,
24548     deferHeight: true,
24549     initialized : false,
24550     activated : false,
24551     sourceEditMode : false,
24552     onFocus : Roo.emptyFn,
24553     iframePad:3,
24554     hideMode:'offsets',
24555     
24556     clearUp: true,
24557     
24558     // blacklist + whitelisted elements..
24559     black: false,
24560     white: false,
24561      
24562     bodyCls : '',
24563
24564     /**
24565      * Protected method that will not generally be called directly. It
24566      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24567      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24568      */
24569     getDocMarkup : function(){
24570         // body styles..
24571         var st = '';
24572         
24573         // inherit styels from page...?? 
24574         if (this.stylesheets === false) {
24575             
24576             Roo.get(document.head).select('style').each(function(node) {
24577                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24578             });
24579             
24580             Roo.get(document.head).select('link').each(function(node) { 
24581                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24582             });
24583             
24584         } else if (!this.stylesheets.length) {
24585                 // simple..
24586                 st = '<style type="text/css">' +
24587                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24588                    '</style>';
24589         } else {
24590             for (var i in this.stylesheets) { 
24591                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24592             }
24593             
24594         }
24595         
24596         st +=  '<style type="text/css">' +
24597             'IMG { cursor: pointer } ' +
24598         '</style>';
24599
24600         var cls = 'roo-htmleditor-body';
24601         
24602         if(this.bodyCls.length){
24603             cls += ' ' + this.bodyCls;
24604         }
24605         
24606         return '<html><head>' + st  +
24607             //<style type="text/css">' +
24608             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24609             //'</style>' +
24610             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24611     },
24612
24613     // private
24614     onRender : function(ct, position)
24615     {
24616         var _t = this;
24617         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24618         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24619         
24620         
24621         this.el.dom.style.border = '0 none';
24622         this.el.dom.setAttribute('tabIndex', -1);
24623         this.el.addClass('x-hidden hide');
24624         
24625         
24626         
24627         if(Roo.isIE){ // fix IE 1px bogus margin
24628             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24629         }
24630        
24631         
24632         this.frameId = Roo.id();
24633         
24634          
24635         
24636         var iframe = this.owner.wrap.createChild({
24637             tag: 'iframe',
24638             cls: 'form-control', // bootstrap..
24639             id: this.frameId,
24640             name: this.frameId,
24641             frameBorder : 'no',
24642             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24643         }, this.el
24644         );
24645         
24646         
24647         this.iframe = iframe.dom;
24648
24649          this.assignDocWin();
24650         
24651         this.doc.designMode = 'on';
24652        
24653         this.doc.open();
24654         this.doc.write(this.getDocMarkup());
24655         this.doc.close();
24656
24657         
24658         var task = { // must defer to wait for browser to be ready
24659             run : function(){
24660                 //console.log("run task?" + this.doc.readyState);
24661                 this.assignDocWin();
24662                 if(this.doc.body || this.doc.readyState == 'complete'){
24663                     try {
24664                         this.doc.designMode="on";
24665                     } catch (e) {
24666                         return;
24667                     }
24668                     Roo.TaskMgr.stop(task);
24669                     this.initEditor.defer(10, this);
24670                 }
24671             },
24672             interval : 10,
24673             duration: 10000,
24674             scope: this
24675         };
24676         Roo.TaskMgr.start(task);
24677
24678     },
24679
24680     // private
24681     onResize : function(w, h)
24682     {
24683          Roo.log('resize: ' +w + ',' + h );
24684         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24685         if(!this.iframe){
24686             return;
24687         }
24688         if(typeof w == 'number'){
24689             
24690             this.iframe.style.width = w + 'px';
24691         }
24692         if(typeof h == 'number'){
24693             
24694             this.iframe.style.height = h + 'px';
24695             if(this.doc){
24696                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24697             }
24698         }
24699         
24700     },
24701
24702     /**
24703      * Toggles the editor between standard and source edit mode.
24704      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24705      */
24706     toggleSourceEdit : function(sourceEditMode){
24707         
24708         this.sourceEditMode = sourceEditMode === true;
24709         
24710         if(this.sourceEditMode){
24711  
24712             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24713             
24714         }else{
24715             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24716             //this.iframe.className = '';
24717             this.deferFocus();
24718         }
24719         //this.setSize(this.owner.wrap.getSize());
24720         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24721     },
24722
24723     
24724   
24725
24726     /**
24727      * Protected method that will not generally be called directly. If you need/want
24728      * custom HTML cleanup, this is the method you should override.
24729      * @param {String} html The HTML to be cleaned
24730      * return {String} The cleaned HTML
24731      */
24732     cleanHtml : function(html){
24733         html = String(html);
24734         if(html.length > 5){
24735             if(Roo.isSafari){ // strip safari nonsense
24736                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24737             }
24738         }
24739         if(html == '&nbsp;'){
24740             html = '';
24741         }
24742         return html;
24743     },
24744
24745     /**
24746      * HTML Editor -> Textarea
24747      * Protected method that will not generally be called directly. Syncs the contents
24748      * of the editor iframe with the textarea.
24749      */
24750     syncValue : function(){
24751         if(this.initialized){
24752             var bd = (this.doc.body || this.doc.documentElement);
24753             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24754             var html = bd.innerHTML;
24755             if(Roo.isSafari){
24756                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24757                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24758                 if(m && m[1]){
24759                     html = '<div style="'+m[0]+'">' + html + '</div>';
24760                 }
24761             }
24762             html = this.cleanHtml(html);
24763             // fix up the special chars.. normaly like back quotes in word...
24764             // however we do not want to do this with chinese..
24765             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24766                 
24767                 var cc = match.charCodeAt();
24768
24769                 // Get the character value, handling surrogate pairs
24770                 if (match.length == 2) {
24771                     // It's a surrogate pair, calculate the Unicode code point
24772                     var high = match.charCodeAt(0) - 0xD800;
24773                     var low  = match.charCodeAt(1) - 0xDC00;
24774                     cc = (high * 0x400) + low + 0x10000;
24775                 }  else if (
24776                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24777                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24778                     (cc >= 0xf900 && cc < 0xfb00 )
24779                 ) {
24780                         return match;
24781                 }  
24782          
24783                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24784                 return "&#" + cc + ";";
24785                 
24786                 
24787             });
24788             
24789             
24790              
24791             if(this.owner.fireEvent('beforesync', this, html) !== false){
24792                 this.el.dom.value = html;
24793                 this.owner.fireEvent('sync', this, html);
24794             }
24795         }
24796     },
24797
24798     /**
24799      * Protected method that will not generally be called directly. Pushes the value of the textarea
24800      * into the iframe editor.
24801      */
24802     pushValue : function(){
24803         if(this.initialized){
24804             var v = this.el.dom.value.trim();
24805             
24806 //            if(v.length < 1){
24807 //                v = '&#160;';
24808 //            }
24809             
24810             if(this.owner.fireEvent('beforepush', this, v) !== false){
24811                 var d = (this.doc.body || this.doc.documentElement);
24812                 d.innerHTML = v;
24813                 this.cleanUpPaste();
24814                 this.el.dom.value = d.innerHTML;
24815                 this.owner.fireEvent('push', this, v);
24816             }
24817         }
24818     },
24819
24820     // private
24821     deferFocus : function(){
24822         this.focus.defer(10, this);
24823     },
24824
24825     // doc'ed in Field
24826     focus : function(){
24827         if(this.win && !this.sourceEditMode){
24828             this.win.focus();
24829         }else{
24830             this.el.focus();
24831         }
24832     },
24833     
24834     assignDocWin: function()
24835     {
24836         var iframe = this.iframe;
24837         
24838          if(Roo.isIE){
24839             this.doc = iframe.contentWindow.document;
24840             this.win = iframe.contentWindow;
24841         } else {
24842 //            if (!Roo.get(this.frameId)) {
24843 //                return;
24844 //            }
24845 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24846 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24847             
24848             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24849                 return;
24850             }
24851             
24852             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24853             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24854         }
24855     },
24856     
24857     // private
24858     initEditor : function(){
24859         //console.log("INIT EDITOR");
24860         this.assignDocWin();
24861         
24862         
24863         
24864         this.doc.designMode="on";
24865         this.doc.open();
24866         this.doc.write(this.getDocMarkup());
24867         this.doc.close();
24868         
24869         var dbody = (this.doc.body || this.doc.documentElement);
24870         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24871         // this copies styles from the containing element into thsi one..
24872         // not sure why we need all of this..
24873         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24874         
24875         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24876         //ss['background-attachment'] = 'fixed'; // w3c
24877         dbody.bgProperties = 'fixed'; // ie
24878         //Roo.DomHelper.applyStyles(dbody, ss);
24879         Roo.EventManager.on(this.doc, {
24880             //'mousedown': this.onEditorEvent,
24881             'mouseup': this.onEditorEvent,
24882             'dblclick': this.onEditorEvent,
24883             'click': this.onEditorEvent,
24884             'keyup': this.onEditorEvent,
24885             buffer:100,
24886             scope: this
24887         });
24888         if(Roo.isGecko){
24889             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24890         }
24891         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24892             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24893         }
24894         this.initialized = true;
24895
24896         this.owner.fireEvent('initialize', this);
24897         this.pushValue();
24898     },
24899
24900     // private
24901     onDestroy : function(){
24902         
24903         
24904         
24905         if(this.rendered){
24906             
24907             //for (var i =0; i < this.toolbars.length;i++) {
24908             //    // fixme - ask toolbars for heights?
24909             //    this.toolbars[i].onDestroy();
24910            // }
24911             
24912             //this.wrap.dom.innerHTML = '';
24913             //this.wrap.remove();
24914         }
24915     },
24916
24917     // private
24918     onFirstFocus : function(){
24919         
24920         this.assignDocWin();
24921         
24922         
24923         this.activated = true;
24924          
24925     
24926         if(Roo.isGecko){ // prevent silly gecko errors
24927             this.win.focus();
24928             var s = this.win.getSelection();
24929             if(!s.focusNode || s.focusNode.nodeType != 3){
24930                 var r = s.getRangeAt(0);
24931                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24932                 r.collapse(true);
24933                 this.deferFocus();
24934             }
24935             try{
24936                 this.execCmd('useCSS', true);
24937                 this.execCmd('styleWithCSS', false);
24938             }catch(e){}
24939         }
24940         this.owner.fireEvent('activate', this);
24941     },
24942
24943     // private
24944     adjustFont: function(btn){
24945         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24946         //if(Roo.isSafari){ // safari
24947         //    adjust *= 2;
24948        // }
24949         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24950         if(Roo.isSafari){ // safari
24951             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24952             v =  (v < 10) ? 10 : v;
24953             v =  (v > 48) ? 48 : v;
24954             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24955             
24956         }
24957         
24958         
24959         v = Math.max(1, v+adjust);
24960         
24961         this.execCmd('FontSize', v  );
24962     },
24963
24964     onEditorEvent : function(e)
24965     {
24966         this.owner.fireEvent('editorevent', this, e);
24967       //  this.updateToolbar();
24968         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24969     },
24970
24971     insertTag : function(tg)
24972     {
24973         // could be a bit smarter... -> wrap the current selected tRoo..
24974         if (tg.toLowerCase() == 'span' ||
24975             tg.toLowerCase() == 'code' ||
24976             tg.toLowerCase() == 'sup' ||
24977             tg.toLowerCase() == 'sub' 
24978             ) {
24979             
24980             range = this.createRange(this.getSelection());
24981             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24982             wrappingNode.appendChild(range.extractContents());
24983             range.insertNode(wrappingNode);
24984
24985             return;
24986             
24987             
24988             
24989         }
24990         this.execCmd("formatblock",   tg);
24991         
24992     },
24993     
24994     insertText : function(txt)
24995     {
24996         
24997         
24998         var range = this.createRange();
24999         range.deleteContents();
25000                //alert(Sender.getAttribute('label'));
25001                
25002         range.insertNode(this.doc.createTextNode(txt));
25003     } ,
25004     
25005      
25006
25007     /**
25008      * Executes a Midas editor command on the editor document and performs necessary focus and
25009      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25010      * @param {String} cmd The Midas command
25011      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25012      */
25013     relayCmd : function(cmd, value){
25014         this.win.focus();
25015         this.execCmd(cmd, value);
25016         this.owner.fireEvent('editorevent', this);
25017         //this.updateToolbar();
25018         this.owner.deferFocus();
25019     },
25020
25021     /**
25022      * Executes a Midas editor command directly on the editor document.
25023      * For visual commands, you should use {@link #relayCmd} instead.
25024      * <b>This should only be called after the editor is initialized.</b>
25025      * @param {String} cmd The Midas command
25026      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25027      */
25028     execCmd : function(cmd, value){
25029         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25030         this.syncValue();
25031     },
25032  
25033  
25034    
25035     /**
25036      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25037      * to insert tRoo.
25038      * @param {String} text | dom node.. 
25039      */
25040     insertAtCursor : function(text)
25041     {
25042         
25043         if(!this.activated){
25044             return;
25045         }
25046         /*
25047         if(Roo.isIE){
25048             this.win.focus();
25049             var r = this.doc.selection.createRange();
25050             if(r){
25051                 r.collapse(true);
25052                 r.pasteHTML(text);
25053                 this.syncValue();
25054                 this.deferFocus();
25055             
25056             }
25057             return;
25058         }
25059         */
25060         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25061             this.win.focus();
25062             
25063             
25064             // from jquery ui (MIT licenced)
25065             var range, node;
25066             var win = this.win;
25067             
25068             if (win.getSelection && win.getSelection().getRangeAt) {
25069                 range = win.getSelection().getRangeAt(0);
25070                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25071                 range.insertNode(node);
25072             } else if (win.document.selection && win.document.selection.createRange) {
25073                 // no firefox support
25074                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25075                 win.document.selection.createRange().pasteHTML(txt);
25076             } else {
25077                 // no firefox support
25078                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25079                 this.execCmd('InsertHTML', txt);
25080             } 
25081             
25082             this.syncValue();
25083             
25084             this.deferFocus();
25085         }
25086     },
25087  // private
25088     mozKeyPress : function(e){
25089         if(e.ctrlKey){
25090             var c = e.getCharCode(), cmd;
25091           
25092             if(c > 0){
25093                 c = String.fromCharCode(c).toLowerCase();
25094                 switch(c){
25095                     case 'b':
25096                         cmd = 'bold';
25097                         break;
25098                     case 'i':
25099                         cmd = 'italic';
25100                         break;
25101                     
25102                     case 'u':
25103                         cmd = 'underline';
25104                         break;
25105                     
25106                     case 'v':
25107                         this.cleanUpPaste.defer(100, this);
25108                         return;
25109                         
25110                 }
25111                 if(cmd){
25112                     this.win.focus();
25113                     this.execCmd(cmd);
25114                     this.deferFocus();
25115                     e.preventDefault();
25116                 }
25117                 
25118             }
25119         }
25120     },
25121
25122     // private
25123     fixKeys : function(){ // load time branching for fastest keydown performance
25124         if(Roo.isIE){
25125             return function(e){
25126                 var k = e.getKey(), r;
25127                 if(k == e.TAB){
25128                     e.stopEvent();
25129                     r = this.doc.selection.createRange();
25130                     if(r){
25131                         r.collapse(true);
25132                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25133                         this.deferFocus();
25134                     }
25135                     return;
25136                 }
25137                 
25138                 if(k == e.ENTER){
25139                     r = this.doc.selection.createRange();
25140                     if(r){
25141                         var target = r.parentElement();
25142                         if(!target || target.tagName.toLowerCase() != 'li'){
25143                             e.stopEvent();
25144                             r.pasteHTML('<br />');
25145                             r.collapse(false);
25146                             r.select();
25147                         }
25148                     }
25149                 }
25150                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25151                     this.cleanUpPaste.defer(100, this);
25152                     return;
25153                 }
25154                 
25155                 
25156             };
25157         }else if(Roo.isOpera){
25158             return function(e){
25159                 var k = e.getKey();
25160                 if(k == e.TAB){
25161                     e.stopEvent();
25162                     this.win.focus();
25163                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25164                     this.deferFocus();
25165                 }
25166                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25167                     this.cleanUpPaste.defer(100, this);
25168                     return;
25169                 }
25170                 
25171             };
25172         }else if(Roo.isSafari){
25173             return function(e){
25174                 var k = e.getKey();
25175                 
25176                 if(k == e.TAB){
25177                     e.stopEvent();
25178                     this.execCmd('InsertText','\t');
25179                     this.deferFocus();
25180                     return;
25181                 }
25182                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25183                     this.cleanUpPaste.defer(100, this);
25184                     return;
25185                 }
25186                 
25187              };
25188         }
25189     }(),
25190     
25191     getAllAncestors: function()
25192     {
25193         var p = this.getSelectedNode();
25194         var a = [];
25195         if (!p) {
25196             a.push(p); // push blank onto stack..
25197             p = this.getParentElement();
25198         }
25199         
25200         
25201         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25202             a.push(p);
25203             p = p.parentNode;
25204         }
25205         a.push(this.doc.body);
25206         return a;
25207     },
25208     lastSel : false,
25209     lastSelNode : false,
25210     
25211     
25212     getSelection : function() 
25213     {
25214         this.assignDocWin();
25215         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25216     },
25217     
25218     getSelectedNode: function() 
25219     {
25220         // this may only work on Gecko!!!
25221         
25222         // should we cache this!!!!
25223         
25224         
25225         
25226          
25227         var range = this.createRange(this.getSelection()).cloneRange();
25228         
25229         if (Roo.isIE) {
25230             var parent = range.parentElement();
25231             while (true) {
25232                 var testRange = range.duplicate();
25233                 testRange.moveToElementText(parent);
25234                 if (testRange.inRange(range)) {
25235                     break;
25236                 }
25237                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25238                     break;
25239                 }
25240                 parent = parent.parentElement;
25241             }
25242             return parent;
25243         }
25244         
25245         // is ancestor a text element.
25246         var ac =  range.commonAncestorContainer;
25247         if (ac.nodeType == 3) {
25248             ac = ac.parentNode;
25249         }
25250         
25251         var ar = ac.childNodes;
25252          
25253         var nodes = [];
25254         var other_nodes = [];
25255         var has_other_nodes = false;
25256         for (var i=0;i<ar.length;i++) {
25257             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25258                 continue;
25259             }
25260             // fullly contained node.
25261             
25262             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25263                 nodes.push(ar[i]);
25264                 continue;
25265             }
25266             
25267             // probably selected..
25268             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25269                 other_nodes.push(ar[i]);
25270                 continue;
25271             }
25272             // outer..
25273             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25274                 continue;
25275             }
25276             
25277             
25278             has_other_nodes = true;
25279         }
25280         if (!nodes.length && other_nodes.length) {
25281             nodes= other_nodes;
25282         }
25283         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25284             return false;
25285         }
25286         
25287         return nodes[0];
25288     },
25289     createRange: function(sel)
25290     {
25291         // this has strange effects when using with 
25292         // top toolbar - not sure if it's a great idea.
25293         //this.editor.contentWindow.focus();
25294         if (typeof sel != "undefined") {
25295             try {
25296                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25297             } catch(e) {
25298                 return this.doc.createRange();
25299             }
25300         } else {
25301             return this.doc.createRange();
25302         }
25303     },
25304     getParentElement: function()
25305     {
25306         
25307         this.assignDocWin();
25308         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25309         
25310         var range = this.createRange(sel);
25311          
25312         try {
25313             var p = range.commonAncestorContainer;
25314             while (p.nodeType == 3) { // text node
25315                 p = p.parentNode;
25316             }
25317             return p;
25318         } catch (e) {
25319             return null;
25320         }
25321     
25322     },
25323     /***
25324      *
25325      * Range intersection.. the hard stuff...
25326      *  '-1' = before
25327      *  '0' = hits..
25328      *  '1' = after.
25329      *         [ -- selected range --- ]
25330      *   [fail]                        [fail]
25331      *
25332      *    basically..
25333      *      if end is before start or  hits it. fail.
25334      *      if start is after end or hits it fail.
25335      *
25336      *   if either hits (but other is outside. - then it's not 
25337      *   
25338      *    
25339      **/
25340     
25341     
25342     // @see http://www.thismuchiknow.co.uk/?p=64.
25343     rangeIntersectsNode : function(range, node)
25344     {
25345         var nodeRange = node.ownerDocument.createRange();
25346         try {
25347             nodeRange.selectNode(node);
25348         } catch (e) {
25349             nodeRange.selectNodeContents(node);
25350         }
25351     
25352         var rangeStartRange = range.cloneRange();
25353         rangeStartRange.collapse(true);
25354     
25355         var rangeEndRange = range.cloneRange();
25356         rangeEndRange.collapse(false);
25357     
25358         var nodeStartRange = nodeRange.cloneRange();
25359         nodeStartRange.collapse(true);
25360     
25361         var nodeEndRange = nodeRange.cloneRange();
25362         nodeEndRange.collapse(false);
25363     
25364         return rangeStartRange.compareBoundaryPoints(
25365                  Range.START_TO_START, nodeEndRange) == -1 &&
25366                rangeEndRange.compareBoundaryPoints(
25367                  Range.START_TO_START, nodeStartRange) == 1;
25368         
25369          
25370     },
25371     rangeCompareNode : function(range, node)
25372     {
25373         var nodeRange = node.ownerDocument.createRange();
25374         try {
25375             nodeRange.selectNode(node);
25376         } catch (e) {
25377             nodeRange.selectNodeContents(node);
25378         }
25379         
25380         
25381         range.collapse(true);
25382     
25383         nodeRange.collapse(true);
25384      
25385         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25386         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25387          
25388         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25389         
25390         var nodeIsBefore   =  ss == 1;
25391         var nodeIsAfter    = ee == -1;
25392         
25393         if (nodeIsBefore && nodeIsAfter) {
25394             return 0; // outer
25395         }
25396         if (!nodeIsBefore && nodeIsAfter) {
25397             return 1; //right trailed.
25398         }
25399         
25400         if (nodeIsBefore && !nodeIsAfter) {
25401             return 2;  // left trailed.
25402         }
25403         // fully contined.
25404         return 3;
25405     },
25406
25407     // private? - in a new class?
25408     cleanUpPaste :  function()
25409     {
25410         // cleans up the whole document..
25411         Roo.log('cleanuppaste');
25412         
25413         this.cleanUpChildren(this.doc.body);
25414         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25415         if (clean != this.doc.body.innerHTML) {
25416             this.doc.body.innerHTML = clean;
25417         }
25418         
25419     },
25420     
25421     cleanWordChars : function(input) {// change the chars to hex code
25422         var he = Roo.HtmlEditorCore;
25423         
25424         var output = input;
25425         Roo.each(he.swapCodes, function(sw) { 
25426             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25427             
25428             output = output.replace(swapper, sw[1]);
25429         });
25430         
25431         return output;
25432     },
25433     
25434     
25435     cleanUpChildren : function (n)
25436     {
25437         if (!n.childNodes.length) {
25438             return;
25439         }
25440         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25441            this.cleanUpChild(n.childNodes[i]);
25442         }
25443     },
25444     
25445     
25446         
25447     
25448     cleanUpChild : function (node)
25449     {
25450         var ed = this;
25451         //console.log(node);
25452         if (node.nodeName == "#text") {
25453             // clean up silly Windows -- stuff?
25454             return; 
25455         }
25456         if (node.nodeName == "#comment") {
25457             node.parentNode.removeChild(node);
25458             // clean up silly Windows -- stuff?
25459             return; 
25460         }
25461         var lcname = node.tagName.toLowerCase();
25462         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25463         // whitelist of tags..
25464         
25465         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25466             // remove node.
25467             node.parentNode.removeChild(node);
25468             return;
25469             
25470         }
25471         
25472         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25473         
25474         // spans with no attributes - just remove them..
25475         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25476             remove_keep_children = true;
25477         }
25478         
25479         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25480         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25481         
25482         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25483         //    remove_keep_children = true;
25484         //}
25485         
25486         if (remove_keep_children) {
25487             this.cleanUpChildren(node);
25488             // inserts everything just before this node...
25489             while (node.childNodes.length) {
25490                 var cn = node.childNodes[0];
25491                 node.removeChild(cn);
25492                 node.parentNode.insertBefore(cn, node);
25493             }
25494             node.parentNode.removeChild(node);
25495             return;
25496         }
25497         
25498         if (!node.attributes || !node.attributes.length) {
25499             
25500           
25501             
25502             
25503             this.cleanUpChildren(node);
25504             return;
25505         }
25506         
25507         function cleanAttr(n,v)
25508         {
25509             
25510             if (v.match(/^\./) || v.match(/^\//)) {
25511                 return;
25512             }
25513             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25514                 return;
25515             }
25516             if (v.match(/^#/)) {
25517                 return;
25518             }
25519             if (v.match(/^\{/)) { // allow template editing.
25520                 return;
25521             }
25522 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25523             node.removeAttribute(n);
25524             
25525         }
25526         
25527         var cwhite = this.cwhite;
25528         var cblack = this.cblack;
25529             
25530         function cleanStyle(n,v)
25531         {
25532             if (v.match(/expression/)) { //XSS?? should we even bother..
25533                 node.removeAttribute(n);
25534                 return;
25535             }
25536             
25537             var parts = v.split(/;/);
25538             var clean = [];
25539             
25540             Roo.each(parts, function(p) {
25541                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25542                 if (!p.length) {
25543                     return true;
25544                 }
25545                 var l = p.split(':').shift().replace(/\s+/g,'');
25546                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25547                 
25548                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25549 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25550                     //node.removeAttribute(n);
25551                     return true;
25552                 }
25553                 //Roo.log()
25554                 // only allow 'c whitelisted system attributes'
25555                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25556 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25557                     //node.removeAttribute(n);
25558                     return true;
25559                 }
25560                 
25561                 
25562                  
25563                 
25564                 clean.push(p);
25565                 return true;
25566             });
25567             if (clean.length) { 
25568                 node.setAttribute(n, clean.join(';'));
25569             } else {
25570                 node.removeAttribute(n);
25571             }
25572             
25573         }
25574         
25575         
25576         for (var i = node.attributes.length-1; i > -1 ; i--) {
25577             var a = node.attributes[i];
25578             //console.log(a);
25579             
25580             if (a.name.toLowerCase().substr(0,2)=='on')  {
25581                 node.removeAttribute(a.name);
25582                 continue;
25583             }
25584             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25585                 node.removeAttribute(a.name);
25586                 continue;
25587             }
25588             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25589                 cleanAttr(a.name,a.value); // fixme..
25590                 continue;
25591             }
25592             if (a.name == 'style') {
25593                 cleanStyle(a.name,a.value);
25594                 continue;
25595             }
25596             /// clean up MS crap..
25597             // tecnically this should be a list of valid class'es..
25598             
25599             
25600             if (a.name == 'class') {
25601                 if (a.value.match(/^Mso/)) {
25602                     node.removeAttribute('class');
25603                 }
25604                 
25605                 if (a.value.match(/^body$/)) {
25606                     node.removeAttribute('class');
25607                 }
25608                 continue;
25609             }
25610             
25611             // style cleanup!?
25612             // class cleanup?
25613             
25614         }
25615         
25616         
25617         this.cleanUpChildren(node);
25618         
25619         
25620     },
25621     
25622     /**
25623      * Clean up MS wordisms...
25624      */
25625     cleanWord : function(node)
25626     {
25627         if (!node) {
25628             this.cleanWord(this.doc.body);
25629             return;
25630         }
25631         
25632         if(
25633                 node.nodeName == 'SPAN' &&
25634                 !node.hasAttributes() &&
25635                 node.childNodes.length == 1 &&
25636                 node.firstChild.nodeName == "#text"  
25637         ) {
25638             var textNode = node.firstChild;
25639             node.removeChild(textNode);
25640             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25641                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25642             }
25643             node.parentNode.insertBefore(textNode, node);
25644             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25645                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25646             }
25647             node.parentNode.removeChild(node);
25648         }
25649         
25650         if (node.nodeName == "#text") {
25651             // clean up silly Windows -- stuff?
25652             return; 
25653         }
25654         if (node.nodeName == "#comment") {
25655             node.parentNode.removeChild(node);
25656             // clean up silly Windows -- stuff?
25657             return; 
25658         }
25659         
25660         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25661             node.parentNode.removeChild(node);
25662             return;
25663         }
25664         //Roo.log(node.tagName);
25665         // remove - but keep children..
25666         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25667             //Roo.log('-- removed');
25668             while (node.childNodes.length) {
25669                 var cn = node.childNodes[0];
25670                 node.removeChild(cn);
25671                 node.parentNode.insertBefore(cn, node);
25672                 // move node to parent - and clean it..
25673                 this.cleanWord(cn);
25674             }
25675             node.parentNode.removeChild(node);
25676             /// no need to iterate chidlren = it's got none..
25677             //this.iterateChildren(node, this.cleanWord);
25678             return;
25679         }
25680         // clean styles
25681         if (node.className.length) {
25682             
25683             var cn = node.className.split(/\W+/);
25684             var cna = [];
25685             Roo.each(cn, function(cls) {
25686                 if (cls.match(/Mso[a-zA-Z]+/)) {
25687                     return;
25688                 }
25689                 cna.push(cls);
25690             });
25691             node.className = cna.length ? cna.join(' ') : '';
25692             if (!cna.length) {
25693                 node.removeAttribute("class");
25694             }
25695         }
25696         
25697         if (node.hasAttribute("lang")) {
25698             node.removeAttribute("lang");
25699         }
25700         
25701         if (node.hasAttribute("style")) {
25702             
25703             var styles = node.getAttribute("style").split(";");
25704             var nstyle = [];
25705             Roo.each(styles, function(s) {
25706                 if (!s.match(/:/)) {
25707                     return;
25708                 }
25709                 var kv = s.split(":");
25710                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25711                     return;
25712                 }
25713                 // what ever is left... we allow.
25714                 nstyle.push(s);
25715             });
25716             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25717             if (!nstyle.length) {
25718                 node.removeAttribute('style');
25719             }
25720         }
25721         this.iterateChildren(node, this.cleanWord);
25722         
25723         
25724         
25725     },
25726     /**
25727      * iterateChildren of a Node, calling fn each time, using this as the scole..
25728      * @param {DomNode} node node to iterate children of.
25729      * @param {Function} fn method of this class to call on each item.
25730      */
25731     iterateChildren : function(node, fn)
25732     {
25733         if (!node.childNodes.length) {
25734                 return;
25735         }
25736         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25737            fn.call(this, node.childNodes[i])
25738         }
25739     },
25740     
25741     
25742     /**
25743      * cleanTableWidths.
25744      *
25745      * Quite often pasting from word etc.. results in tables with column and widths.
25746      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25747      *
25748      */
25749     cleanTableWidths : function(node)
25750     {
25751          
25752          
25753         if (!node) {
25754             this.cleanTableWidths(this.doc.body);
25755             return;
25756         }
25757         
25758         // ignore list...
25759         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25760             return; 
25761         }
25762         Roo.log(node.tagName);
25763         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25764             this.iterateChildren(node, this.cleanTableWidths);
25765             return;
25766         }
25767         if (node.hasAttribute('width')) {
25768             node.removeAttribute('width');
25769         }
25770         
25771          
25772         if (node.hasAttribute("style")) {
25773             // pretty basic...
25774             
25775             var styles = node.getAttribute("style").split(";");
25776             var nstyle = [];
25777             Roo.each(styles, function(s) {
25778                 if (!s.match(/:/)) {
25779                     return;
25780                 }
25781                 var kv = s.split(":");
25782                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25783                     return;
25784                 }
25785                 // what ever is left... we allow.
25786                 nstyle.push(s);
25787             });
25788             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25789             if (!nstyle.length) {
25790                 node.removeAttribute('style');
25791             }
25792         }
25793         
25794         this.iterateChildren(node, this.cleanTableWidths);
25795         
25796         
25797     },
25798     
25799     
25800     
25801     
25802     domToHTML : function(currentElement, depth, nopadtext) {
25803         
25804         depth = depth || 0;
25805         nopadtext = nopadtext || false;
25806     
25807         if (!currentElement) {
25808             return this.domToHTML(this.doc.body);
25809         }
25810         
25811         //Roo.log(currentElement);
25812         var j;
25813         var allText = false;
25814         var nodeName = currentElement.nodeName;
25815         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25816         
25817         if  (nodeName == '#text') {
25818             
25819             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25820         }
25821         
25822         
25823         var ret = '';
25824         if (nodeName != 'BODY') {
25825              
25826             var i = 0;
25827             // Prints the node tagName, such as <A>, <IMG>, etc
25828             if (tagName) {
25829                 var attr = [];
25830                 for(i = 0; i < currentElement.attributes.length;i++) {
25831                     // quoting?
25832                     var aname = currentElement.attributes.item(i).name;
25833                     if (!currentElement.attributes.item(i).value.length) {
25834                         continue;
25835                     }
25836                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25837                 }
25838                 
25839                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25840             } 
25841             else {
25842                 
25843                 // eack
25844             }
25845         } else {
25846             tagName = false;
25847         }
25848         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25849             return ret;
25850         }
25851         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25852             nopadtext = true;
25853         }
25854         
25855         
25856         // Traverse the tree
25857         i = 0;
25858         var currentElementChild = currentElement.childNodes.item(i);
25859         var allText = true;
25860         var innerHTML  = '';
25861         lastnode = '';
25862         while (currentElementChild) {
25863             // Formatting code (indent the tree so it looks nice on the screen)
25864             var nopad = nopadtext;
25865             if (lastnode == 'SPAN') {
25866                 nopad  = true;
25867             }
25868             // text
25869             if  (currentElementChild.nodeName == '#text') {
25870                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25871                 toadd = nopadtext ? toadd : toadd.trim();
25872                 if (!nopad && toadd.length > 80) {
25873                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25874                 }
25875                 innerHTML  += toadd;
25876                 
25877                 i++;
25878                 currentElementChild = currentElement.childNodes.item(i);
25879                 lastNode = '';
25880                 continue;
25881             }
25882             allText = false;
25883             
25884             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25885                 
25886             // Recursively traverse the tree structure of the child node
25887             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25888             lastnode = currentElementChild.nodeName;
25889             i++;
25890             currentElementChild=currentElement.childNodes.item(i);
25891         }
25892         
25893         ret += innerHTML;
25894         
25895         if (!allText) {
25896                 // The remaining code is mostly for formatting the tree
25897             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25898         }
25899         
25900         
25901         if (tagName) {
25902             ret+= "</"+tagName+">";
25903         }
25904         return ret;
25905         
25906     },
25907         
25908     applyBlacklists : function()
25909     {
25910         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25911         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25912         
25913         this.white = [];
25914         this.black = [];
25915         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25916             if (b.indexOf(tag) > -1) {
25917                 return;
25918             }
25919             this.white.push(tag);
25920             
25921         }, this);
25922         
25923         Roo.each(w, function(tag) {
25924             if (b.indexOf(tag) > -1) {
25925                 return;
25926             }
25927             if (this.white.indexOf(tag) > -1) {
25928                 return;
25929             }
25930             this.white.push(tag);
25931             
25932         }, this);
25933         
25934         
25935         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25936             if (w.indexOf(tag) > -1) {
25937                 return;
25938             }
25939             this.black.push(tag);
25940             
25941         }, this);
25942         
25943         Roo.each(b, function(tag) {
25944             if (w.indexOf(tag) > -1) {
25945                 return;
25946             }
25947             if (this.black.indexOf(tag) > -1) {
25948                 return;
25949             }
25950             this.black.push(tag);
25951             
25952         }, this);
25953         
25954         
25955         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25956         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25957         
25958         this.cwhite = [];
25959         this.cblack = [];
25960         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25961             if (b.indexOf(tag) > -1) {
25962                 return;
25963             }
25964             this.cwhite.push(tag);
25965             
25966         }, this);
25967         
25968         Roo.each(w, function(tag) {
25969             if (b.indexOf(tag) > -1) {
25970                 return;
25971             }
25972             if (this.cwhite.indexOf(tag) > -1) {
25973                 return;
25974             }
25975             this.cwhite.push(tag);
25976             
25977         }, this);
25978         
25979         
25980         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25981             if (w.indexOf(tag) > -1) {
25982                 return;
25983             }
25984             this.cblack.push(tag);
25985             
25986         }, this);
25987         
25988         Roo.each(b, function(tag) {
25989             if (w.indexOf(tag) > -1) {
25990                 return;
25991             }
25992             if (this.cblack.indexOf(tag) > -1) {
25993                 return;
25994             }
25995             this.cblack.push(tag);
25996             
25997         }, this);
25998     },
25999     
26000     setStylesheets : function(stylesheets)
26001     {
26002         if(typeof(stylesheets) == 'string'){
26003             Roo.get(this.iframe.contentDocument.head).createChild({
26004                 tag : 'link',
26005                 rel : 'stylesheet',
26006                 type : 'text/css',
26007                 href : stylesheets
26008             });
26009             
26010             return;
26011         }
26012         var _this = this;
26013      
26014         Roo.each(stylesheets, function(s) {
26015             if(!s.length){
26016                 return;
26017             }
26018             
26019             Roo.get(_this.iframe.contentDocument.head).createChild({
26020                 tag : 'link',
26021                 rel : 'stylesheet',
26022                 type : 'text/css',
26023                 href : s
26024             });
26025         });
26026
26027         
26028     },
26029     
26030     removeStylesheets : function()
26031     {
26032         var _this = this;
26033         
26034         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26035             s.remove();
26036         });
26037     },
26038     
26039     setStyle : function(style)
26040     {
26041         Roo.get(this.iframe.contentDocument.head).createChild({
26042             tag : 'style',
26043             type : 'text/css',
26044             html : style
26045         });
26046
26047         return;
26048     }
26049     
26050     // hide stuff that is not compatible
26051     /**
26052      * @event blur
26053      * @hide
26054      */
26055     /**
26056      * @event change
26057      * @hide
26058      */
26059     /**
26060      * @event focus
26061      * @hide
26062      */
26063     /**
26064      * @event specialkey
26065      * @hide
26066      */
26067     /**
26068      * @cfg {String} fieldClass @hide
26069      */
26070     /**
26071      * @cfg {String} focusClass @hide
26072      */
26073     /**
26074      * @cfg {String} autoCreate @hide
26075      */
26076     /**
26077      * @cfg {String} inputType @hide
26078      */
26079     /**
26080      * @cfg {String} invalidClass @hide
26081      */
26082     /**
26083      * @cfg {String} invalidText @hide
26084      */
26085     /**
26086      * @cfg {String} msgFx @hide
26087      */
26088     /**
26089      * @cfg {String} validateOnBlur @hide
26090      */
26091 });
26092
26093 Roo.HtmlEditorCore.white = [
26094         'area', 'br', 'img', 'input', 'hr', 'wbr',
26095         
26096        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26097        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26098        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26099        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26100        'table',   'ul',         'xmp', 
26101        
26102        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26103       'thead',   'tr', 
26104      
26105       'dir', 'menu', 'ol', 'ul', 'dl',
26106        
26107       'embed',  'object'
26108 ];
26109
26110
26111 Roo.HtmlEditorCore.black = [
26112     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26113         'applet', // 
26114         'base',   'basefont', 'bgsound', 'blink',  'body', 
26115         'frame',  'frameset', 'head',    'html',   'ilayer', 
26116         'iframe', 'layer',  'link',     'meta',    'object',   
26117         'script', 'style' ,'title',  'xml' // clean later..
26118 ];
26119 Roo.HtmlEditorCore.clean = [
26120     'script', 'style', 'title', 'xml'
26121 ];
26122 Roo.HtmlEditorCore.remove = [
26123     'font'
26124 ];
26125 // attributes..
26126
26127 Roo.HtmlEditorCore.ablack = [
26128     'on'
26129 ];
26130     
26131 Roo.HtmlEditorCore.aclean = [ 
26132     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26133 ];
26134
26135 // protocols..
26136 Roo.HtmlEditorCore.pwhite= [
26137         'http',  'https',  'mailto'
26138 ];
26139
26140 // white listed style attributes.
26141 Roo.HtmlEditorCore.cwhite= [
26142       //  'text-align', /// default is to allow most things..
26143       
26144          
26145 //        'font-size'//??
26146 ];
26147
26148 // black listed style attributes.
26149 Roo.HtmlEditorCore.cblack= [
26150       //  'font-size' -- this can be set by the project 
26151 ];
26152
26153
26154 Roo.HtmlEditorCore.swapCodes   =[ 
26155     [    8211, "&#8211;" ], 
26156     [    8212, "&#8212;" ], 
26157     [    8216,  "'" ],  
26158     [    8217, "'" ],  
26159     [    8220, '"' ],  
26160     [    8221, '"' ],  
26161     [    8226, "*" ],  
26162     [    8230, "..." ]
26163 ]; 
26164
26165     /*
26166  * - LGPL
26167  *
26168  * HtmlEditor
26169  * 
26170  */
26171
26172 /**
26173  * @class Roo.bootstrap.HtmlEditor
26174  * @extends Roo.bootstrap.TextArea
26175  * Bootstrap HtmlEditor class
26176
26177  * @constructor
26178  * Create a new HtmlEditor
26179  * @param {Object} config The config object
26180  */
26181
26182 Roo.bootstrap.HtmlEditor = function(config){
26183     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26184     if (!this.toolbars) {
26185         this.toolbars = [];
26186     }
26187     
26188     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26189     this.addEvents({
26190             /**
26191              * @event initialize
26192              * Fires when the editor is fully initialized (including the iframe)
26193              * @param {HtmlEditor} this
26194              */
26195             initialize: true,
26196             /**
26197              * @event activate
26198              * Fires when the editor is first receives the focus. Any insertion must wait
26199              * until after this event.
26200              * @param {HtmlEditor} this
26201              */
26202             activate: true,
26203              /**
26204              * @event beforesync
26205              * Fires before the textarea is updated with content from the editor iframe. Return false
26206              * to cancel the sync.
26207              * @param {HtmlEditor} this
26208              * @param {String} html
26209              */
26210             beforesync: true,
26211              /**
26212              * @event beforepush
26213              * Fires before the iframe editor is updated with content from the textarea. Return false
26214              * to cancel the push.
26215              * @param {HtmlEditor} this
26216              * @param {String} html
26217              */
26218             beforepush: true,
26219              /**
26220              * @event sync
26221              * Fires when the textarea is updated with content from the editor iframe.
26222              * @param {HtmlEditor} this
26223              * @param {String} html
26224              */
26225             sync: true,
26226              /**
26227              * @event push
26228              * Fires when the iframe editor is updated with content from the textarea.
26229              * @param {HtmlEditor} this
26230              * @param {String} html
26231              */
26232             push: true,
26233              /**
26234              * @event editmodechange
26235              * Fires when the editor switches edit modes
26236              * @param {HtmlEditor} this
26237              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26238              */
26239             editmodechange: true,
26240             /**
26241              * @event editorevent
26242              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26243              * @param {HtmlEditor} this
26244              */
26245             editorevent: true,
26246             /**
26247              * @event firstfocus
26248              * Fires when on first focus - needed by toolbars..
26249              * @param {HtmlEditor} this
26250              */
26251             firstfocus: true,
26252             /**
26253              * @event autosave
26254              * Auto save the htmlEditor value as a file into Events
26255              * @param {HtmlEditor} this
26256              */
26257             autosave: true,
26258             /**
26259              * @event savedpreview
26260              * preview the saved version of htmlEditor
26261              * @param {HtmlEditor} this
26262              */
26263             savedpreview: true
26264         });
26265 };
26266
26267
26268 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26269     
26270     
26271       /**
26272      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26273      */
26274     toolbars : false,
26275     
26276      /**
26277     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26278     */
26279     btns : [],
26280    
26281      /**
26282      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26283      *                        Roo.resizable.
26284      */
26285     resizable : false,
26286      /**
26287      * @cfg {Number} height (in pixels)
26288      */   
26289     height: 300,
26290    /**
26291      * @cfg {Number} width (in pixels)
26292      */   
26293     width: false,
26294     
26295     /**
26296      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26297      * 
26298      */
26299     stylesheets: false,
26300     
26301     // id of frame..
26302     frameId: false,
26303     
26304     // private properties
26305     validationEvent : false,
26306     deferHeight: true,
26307     initialized : false,
26308     activated : false,
26309     
26310     onFocus : Roo.emptyFn,
26311     iframePad:3,
26312     hideMode:'offsets',
26313     
26314     tbContainer : false,
26315     
26316     bodyCls : '',
26317     
26318     toolbarContainer :function() {
26319         return this.wrap.select('.x-html-editor-tb',true).first();
26320     },
26321
26322     /**
26323      * Protected method that will not generally be called directly. It
26324      * is called when the editor creates its toolbar. Override this method if you need to
26325      * add custom toolbar buttons.
26326      * @param {HtmlEditor} editor
26327      */
26328     createToolbar : function(){
26329         Roo.log('renewing');
26330         Roo.log("create toolbars");
26331         
26332         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26333         this.toolbars[0].render(this.toolbarContainer());
26334         
26335         return;
26336         
26337 //        if (!editor.toolbars || !editor.toolbars.length) {
26338 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26339 //        }
26340 //        
26341 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26342 //            editor.toolbars[i] = Roo.factory(
26343 //                    typeof(editor.toolbars[i]) == 'string' ?
26344 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26345 //                Roo.bootstrap.HtmlEditor);
26346 //            editor.toolbars[i].init(editor);
26347 //        }
26348     },
26349
26350      
26351     // private
26352     onRender : function(ct, position)
26353     {
26354        // Roo.log("Call onRender: " + this.xtype);
26355         var _t = this;
26356         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26357       
26358         this.wrap = this.inputEl().wrap({
26359             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26360         });
26361         
26362         this.editorcore.onRender(ct, position);
26363          
26364         if (this.resizable) {
26365             this.resizeEl = new Roo.Resizable(this.wrap, {
26366                 pinned : true,
26367                 wrap: true,
26368                 dynamic : true,
26369                 minHeight : this.height,
26370                 height: this.height,
26371                 handles : this.resizable,
26372                 width: this.width,
26373                 listeners : {
26374                     resize : function(r, w, h) {
26375                         _t.onResize(w,h); // -something
26376                     }
26377                 }
26378             });
26379             
26380         }
26381         this.createToolbar(this);
26382        
26383         
26384         if(!this.width && this.resizable){
26385             this.setSize(this.wrap.getSize());
26386         }
26387         if (this.resizeEl) {
26388             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26389             // should trigger onReize..
26390         }
26391         
26392     },
26393
26394     // private
26395     onResize : function(w, h)
26396     {
26397         Roo.log('resize: ' +w + ',' + h );
26398         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26399         var ew = false;
26400         var eh = false;
26401         
26402         if(this.inputEl() ){
26403             if(typeof w == 'number'){
26404                 var aw = w - this.wrap.getFrameWidth('lr');
26405                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26406                 ew = aw;
26407             }
26408             if(typeof h == 'number'){
26409                  var tbh = -11;  // fixme it needs to tool bar size!
26410                 for (var i =0; i < this.toolbars.length;i++) {
26411                     // fixme - ask toolbars for heights?
26412                     tbh += this.toolbars[i].el.getHeight();
26413                     //if (this.toolbars[i].footer) {
26414                     //    tbh += this.toolbars[i].footer.el.getHeight();
26415                     //}
26416                 }
26417               
26418                 
26419                 
26420                 
26421                 
26422                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26423                 ah -= 5; // knock a few pixes off for look..
26424                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26425                 var eh = ah;
26426             }
26427         }
26428         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26429         this.editorcore.onResize(ew,eh);
26430         
26431     },
26432
26433     /**
26434      * Toggles the editor between standard and source edit mode.
26435      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26436      */
26437     toggleSourceEdit : function(sourceEditMode)
26438     {
26439         this.editorcore.toggleSourceEdit(sourceEditMode);
26440         
26441         if(this.editorcore.sourceEditMode){
26442             Roo.log('editor - showing textarea');
26443             
26444 //            Roo.log('in');
26445 //            Roo.log(this.syncValue());
26446             this.syncValue();
26447             this.inputEl().removeClass(['hide', 'x-hidden']);
26448             this.inputEl().dom.removeAttribute('tabIndex');
26449             this.inputEl().focus();
26450         }else{
26451             Roo.log('editor - hiding textarea');
26452 //            Roo.log('out')
26453 //            Roo.log(this.pushValue()); 
26454             this.pushValue();
26455             
26456             this.inputEl().addClass(['hide', 'x-hidden']);
26457             this.inputEl().dom.setAttribute('tabIndex', -1);
26458             //this.deferFocus();
26459         }
26460          
26461         if(this.resizable){
26462             this.setSize(this.wrap.getSize());
26463         }
26464         
26465         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26466     },
26467  
26468     // private (for BoxComponent)
26469     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26470
26471     // private (for BoxComponent)
26472     getResizeEl : function(){
26473         return this.wrap;
26474     },
26475
26476     // private (for BoxComponent)
26477     getPositionEl : function(){
26478         return this.wrap;
26479     },
26480
26481     // private
26482     initEvents : function(){
26483         this.originalValue = this.getValue();
26484     },
26485
26486 //    /**
26487 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26488 //     * @method
26489 //     */
26490 //    markInvalid : Roo.emptyFn,
26491 //    /**
26492 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26493 //     * @method
26494 //     */
26495 //    clearInvalid : Roo.emptyFn,
26496
26497     setValue : function(v){
26498         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26499         this.editorcore.pushValue();
26500     },
26501
26502      
26503     // private
26504     deferFocus : function(){
26505         this.focus.defer(10, this);
26506     },
26507
26508     // doc'ed in Field
26509     focus : function(){
26510         this.editorcore.focus();
26511         
26512     },
26513       
26514
26515     // private
26516     onDestroy : function(){
26517         
26518         
26519         
26520         if(this.rendered){
26521             
26522             for (var i =0; i < this.toolbars.length;i++) {
26523                 // fixme - ask toolbars for heights?
26524                 this.toolbars[i].onDestroy();
26525             }
26526             
26527             this.wrap.dom.innerHTML = '';
26528             this.wrap.remove();
26529         }
26530     },
26531
26532     // private
26533     onFirstFocus : function(){
26534         //Roo.log("onFirstFocus");
26535         this.editorcore.onFirstFocus();
26536          for (var i =0; i < this.toolbars.length;i++) {
26537             this.toolbars[i].onFirstFocus();
26538         }
26539         
26540     },
26541     
26542     // private
26543     syncValue : function()
26544     {   
26545         this.editorcore.syncValue();
26546     },
26547     
26548     pushValue : function()
26549     {   
26550         this.editorcore.pushValue();
26551     }
26552      
26553     
26554     // hide stuff that is not compatible
26555     /**
26556      * @event blur
26557      * @hide
26558      */
26559     /**
26560      * @event change
26561      * @hide
26562      */
26563     /**
26564      * @event focus
26565      * @hide
26566      */
26567     /**
26568      * @event specialkey
26569      * @hide
26570      */
26571     /**
26572      * @cfg {String} fieldClass @hide
26573      */
26574     /**
26575      * @cfg {String} focusClass @hide
26576      */
26577     /**
26578      * @cfg {String} autoCreate @hide
26579      */
26580     /**
26581      * @cfg {String} inputType @hide
26582      */
26583      
26584     /**
26585      * @cfg {String} invalidText @hide
26586      */
26587     /**
26588      * @cfg {String} msgFx @hide
26589      */
26590     /**
26591      * @cfg {String} validateOnBlur @hide
26592      */
26593 });
26594  
26595     
26596    
26597    
26598    
26599       
26600 Roo.namespace('Roo.bootstrap.htmleditor');
26601 /**
26602  * @class Roo.bootstrap.HtmlEditorToolbar1
26603  * Basic Toolbar
26604  * 
26605  * @example
26606  * Usage:
26607  *
26608  new Roo.bootstrap.HtmlEditor({
26609     ....
26610     toolbars : [
26611         new Roo.bootstrap.HtmlEditorToolbar1({
26612             disable : { fonts: 1 , format: 1, ..., ... , ...],
26613             btns : [ .... ]
26614         })
26615     }
26616      
26617  * 
26618  * @cfg {Object} disable List of elements to disable..
26619  * @cfg {Array} btns List of additional buttons.
26620  * 
26621  * 
26622  * NEEDS Extra CSS? 
26623  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26624  */
26625  
26626 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26627 {
26628     
26629     Roo.apply(this, config);
26630     
26631     // default disabled, based on 'good practice'..
26632     this.disable = this.disable || {};
26633     Roo.applyIf(this.disable, {
26634         fontSize : true,
26635         colors : true,
26636         specialElements : true
26637     });
26638     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26639     
26640     this.editor = config.editor;
26641     this.editorcore = config.editor.editorcore;
26642     
26643     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26644     
26645     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26646     // dont call parent... till later.
26647 }
26648 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26649      
26650     bar : true,
26651     
26652     editor : false,
26653     editorcore : false,
26654     
26655     
26656     formats : [
26657         "p" ,  
26658         "h1","h2","h3","h4","h5","h6", 
26659         "pre", "code", 
26660         "abbr", "acronym", "address", "cite", "samp", "var",
26661         'div','span'
26662     ],
26663     
26664     onRender : function(ct, position)
26665     {
26666        // Roo.log("Call onRender: " + this.xtype);
26667         
26668        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26669        Roo.log(this.el);
26670        this.el.dom.style.marginBottom = '0';
26671        var _this = this;
26672        var editorcore = this.editorcore;
26673        var editor= this.editor;
26674        
26675        var children = [];
26676        var btn = function(id,cmd , toggle, handler, html){
26677        
26678             var  event = toggle ? 'toggle' : 'click';
26679        
26680             var a = {
26681                 size : 'sm',
26682                 xtype: 'Button',
26683                 xns: Roo.bootstrap,
26684                 //glyphicon : id,
26685                 fa: id,
26686                 cmd : id || cmd,
26687                 enableToggle:toggle !== false,
26688                 html : html || '',
26689                 pressed : toggle ? false : null,
26690                 listeners : {}
26691             };
26692             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26693                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26694             };
26695             children.push(a);
26696             return a;
26697        }
26698        
26699     //    var cb_box = function...
26700         
26701         var style = {
26702                 xtype: 'Button',
26703                 size : 'sm',
26704                 xns: Roo.bootstrap,
26705                 fa : 'font',
26706                 //html : 'submit'
26707                 menu : {
26708                     xtype: 'Menu',
26709                     xns: Roo.bootstrap,
26710                     items:  []
26711                 }
26712         };
26713         Roo.each(this.formats, function(f) {
26714             style.menu.items.push({
26715                 xtype :'MenuItem',
26716                 xns: Roo.bootstrap,
26717                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26718                 tagname : f,
26719                 listeners : {
26720                     click : function()
26721                     {
26722                         editorcore.insertTag(this.tagname);
26723                         editor.focus();
26724                     }
26725                 }
26726                 
26727             });
26728         });
26729         children.push(style);   
26730         
26731         btn('bold',false,true);
26732         btn('italic',false,true);
26733         btn('align-left', 'justifyleft',true);
26734         btn('align-center', 'justifycenter',true);
26735         btn('align-right' , 'justifyright',true);
26736         btn('link', false, false, function(btn) {
26737             //Roo.log("create link?");
26738             var url = prompt(this.createLinkText, this.defaultLinkValue);
26739             if(url && url != 'http:/'+'/'){
26740                 this.editorcore.relayCmd('createlink', url);
26741             }
26742         }),
26743         btn('list','insertunorderedlist',true);
26744         btn('pencil', false,true, function(btn){
26745                 Roo.log(this);
26746                 this.toggleSourceEdit(btn.pressed);
26747         });
26748         
26749         if (this.editor.btns.length > 0) {
26750             for (var i = 0; i<this.editor.btns.length; i++) {
26751                 children.push(this.editor.btns[i]);
26752             }
26753         }
26754         
26755         /*
26756         var cog = {
26757                 xtype: 'Button',
26758                 size : 'sm',
26759                 xns: Roo.bootstrap,
26760                 glyphicon : 'cog',
26761                 //html : 'submit'
26762                 menu : {
26763                     xtype: 'Menu',
26764                     xns: Roo.bootstrap,
26765                     items:  []
26766                 }
26767         };
26768         
26769         cog.menu.items.push({
26770             xtype :'MenuItem',
26771             xns: Roo.bootstrap,
26772             html : Clean styles,
26773             tagname : f,
26774             listeners : {
26775                 click : function()
26776                 {
26777                     editorcore.insertTag(this.tagname);
26778                     editor.focus();
26779                 }
26780             }
26781             
26782         });
26783        */
26784         
26785          
26786        this.xtype = 'NavSimplebar';
26787         
26788         for(var i=0;i< children.length;i++) {
26789             
26790             this.buttons.add(this.addxtypeChild(children[i]));
26791             
26792         }
26793         
26794         editor.on('editorevent', this.updateToolbar, this);
26795     },
26796     onBtnClick : function(id)
26797     {
26798        this.editorcore.relayCmd(id);
26799        this.editorcore.focus();
26800     },
26801     
26802     /**
26803      * Protected method that will not generally be called directly. It triggers
26804      * a toolbar update by reading the markup state of the current selection in the editor.
26805      */
26806     updateToolbar: function(){
26807
26808         if(!this.editorcore.activated){
26809             this.editor.onFirstFocus(); // is this neeed?
26810             return;
26811         }
26812
26813         var btns = this.buttons; 
26814         var doc = this.editorcore.doc;
26815         btns.get('bold').setActive(doc.queryCommandState('bold'));
26816         btns.get('italic').setActive(doc.queryCommandState('italic'));
26817         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26818         
26819         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26820         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26821         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26822         
26823         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26824         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26825          /*
26826         
26827         var ans = this.editorcore.getAllAncestors();
26828         if (this.formatCombo) {
26829             
26830             
26831             var store = this.formatCombo.store;
26832             this.formatCombo.setValue("");
26833             for (var i =0; i < ans.length;i++) {
26834                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26835                     // select it..
26836                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26837                     break;
26838                 }
26839             }
26840         }
26841         
26842         
26843         
26844         // hides menus... - so this cant be on a menu...
26845         Roo.bootstrap.MenuMgr.hideAll();
26846         */
26847         Roo.bootstrap.MenuMgr.hideAll();
26848         //this.editorsyncValue();
26849     },
26850     onFirstFocus: function() {
26851         this.buttons.each(function(item){
26852            item.enable();
26853         });
26854     },
26855     toggleSourceEdit : function(sourceEditMode){
26856         
26857           
26858         if(sourceEditMode){
26859             Roo.log("disabling buttons");
26860            this.buttons.each( function(item){
26861                 if(item.cmd != 'pencil'){
26862                     item.disable();
26863                 }
26864             });
26865           
26866         }else{
26867             Roo.log("enabling buttons");
26868             if(this.editorcore.initialized){
26869                 this.buttons.each( function(item){
26870                     item.enable();
26871                 });
26872             }
26873             
26874         }
26875         Roo.log("calling toggole on editor");
26876         // tell the editor that it's been pressed..
26877         this.editor.toggleSourceEdit(sourceEditMode);
26878        
26879     }
26880 });
26881
26882
26883
26884
26885  
26886 /*
26887  * - LGPL
26888  */
26889
26890 /**
26891  * @class Roo.bootstrap.Markdown
26892  * @extends Roo.bootstrap.TextArea
26893  * Bootstrap Showdown editable area
26894  * @cfg {string} content
26895  * 
26896  * @constructor
26897  * Create a new Showdown
26898  */
26899
26900 Roo.bootstrap.Markdown = function(config){
26901     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26902    
26903 };
26904
26905 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26906     
26907     editing :false,
26908     
26909     initEvents : function()
26910     {
26911         
26912         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26913         this.markdownEl = this.el.createChild({
26914             cls : 'roo-markdown-area'
26915         });
26916         this.inputEl().addClass('d-none');
26917         if (this.getValue() == '') {
26918             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26919             
26920         } else {
26921             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26922         }
26923         this.markdownEl.on('click', this.toggleTextEdit, this);
26924         this.on('blur', this.toggleTextEdit, this);
26925         this.on('specialkey', this.resizeTextArea, this);
26926     },
26927     
26928     toggleTextEdit : function()
26929     {
26930         var sh = this.markdownEl.getHeight();
26931         this.inputEl().addClass('d-none');
26932         this.markdownEl.addClass('d-none');
26933         if (!this.editing) {
26934             // show editor?
26935             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26936             this.inputEl().removeClass('d-none');
26937             this.inputEl().focus();
26938             this.editing = true;
26939             return;
26940         }
26941         // show showdown...
26942         this.updateMarkdown();
26943         this.markdownEl.removeClass('d-none');
26944         this.editing = false;
26945         return;
26946     },
26947     updateMarkdown : function()
26948     {
26949         if (this.getValue() == '') {
26950             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26951             return;
26952         }
26953  
26954         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26955     },
26956     
26957     resizeTextArea: function () {
26958         
26959         var sh = 100;
26960         Roo.log([sh, this.getValue().split("\n").length * 30]);
26961         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26962     },
26963     setValue : function(val)
26964     {
26965         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26966         if (!this.editing) {
26967             this.updateMarkdown();
26968         }
26969         
26970     },
26971     focus : function()
26972     {
26973         if (!this.editing) {
26974             this.toggleTextEdit();
26975         }
26976         
26977     }
26978
26979
26980 });
26981 /**
26982  * @class Roo.bootstrap.Table.AbstractSelectionModel
26983  * @extends Roo.util.Observable
26984  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26985  * implemented by descendant classes.  This class should not be directly instantiated.
26986  * @constructor
26987  */
26988 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26989     this.locked = false;
26990     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26991 };
26992
26993
26994 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26995     /** @ignore Called by the grid automatically. Do not call directly. */
26996     init : function(grid){
26997         this.grid = grid;
26998         this.initEvents();
26999     },
27000
27001     /**
27002      * Locks the selections.
27003      */
27004     lock : function(){
27005         this.locked = true;
27006     },
27007
27008     /**
27009      * Unlocks the selections.
27010      */
27011     unlock : function(){
27012         this.locked = false;
27013     },
27014
27015     /**
27016      * Returns true if the selections are locked.
27017      * @return {Boolean}
27018      */
27019     isLocked : function(){
27020         return this.locked;
27021     },
27022     
27023     
27024     initEvents : function ()
27025     {
27026         
27027     }
27028 });
27029 /**
27030  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27031  * @class Roo.bootstrap.Table.RowSelectionModel
27032  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27033  * It supports multiple selections and keyboard selection/navigation. 
27034  * @constructor
27035  * @param {Object} config
27036  */
27037
27038 Roo.bootstrap.Table.RowSelectionModel = function(config){
27039     Roo.apply(this, config);
27040     this.selections = new Roo.util.MixedCollection(false, function(o){
27041         return o.id;
27042     });
27043
27044     this.last = false;
27045     this.lastActive = false;
27046
27047     this.addEvents({
27048         /**
27049              * @event selectionchange
27050              * Fires when the selection changes
27051              * @param {SelectionModel} this
27052              */
27053             "selectionchange" : true,
27054         /**
27055              * @event afterselectionchange
27056              * Fires after the selection changes (eg. by key press or clicking)
27057              * @param {SelectionModel} this
27058              */
27059             "afterselectionchange" : true,
27060         /**
27061              * @event beforerowselect
27062              * Fires when a row is selected being selected, return false to cancel.
27063              * @param {SelectionModel} this
27064              * @param {Number} rowIndex The selected index
27065              * @param {Boolean} keepExisting False if other selections will be cleared
27066              */
27067             "beforerowselect" : true,
27068         /**
27069              * @event rowselect
27070              * Fires when a row is selected.
27071              * @param {SelectionModel} this
27072              * @param {Number} rowIndex The selected index
27073              * @param {Roo.data.Record} r The record
27074              */
27075             "rowselect" : true,
27076         /**
27077              * @event rowdeselect
27078              * Fires when a row is deselected.
27079              * @param {SelectionModel} this
27080              * @param {Number} rowIndex The selected index
27081              */
27082         "rowdeselect" : true
27083     });
27084     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27085     this.locked = false;
27086  };
27087
27088 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27089     /**
27090      * @cfg {Boolean} singleSelect
27091      * True to allow selection of only one row at a time (defaults to false)
27092      */
27093     singleSelect : false,
27094
27095     // private
27096     initEvents : function()
27097     {
27098
27099         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27100         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27101         //}else{ // allow click to work like normal
27102          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27103         //}
27104         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27105         this.grid.on("rowclick", this.handleMouseDown, this);
27106         
27107         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27108             "up" : function(e){
27109                 if(!e.shiftKey){
27110                     this.selectPrevious(e.shiftKey);
27111                 }else if(this.last !== false && this.lastActive !== false){
27112                     var last = this.last;
27113                     this.selectRange(this.last,  this.lastActive-1);
27114                     this.grid.getView().focusRow(this.lastActive);
27115                     if(last !== false){
27116                         this.last = last;
27117                     }
27118                 }else{
27119                     this.selectFirstRow();
27120                 }
27121                 this.fireEvent("afterselectionchange", this);
27122             },
27123             "down" : function(e){
27124                 if(!e.shiftKey){
27125                     this.selectNext(e.shiftKey);
27126                 }else if(this.last !== false && this.lastActive !== false){
27127                     var last = this.last;
27128                     this.selectRange(this.last,  this.lastActive+1);
27129                     this.grid.getView().focusRow(this.lastActive);
27130                     if(last !== false){
27131                         this.last = last;
27132                     }
27133                 }else{
27134                     this.selectFirstRow();
27135                 }
27136                 this.fireEvent("afterselectionchange", this);
27137             },
27138             scope: this
27139         });
27140         this.grid.store.on('load', function(){
27141             this.selections.clear();
27142         },this);
27143         /*
27144         var view = this.grid.view;
27145         view.on("refresh", this.onRefresh, this);
27146         view.on("rowupdated", this.onRowUpdated, this);
27147         view.on("rowremoved", this.onRemove, this);
27148         */
27149     },
27150
27151     // private
27152     onRefresh : function()
27153     {
27154         var ds = this.grid.store, i, v = this.grid.view;
27155         var s = this.selections;
27156         s.each(function(r){
27157             if((i = ds.indexOfId(r.id)) != -1){
27158                 v.onRowSelect(i);
27159             }else{
27160                 s.remove(r);
27161             }
27162         });
27163     },
27164
27165     // private
27166     onRemove : function(v, index, r){
27167         this.selections.remove(r);
27168     },
27169
27170     // private
27171     onRowUpdated : function(v, index, r){
27172         if(this.isSelected(r)){
27173             v.onRowSelect(index);
27174         }
27175     },
27176
27177     /**
27178      * Select records.
27179      * @param {Array} records The records to select
27180      * @param {Boolean} keepExisting (optional) True to keep existing selections
27181      */
27182     selectRecords : function(records, keepExisting)
27183     {
27184         if(!keepExisting){
27185             this.clearSelections();
27186         }
27187             var ds = this.grid.store;
27188         for(var i = 0, len = records.length; i < len; i++){
27189             this.selectRow(ds.indexOf(records[i]), true);
27190         }
27191     },
27192
27193     /**
27194      * Gets the number of selected rows.
27195      * @return {Number}
27196      */
27197     getCount : function(){
27198         return this.selections.length;
27199     },
27200
27201     /**
27202      * Selects the first row in the grid.
27203      */
27204     selectFirstRow : function(){
27205         this.selectRow(0);
27206     },
27207
27208     /**
27209      * Select the last row.
27210      * @param {Boolean} keepExisting (optional) True to keep existing selections
27211      */
27212     selectLastRow : function(keepExisting){
27213         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27214         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27215     },
27216
27217     /**
27218      * Selects the row immediately following the last selected row.
27219      * @param {Boolean} keepExisting (optional) True to keep existing selections
27220      */
27221     selectNext : function(keepExisting)
27222     {
27223             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27224             this.selectRow(this.last+1, keepExisting);
27225             this.grid.getView().focusRow(this.last);
27226         }
27227     },
27228
27229     /**
27230      * Selects the row that precedes the last selected row.
27231      * @param {Boolean} keepExisting (optional) True to keep existing selections
27232      */
27233     selectPrevious : function(keepExisting){
27234         if(this.last){
27235             this.selectRow(this.last-1, keepExisting);
27236             this.grid.getView().focusRow(this.last);
27237         }
27238     },
27239
27240     /**
27241      * Returns the selected records
27242      * @return {Array} Array of selected records
27243      */
27244     getSelections : function(){
27245         return [].concat(this.selections.items);
27246     },
27247
27248     /**
27249      * Returns the first selected record.
27250      * @return {Record}
27251      */
27252     getSelected : function(){
27253         return this.selections.itemAt(0);
27254     },
27255
27256
27257     /**
27258      * Clears all selections.
27259      */
27260     clearSelections : function(fast)
27261     {
27262         if(this.locked) {
27263             return;
27264         }
27265         if(fast !== true){
27266                 var ds = this.grid.store;
27267             var s = this.selections;
27268             s.each(function(r){
27269                 this.deselectRow(ds.indexOfId(r.id));
27270             }, this);
27271             s.clear();
27272         }else{
27273             this.selections.clear();
27274         }
27275         this.last = false;
27276     },
27277
27278
27279     /**
27280      * Selects all rows.
27281      */
27282     selectAll : function(){
27283         if(this.locked) {
27284             return;
27285         }
27286         this.selections.clear();
27287         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27288             this.selectRow(i, true);
27289         }
27290     },
27291
27292     /**
27293      * Returns True if there is a selection.
27294      * @return {Boolean}
27295      */
27296     hasSelection : function(){
27297         return this.selections.length > 0;
27298     },
27299
27300     /**
27301      * Returns True if the specified row is selected.
27302      * @param {Number/Record} record The record or index of the record to check
27303      * @return {Boolean}
27304      */
27305     isSelected : function(index){
27306             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27307         return (r && this.selections.key(r.id) ? true : false);
27308     },
27309
27310     /**
27311      * Returns True if the specified record id is selected.
27312      * @param {String} id The id of record to check
27313      * @return {Boolean}
27314      */
27315     isIdSelected : function(id){
27316         return (this.selections.key(id) ? true : false);
27317     },
27318
27319
27320     // private
27321     handleMouseDBClick : function(e, t){
27322         
27323     },
27324     // private
27325     handleMouseDown : function(e, t)
27326     {
27327             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27328         if(this.isLocked() || rowIndex < 0 ){
27329             return;
27330         };
27331         if(e.shiftKey && this.last !== false){
27332             var last = this.last;
27333             this.selectRange(last, rowIndex, e.ctrlKey);
27334             this.last = last; // reset the last
27335             t.focus();
27336     
27337         }else{
27338             var isSelected = this.isSelected(rowIndex);
27339             //Roo.log("select row:" + rowIndex);
27340             if(isSelected){
27341                 this.deselectRow(rowIndex);
27342             } else {
27343                         this.selectRow(rowIndex, true);
27344             }
27345     
27346             /*
27347                 if(e.button !== 0 && isSelected){
27348                 alert('rowIndex 2: ' + rowIndex);
27349                     view.focusRow(rowIndex);
27350                 }else if(e.ctrlKey && isSelected){
27351                     this.deselectRow(rowIndex);
27352                 }else if(!isSelected){
27353                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27354                     view.focusRow(rowIndex);
27355                 }
27356             */
27357         }
27358         this.fireEvent("afterselectionchange", this);
27359     },
27360     // private
27361     handleDragableRowClick :  function(grid, rowIndex, e) 
27362     {
27363         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27364             this.selectRow(rowIndex, false);
27365             grid.view.focusRow(rowIndex);
27366              this.fireEvent("afterselectionchange", this);
27367         }
27368     },
27369     
27370     /**
27371      * Selects multiple rows.
27372      * @param {Array} rows Array of the indexes of the row to select
27373      * @param {Boolean} keepExisting (optional) True to keep existing selections
27374      */
27375     selectRows : function(rows, keepExisting){
27376         if(!keepExisting){
27377             this.clearSelections();
27378         }
27379         for(var i = 0, len = rows.length; i < len; i++){
27380             this.selectRow(rows[i], true);
27381         }
27382     },
27383
27384     /**
27385      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27386      * @param {Number} startRow The index of the first row in the range
27387      * @param {Number} endRow The index of the last row in the range
27388      * @param {Boolean} keepExisting (optional) True to retain existing selections
27389      */
27390     selectRange : function(startRow, endRow, keepExisting){
27391         if(this.locked) {
27392             return;
27393         }
27394         if(!keepExisting){
27395             this.clearSelections();
27396         }
27397         if(startRow <= endRow){
27398             for(var i = startRow; i <= endRow; i++){
27399                 this.selectRow(i, true);
27400             }
27401         }else{
27402             for(var i = startRow; i >= endRow; i--){
27403                 this.selectRow(i, true);
27404             }
27405         }
27406     },
27407
27408     /**
27409      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27410      * @param {Number} startRow The index of the first row in the range
27411      * @param {Number} endRow The index of the last row in the range
27412      */
27413     deselectRange : function(startRow, endRow, preventViewNotify){
27414         if(this.locked) {
27415             return;
27416         }
27417         for(var i = startRow; i <= endRow; i++){
27418             this.deselectRow(i, preventViewNotify);
27419         }
27420     },
27421
27422     /**
27423      * Selects a row.
27424      * @param {Number} row The index of the row to select
27425      * @param {Boolean} keepExisting (optional) True to keep existing selections
27426      */
27427     selectRow : function(index, keepExisting, preventViewNotify)
27428     {
27429             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27430             return;
27431         }
27432         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27433             if(!keepExisting || this.singleSelect){
27434                 this.clearSelections();
27435             }
27436             
27437             var r = this.grid.store.getAt(index);
27438             //console.log('selectRow - record id :' + r.id);
27439             
27440             this.selections.add(r);
27441             this.last = this.lastActive = index;
27442             if(!preventViewNotify){
27443                 var proxy = new Roo.Element(
27444                                 this.grid.getRowDom(index)
27445                 );
27446                 proxy.addClass('bg-info info');
27447             }
27448             this.fireEvent("rowselect", this, index, r);
27449             this.fireEvent("selectionchange", this);
27450         }
27451     },
27452
27453     /**
27454      * Deselects a row.
27455      * @param {Number} row The index of the row to deselect
27456      */
27457     deselectRow : function(index, preventViewNotify)
27458     {
27459         if(this.locked) {
27460             return;
27461         }
27462         if(this.last == index){
27463             this.last = false;
27464         }
27465         if(this.lastActive == index){
27466             this.lastActive = false;
27467         }
27468         
27469         var r = this.grid.store.getAt(index);
27470         if (!r) {
27471             return;
27472         }
27473         
27474         this.selections.remove(r);
27475         //.console.log('deselectRow - record id :' + r.id);
27476         if(!preventViewNotify){
27477         
27478             var proxy = new Roo.Element(
27479                 this.grid.getRowDom(index)
27480             );
27481             proxy.removeClass('bg-info info');
27482         }
27483         this.fireEvent("rowdeselect", this, index);
27484         this.fireEvent("selectionchange", this);
27485     },
27486
27487     // private
27488     restoreLast : function(){
27489         if(this._last){
27490             this.last = this._last;
27491         }
27492     },
27493
27494     // private
27495     acceptsNav : function(row, col, cm){
27496         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27497     },
27498
27499     // private
27500     onEditorKey : function(field, e){
27501         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27502         if(k == e.TAB){
27503             e.stopEvent();
27504             ed.completeEdit();
27505             if(e.shiftKey){
27506                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27507             }else{
27508                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27509             }
27510         }else if(k == e.ENTER && !e.ctrlKey){
27511             e.stopEvent();
27512             ed.completeEdit();
27513             if(e.shiftKey){
27514                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27515             }else{
27516                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27517             }
27518         }else if(k == e.ESC){
27519             ed.cancelEdit();
27520         }
27521         if(newCell){
27522             g.startEditing(newCell[0], newCell[1]);
27523         }
27524     }
27525 });
27526 /*
27527  * Based on:
27528  * Ext JS Library 1.1.1
27529  * Copyright(c) 2006-2007, Ext JS, LLC.
27530  *
27531  * Originally Released Under LGPL - original licence link has changed is not relivant.
27532  *
27533  * Fork - LGPL
27534  * <script type="text/javascript">
27535  */
27536  
27537 /**
27538  * @class Roo.bootstrap.PagingToolbar
27539  * @extends Roo.bootstrap.NavSimplebar
27540  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27541  * @constructor
27542  * Create a new PagingToolbar
27543  * @param {Object} config The config object
27544  * @param {Roo.data.Store} store
27545  */
27546 Roo.bootstrap.PagingToolbar = function(config)
27547 {
27548     // old args format still supported... - xtype is prefered..
27549         // created from xtype...
27550     
27551     this.ds = config.dataSource;
27552     
27553     if (config.store && !this.ds) {
27554         this.store= Roo.factory(config.store, Roo.data);
27555         this.ds = this.store;
27556         this.ds.xmodule = this.xmodule || false;
27557     }
27558     
27559     this.toolbarItems = [];
27560     if (config.items) {
27561         this.toolbarItems = config.items;
27562     }
27563     
27564     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27565     
27566     this.cursor = 0;
27567     
27568     if (this.ds) { 
27569         this.bind(this.ds);
27570     }
27571     
27572     if (Roo.bootstrap.version == 4) {
27573         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27574     } else {
27575         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27576     }
27577     
27578 };
27579
27580 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27581     /**
27582      * @cfg {Roo.data.Store} dataSource
27583      * The underlying data store providing the paged data
27584      */
27585     /**
27586      * @cfg {String/HTMLElement/Element} container
27587      * container The id or element that will contain the toolbar
27588      */
27589     /**
27590      * @cfg {Boolean} displayInfo
27591      * True to display the displayMsg (defaults to false)
27592      */
27593     /**
27594      * @cfg {Number} pageSize
27595      * The number of records to display per page (defaults to 20)
27596      */
27597     pageSize: 20,
27598     /**
27599      * @cfg {String} displayMsg
27600      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27601      */
27602     displayMsg : 'Displaying {0} - {1} of {2}',
27603     /**
27604      * @cfg {String} emptyMsg
27605      * The message to display when no records are found (defaults to "No data to display")
27606      */
27607     emptyMsg : 'No data to display',
27608     /**
27609      * Customizable piece of the default paging text (defaults to "Page")
27610      * @type String
27611      */
27612     beforePageText : "Page",
27613     /**
27614      * Customizable piece of the default paging text (defaults to "of %0")
27615      * @type String
27616      */
27617     afterPageText : "of {0}",
27618     /**
27619      * Customizable piece of the default paging text (defaults to "First Page")
27620      * @type String
27621      */
27622     firstText : "First Page",
27623     /**
27624      * Customizable piece of the default paging text (defaults to "Previous Page")
27625      * @type String
27626      */
27627     prevText : "Previous Page",
27628     /**
27629      * Customizable piece of the default paging text (defaults to "Next Page")
27630      * @type String
27631      */
27632     nextText : "Next Page",
27633     /**
27634      * Customizable piece of the default paging text (defaults to "Last Page")
27635      * @type String
27636      */
27637     lastText : "Last Page",
27638     /**
27639      * Customizable piece of the default paging text (defaults to "Refresh")
27640      * @type String
27641      */
27642     refreshText : "Refresh",
27643
27644     buttons : false,
27645     // private
27646     onRender : function(ct, position) 
27647     {
27648         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27649         this.navgroup.parentId = this.id;
27650         this.navgroup.onRender(this.el, null);
27651         // add the buttons to the navgroup
27652         
27653         if(this.displayInfo){
27654             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27655             this.displayEl = this.el.select('.x-paging-info', true).first();
27656 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27657 //            this.displayEl = navel.el.select('span',true).first();
27658         }
27659         
27660         var _this = this;
27661         
27662         if(this.buttons){
27663             Roo.each(_this.buttons, function(e){ // this might need to use render????
27664                Roo.factory(e).render(_this.el);
27665             });
27666         }
27667             
27668         Roo.each(_this.toolbarItems, function(e) {
27669             _this.navgroup.addItem(e);
27670         });
27671         
27672         
27673         this.first = this.navgroup.addItem({
27674             tooltip: this.firstText,
27675             cls: "prev btn-outline-secondary",
27676             html : ' <i class="fa fa-step-backward"></i>',
27677             disabled: true,
27678             preventDefault: true,
27679             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27680         });
27681         
27682         this.prev =  this.navgroup.addItem({
27683             tooltip: this.prevText,
27684             cls: "prev btn-outline-secondary",
27685             html : ' <i class="fa fa-backward"></i>',
27686             disabled: true,
27687             preventDefault: true,
27688             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27689         });
27690     //this.addSeparator();
27691         
27692         
27693         var field = this.navgroup.addItem( {
27694             tagtype : 'span',
27695             cls : 'x-paging-position  btn-outline-secondary',
27696              disabled: true,
27697             html : this.beforePageText  +
27698                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27699                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27700          } ); //?? escaped?
27701         
27702         this.field = field.el.select('input', true).first();
27703         this.field.on("keydown", this.onPagingKeydown, this);
27704         this.field.on("focus", function(){this.dom.select();});
27705     
27706     
27707         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27708         //this.field.setHeight(18);
27709         //this.addSeparator();
27710         this.next = this.navgroup.addItem({
27711             tooltip: this.nextText,
27712             cls: "next btn-outline-secondary",
27713             html : ' <i class="fa fa-forward"></i>',
27714             disabled: true,
27715             preventDefault: true,
27716             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27717         });
27718         this.last = this.navgroup.addItem({
27719             tooltip: this.lastText,
27720             html : ' <i class="fa fa-step-forward"></i>',
27721             cls: "next btn-outline-secondary",
27722             disabled: true,
27723             preventDefault: true,
27724             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27725         });
27726     //this.addSeparator();
27727         this.loading = this.navgroup.addItem({
27728             tooltip: this.refreshText,
27729             cls: "btn-outline-secondary",
27730             html : ' <i class="fa fa-refresh"></i>',
27731             preventDefault: true,
27732             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27733         });
27734         
27735     },
27736
27737     // private
27738     updateInfo : function(){
27739         if(this.displayEl){
27740             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27741             var msg = count == 0 ?
27742                 this.emptyMsg :
27743                 String.format(
27744                     this.displayMsg,
27745                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27746                 );
27747             this.displayEl.update(msg);
27748         }
27749     },
27750
27751     // private
27752     onLoad : function(ds, r, o)
27753     {
27754         this.cursor = o.params && o.params.start ? o.params.start : 0;
27755         
27756         var d = this.getPageData(),
27757             ap = d.activePage,
27758             ps = d.pages;
27759         
27760         
27761         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27762         this.field.dom.value = ap;
27763         this.first.setDisabled(ap == 1);
27764         this.prev.setDisabled(ap == 1);
27765         this.next.setDisabled(ap == ps);
27766         this.last.setDisabled(ap == ps);
27767         this.loading.enable();
27768         this.updateInfo();
27769     },
27770
27771     // private
27772     getPageData : function(){
27773         var total = this.ds.getTotalCount();
27774         return {
27775             total : total,
27776             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27777             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27778         };
27779     },
27780
27781     // private
27782     onLoadError : function(){
27783         this.loading.enable();
27784     },
27785
27786     // private
27787     onPagingKeydown : function(e){
27788         var k = e.getKey();
27789         var d = this.getPageData();
27790         if(k == e.RETURN){
27791             var v = this.field.dom.value, pageNum;
27792             if(!v || isNaN(pageNum = parseInt(v, 10))){
27793                 this.field.dom.value = d.activePage;
27794                 return;
27795             }
27796             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27797             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27798             e.stopEvent();
27799         }
27800         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))
27801         {
27802           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27803           this.field.dom.value = pageNum;
27804           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27805           e.stopEvent();
27806         }
27807         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27808         {
27809           var v = this.field.dom.value, pageNum; 
27810           var increment = (e.shiftKey) ? 10 : 1;
27811           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27812                 increment *= -1;
27813           }
27814           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27815             this.field.dom.value = d.activePage;
27816             return;
27817           }
27818           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27819           {
27820             this.field.dom.value = parseInt(v, 10) + increment;
27821             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27822             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27823           }
27824           e.stopEvent();
27825         }
27826     },
27827
27828     // private
27829     beforeLoad : function(){
27830         if(this.loading){
27831             this.loading.disable();
27832         }
27833     },
27834
27835     // private
27836     onClick : function(which){
27837         
27838         var ds = this.ds;
27839         if (!ds) {
27840             return;
27841         }
27842         
27843         switch(which){
27844             case "first":
27845                 ds.load({params:{start: 0, limit: this.pageSize}});
27846             break;
27847             case "prev":
27848                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27849             break;
27850             case "next":
27851                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27852             break;
27853             case "last":
27854                 var total = ds.getTotalCount();
27855                 var extra = total % this.pageSize;
27856                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27857                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27858             break;
27859             case "refresh":
27860                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27861             break;
27862         }
27863     },
27864
27865     /**
27866      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27867      * @param {Roo.data.Store} store The data store to unbind
27868      */
27869     unbind : function(ds){
27870         ds.un("beforeload", this.beforeLoad, this);
27871         ds.un("load", this.onLoad, this);
27872         ds.un("loadexception", this.onLoadError, this);
27873         ds.un("remove", this.updateInfo, this);
27874         ds.un("add", this.updateInfo, this);
27875         this.ds = undefined;
27876     },
27877
27878     /**
27879      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27880      * @param {Roo.data.Store} store The data store to bind
27881      */
27882     bind : function(ds){
27883         ds.on("beforeload", this.beforeLoad, this);
27884         ds.on("load", this.onLoad, this);
27885         ds.on("loadexception", this.onLoadError, this);
27886         ds.on("remove", this.updateInfo, this);
27887         ds.on("add", this.updateInfo, this);
27888         this.ds = ds;
27889     }
27890 });/*
27891  * - LGPL
27892  *
27893  * element
27894  * 
27895  */
27896
27897 /**
27898  * @class Roo.bootstrap.MessageBar
27899  * @extends Roo.bootstrap.Component
27900  * Bootstrap MessageBar class
27901  * @cfg {String} html contents of the MessageBar
27902  * @cfg {String} weight (info | success | warning | danger) default info
27903  * @cfg {String} beforeClass insert the bar before the given class
27904  * @cfg {Boolean} closable (true | false) default false
27905  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27906  * 
27907  * @constructor
27908  * Create a new Element
27909  * @param {Object} config The config object
27910  */
27911
27912 Roo.bootstrap.MessageBar = function(config){
27913     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27914 };
27915
27916 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27917     
27918     html: '',
27919     weight: 'info',
27920     closable: false,
27921     fixed: false,
27922     beforeClass: 'bootstrap-sticky-wrap',
27923     
27924     getAutoCreate : function(){
27925         
27926         var cfg = {
27927             tag: 'div',
27928             cls: 'alert alert-dismissable alert-' + this.weight,
27929             cn: [
27930                 {
27931                     tag: 'span',
27932                     cls: 'message',
27933                     html: this.html || ''
27934                 }
27935             ]
27936         };
27937         
27938         if(this.fixed){
27939             cfg.cls += ' alert-messages-fixed';
27940         }
27941         
27942         if(this.closable){
27943             cfg.cn.push({
27944                 tag: 'button',
27945                 cls: 'close',
27946                 html: 'x'
27947             });
27948         }
27949         
27950         return cfg;
27951     },
27952     
27953     onRender : function(ct, position)
27954     {
27955         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27956         
27957         if(!this.el){
27958             var cfg = Roo.apply({},  this.getAutoCreate());
27959             cfg.id = Roo.id();
27960             
27961             if (this.cls) {
27962                 cfg.cls += ' ' + this.cls;
27963             }
27964             if (this.style) {
27965                 cfg.style = this.style;
27966             }
27967             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27968             
27969             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27970         }
27971         
27972         this.el.select('>button.close').on('click', this.hide, this);
27973         
27974     },
27975     
27976     show : function()
27977     {
27978         if (!this.rendered) {
27979             this.render();
27980         }
27981         
27982         this.el.show();
27983         
27984         this.fireEvent('show', this);
27985         
27986     },
27987     
27988     hide : function()
27989     {
27990         if (!this.rendered) {
27991             this.render();
27992         }
27993         
27994         this.el.hide();
27995         
27996         this.fireEvent('hide', this);
27997     },
27998     
27999     update : function()
28000     {
28001 //        var e = this.el.dom.firstChild;
28002 //        
28003 //        if(this.closable){
28004 //            e = e.nextSibling;
28005 //        }
28006 //        
28007 //        e.data = this.html || '';
28008
28009         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28010     }
28011    
28012 });
28013
28014  
28015
28016      /*
28017  * - LGPL
28018  *
28019  * Graph
28020  * 
28021  */
28022
28023
28024 /**
28025  * @class Roo.bootstrap.Graph
28026  * @extends Roo.bootstrap.Component
28027  * Bootstrap Graph class
28028 > Prameters
28029  -sm {number} sm 4
28030  -md {number} md 5
28031  @cfg {String} graphtype  bar | vbar | pie
28032  @cfg {number} g_x coodinator | centre x (pie)
28033  @cfg {number} g_y coodinator | centre y (pie)
28034  @cfg {number} g_r radius (pie)
28035  @cfg {number} g_height height of the chart (respected by all elements in the set)
28036  @cfg {number} g_width width of the chart (respected by all elements in the set)
28037  @cfg {Object} title The title of the chart
28038     
28039  -{Array}  values
28040  -opts (object) options for the chart 
28041      o {
28042      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28043      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28044      o vgutter (number)
28045      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.
28046      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28047      o to
28048      o stretch (boolean)
28049      o }
28050  -opts (object) options for the pie
28051      o{
28052      o cut
28053      o startAngle (number)
28054      o endAngle (number)
28055      } 
28056  *
28057  * @constructor
28058  * Create a new Input
28059  * @param {Object} config The config object
28060  */
28061
28062 Roo.bootstrap.Graph = function(config){
28063     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28064     
28065     this.addEvents({
28066         // img events
28067         /**
28068          * @event click
28069          * The img click event for the img.
28070          * @param {Roo.EventObject} e
28071          */
28072         "click" : true
28073     });
28074 };
28075
28076 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28077     
28078     sm: 4,
28079     md: 5,
28080     graphtype: 'bar',
28081     g_height: 250,
28082     g_width: 400,
28083     g_x: 50,
28084     g_y: 50,
28085     g_r: 30,
28086     opts:{
28087         //g_colors: this.colors,
28088         g_type: 'soft',
28089         g_gutter: '20%'
28090
28091     },
28092     title : false,
28093
28094     getAutoCreate : function(){
28095         
28096         var cfg = {
28097             tag: 'div',
28098             html : null
28099         };
28100         
28101         
28102         return  cfg;
28103     },
28104
28105     onRender : function(ct,position){
28106         
28107         
28108         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28109         
28110         if (typeof(Raphael) == 'undefined') {
28111             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28112             return;
28113         }
28114         
28115         this.raphael = Raphael(this.el.dom);
28116         
28117                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28118                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28119                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28120                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28121                 /*
28122                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28123                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28124                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28125                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28126                 
28127                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28128                 r.barchart(330, 10, 300, 220, data1);
28129                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28130                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28131                 */
28132                 
28133                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28134                 // r.barchart(30, 30, 560, 250,  xdata, {
28135                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28136                 //     axis : "0 0 1 1",
28137                 //     axisxlabels :  xdata
28138                 //     //yvalues : cols,
28139                    
28140                 // });
28141 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28142 //        
28143 //        this.load(null,xdata,{
28144 //                axis : "0 0 1 1",
28145 //                axisxlabels :  xdata
28146 //                });
28147
28148     },
28149
28150     load : function(graphtype,xdata,opts)
28151     {
28152         this.raphael.clear();
28153         if(!graphtype) {
28154             graphtype = this.graphtype;
28155         }
28156         if(!opts){
28157             opts = this.opts;
28158         }
28159         var r = this.raphael,
28160             fin = function () {
28161                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28162             },
28163             fout = function () {
28164                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28165             },
28166             pfin = function() {
28167                 this.sector.stop();
28168                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28169
28170                 if (this.label) {
28171                     this.label[0].stop();
28172                     this.label[0].attr({ r: 7.5 });
28173                     this.label[1].attr({ "font-weight": 800 });
28174                 }
28175             },
28176             pfout = function() {
28177                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28178
28179                 if (this.label) {
28180                     this.label[0].animate({ r: 5 }, 500, "bounce");
28181                     this.label[1].attr({ "font-weight": 400 });
28182                 }
28183             };
28184
28185         switch(graphtype){
28186             case 'bar':
28187                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28188                 break;
28189             case 'hbar':
28190                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28191                 break;
28192             case 'pie':
28193 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28194 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28195 //            
28196                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28197                 
28198                 break;
28199
28200         }
28201         
28202         if(this.title){
28203             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28204         }
28205         
28206     },
28207     
28208     setTitle: function(o)
28209     {
28210         this.title = o;
28211     },
28212     
28213     initEvents: function() {
28214         
28215         if(!this.href){
28216             this.el.on('click', this.onClick, this);
28217         }
28218     },
28219     
28220     onClick : function(e)
28221     {
28222         Roo.log('img onclick');
28223         this.fireEvent('click', this, e);
28224     }
28225    
28226 });
28227
28228  
28229 /*
28230  * - LGPL
28231  *
28232  * numberBox
28233  * 
28234  */
28235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28236
28237 /**
28238  * @class Roo.bootstrap.dash.NumberBox
28239  * @extends Roo.bootstrap.Component
28240  * Bootstrap NumberBox class
28241  * @cfg {String} headline Box headline
28242  * @cfg {String} content Box content
28243  * @cfg {String} icon Box icon
28244  * @cfg {String} footer Footer text
28245  * @cfg {String} fhref Footer href
28246  * 
28247  * @constructor
28248  * Create a new NumberBox
28249  * @param {Object} config The config object
28250  */
28251
28252
28253 Roo.bootstrap.dash.NumberBox = function(config){
28254     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28255     
28256 };
28257
28258 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28259     
28260     headline : '',
28261     content : '',
28262     icon : '',
28263     footer : '',
28264     fhref : '',
28265     ficon : '',
28266     
28267     getAutoCreate : function(){
28268         
28269         var cfg = {
28270             tag : 'div',
28271             cls : 'small-box ',
28272             cn : [
28273                 {
28274                     tag : 'div',
28275                     cls : 'inner',
28276                     cn :[
28277                         {
28278                             tag : 'h3',
28279                             cls : 'roo-headline',
28280                             html : this.headline
28281                         },
28282                         {
28283                             tag : 'p',
28284                             cls : 'roo-content',
28285                             html : this.content
28286                         }
28287                     ]
28288                 }
28289             ]
28290         };
28291         
28292         if(this.icon){
28293             cfg.cn.push({
28294                 tag : 'div',
28295                 cls : 'icon',
28296                 cn :[
28297                     {
28298                         tag : 'i',
28299                         cls : 'ion ' + this.icon
28300                     }
28301                 ]
28302             });
28303         }
28304         
28305         if(this.footer){
28306             var footer = {
28307                 tag : 'a',
28308                 cls : 'small-box-footer',
28309                 href : this.fhref || '#',
28310                 html : this.footer
28311             };
28312             
28313             cfg.cn.push(footer);
28314             
28315         }
28316         
28317         return  cfg;
28318     },
28319
28320     onRender : function(ct,position){
28321         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28322
28323
28324        
28325                 
28326     },
28327
28328     setHeadline: function (value)
28329     {
28330         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28331     },
28332     
28333     setFooter: function (value, href)
28334     {
28335         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28336         
28337         if(href){
28338             this.el.select('a.small-box-footer',true).first().attr('href', href);
28339         }
28340         
28341     },
28342
28343     setContent: function (value)
28344     {
28345         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28346     },
28347
28348     initEvents: function() 
28349     {   
28350         
28351     }
28352     
28353 });
28354
28355  
28356 /*
28357  * - LGPL
28358  *
28359  * TabBox
28360  * 
28361  */
28362 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28363
28364 /**
28365  * @class Roo.bootstrap.dash.TabBox
28366  * @extends Roo.bootstrap.Component
28367  * Bootstrap TabBox class
28368  * @cfg {String} title Title of the TabBox
28369  * @cfg {String} icon Icon of the TabBox
28370  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28371  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28372  * 
28373  * @constructor
28374  * Create a new TabBox
28375  * @param {Object} config The config object
28376  */
28377
28378
28379 Roo.bootstrap.dash.TabBox = function(config){
28380     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28381     this.addEvents({
28382         // raw events
28383         /**
28384          * @event addpane
28385          * When a pane is added
28386          * @param {Roo.bootstrap.dash.TabPane} pane
28387          */
28388         "addpane" : true,
28389         /**
28390          * @event activatepane
28391          * When a pane is activated
28392          * @param {Roo.bootstrap.dash.TabPane} pane
28393          */
28394         "activatepane" : true
28395         
28396          
28397     });
28398     
28399     this.panes = [];
28400 };
28401
28402 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28403
28404     title : '',
28405     icon : false,
28406     showtabs : true,
28407     tabScrollable : false,
28408     
28409     getChildContainer : function()
28410     {
28411         return this.el.select('.tab-content', true).first();
28412     },
28413     
28414     getAutoCreate : function(){
28415         
28416         var header = {
28417             tag: 'li',
28418             cls: 'pull-left header',
28419             html: this.title,
28420             cn : []
28421         };
28422         
28423         if(this.icon){
28424             header.cn.push({
28425                 tag: 'i',
28426                 cls: 'fa ' + this.icon
28427             });
28428         }
28429         
28430         var h = {
28431             tag: 'ul',
28432             cls: 'nav nav-tabs pull-right',
28433             cn: [
28434                 header
28435             ]
28436         };
28437         
28438         if(this.tabScrollable){
28439             h = {
28440                 tag: 'div',
28441                 cls: 'tab-header',
28442                 cn: [
28443                     {
28444                         tag: 'ul',
28445                         cls: 'nav nav-tabs pull-right',
28446                         cn: [
28447                             header
28448                         ]
28449                     }
28450                 ]
28451             };
28452         }
28453         
28454         var cfg = {
28455             tag: 'div',
28456             cls: 'nav-tabs-custom',
28457             cn: [
28458                 h,
28459                 {
28460                     tag: 'div',
28461                     cls: 'tab-content no-padding',
28462                     cn: []
28463                 }
28464             ]
28465         };
28466
28467         return  cfg;
28468     },
28469     initEvents : function()
28470     {
28471         //Roo.log('add add pane handler');
28472         this.on('addpane', this.onAddPane, this);
28473     },
28474      /**
28475      * Updates the box title
28476      * @param {String} html to set the title to.
28477      */
28478     setTitle : function(value)
28479     {
28480         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28481     },
28482     onAddPane : function(pane)
28483     {
28484         this.panes.push(pane);
28485         //Roo.log('addpane');
28486         //Roo.log(pane);
28487         // tabs are rendere left to right..
28488         if(!this.showtabs){
28489             return;
28490         }
28491         
28492         var ctr = this.el.select('.nav-tabs', true).first();
28493          
28494          
28495         var existing = ctr.select('.nav-tab',true);
28496         var qty = existing.getCount();;
28497         
28498         
28499         var tab = ctr.createChild({
28500             tag : 'li',
28501             cls : 'nav-tab' + (qty ? '' : ' active'),
28502             cn : [
28503                 {
28504                     tag : 'a',
28505                     href:'#',
28506                     html : pane.title
28507                 }
28508             ]
28509         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28510         pane.tab = tab;
28511         
28512         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28513         if (!qty) {
28514             pane.el.addClass('active');
28515         }
28516         
28517                 
28518     },
28519     onTabClick : function(ev,un,ob,pane)
28520     {
28521         //Roo.log('tab - prev default');
28522         ev.preventDefault();
28523         
28524         
28525         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28526         pane.tab.addClass('active');
28527         //Roo.log(pane.title);
28528         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28529         // technically we should have a deactivate event.. but maybe add later.
28530         // and it should not de-activate the selected tab...
28531         this.fireEvent('activatepane', pane);
28532         pane.el.addClass('active');
28533         pane.fireEvent('activate');
28534         
28535         
28536     },
28537     
28538     getActivePane : function()
28539     {
28540         var r = false;
28541         Roo.each(this.panes, function(p) {
28542             if(p.el.hasClass('active')){
28543                 r = p;
28544                 return false;
28545             }
28546             
28547             return;
28548         });
28549         
28550         return r;
28551     }
28552     
28553     
28554 });
28555
28556  
28557 /*
28558  * - LGPL
28559  *
28560  * Tab pane
28561  * 
28562  */
28563 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28564 /**
28565  * @class Roo.bootstrap.TabPane
28566  * @extends Roo.bootstrap.Component
28567  * Bootstrap TabPane class
28568  * @cfg {Boolean} active (false | true) Default false
28569  * @cfg {String} title title of panel
28570
28571  * 
28572  * @constructor
28573  * Create a new TabPane
28574  * @param {Object} config The config object
28575  */
28576
28577 Roo.bootstrap.dash.TabPane = function(config){
28578     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28579     
28580     this.addEvents({
28581         // raw events
28582         /**
28583          * @event activate
28584          * When a pane is activated
28585          * @param {Roo.bootstrap.dash.TabPane} pane
28586          */
28587         "activate" : true
28588          
28589     });
28590 };
28591
28592 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28593     
28594     active : false,
28595     title : '',
28596     
28597     // the tabBox that this is attached to.
28598     tab : false,
28599      
28600     getAutoCreate : function() 
28601     {
28602         var cfg = {
28603             tag: 'div',
28604             cls: 'tab-pane'
28605         };
28606         
28607         if(this.active){
28608             cfg.cls += ' active';
28609         }
28610         
28611         return cfg;
28612     },
28613     initEvents  : function()
28614     {
28615         //Roo.log('trigger add pane handler');
28616         this.parent().fireEvent('addpane', this)
28617     },
28618     
28619      /**
28620      * Updates the tab title 
28621      * @param {String} html to set the title to.
28622      */
28623     setTitle: function(str)
28624     {
28625         if (!this.tab) {
28626             return;
28627         }
28628         this.title = str;
28629         this.tab.select('a', true).first().dom.innerHTML = str;
28630         
28631     }
28632     
28633     
28634     
28635 });
28636
28637  
28638
28639
28640  /*
28641  * - LGPL
28642  *
28643  * menu
28644  * 
28645  */
28646 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28647
28648 /**
28649  * @class Roo.bootstrap.menu.Menu
28650  * @extends Roo.bootstrap.Component
28651  * Bootstrap Menu class - container for Menu
28652  * @cfg {String} html Text of the menu
28653  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28654  * @cfg {String} icon Font awesome icon
28655  * @cfg {String} pos Menu align to (top | bottom) default bottom
28656  * 
28657  * 
28658  * @constructor
28659  * Create a new Menu
28660  * @param {Object} config The config object
28661  */
28662
28663
28664 Roo.bootstrap.menu.Menu = function(config){
28665     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28666     
28667     this.addEvents({
28668         /**
28669          * @event beforeshow
28670          * Fires before this menu is displayed
28671          * @param {Roo.bootstrap.menu.Menu} this
28672          */
28673         beforeshow : true,
28674         /**
28675          * @event beforehide
28676          * Fires before this menu is hidden
28677          * @param {Roo.bootstrap.menu.Menu} this
28678          */
28679         beforehide : true,
28680         /**
28681          * @event show
28682          * Fires after this menu is displayed
28683          * @param {Roo.bootstrap.menu.Menu} this
28684          */
28685         show : true,
28686         /**
28687          * @event hide
28688          * Fires after this menu is hidden
28689          * @param {Roo.bootstrap.menu.Menu} this
28690          */
28691         hide : true,
28692         /**
28693          * @event click
28694          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28695          * @param {Roo.bootstrap.menu.Menu} this
28696          * @param {Roo.EventObject} e
28697          */
28698         click : true
28699     });
28700     
28701 };
28702
28703 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28704     
28705     submenu : false,
28706     html : '',
28707     weight : 'default',
28708     icon : false,
28709     pos : 'bottom',
28710     
28711     
28712     getChildContainer : function() {
28713         if(this.isSubMenu){
28714             return this.el;
28715         }
28716         
28717         return this.el.select('ul.dropdown-menu', true).first();  
28718     },
28719     
28720     getAutoCreate : function()
28721     {
28722         var text = [
28723             {
28724                 tag : 'span',
28725                 cls : 'roo-menu-text',
28726                 html : this.html
28727             }
28728         ];
28729         
28730         if(this.icon){
28731             text.unshift({
28732                 tag : 'i',
28733                 cls : 'fa ' + this.icon
28734             })
28735         }
28736         
28737         
28738         var cfg = {
28739             tag : 'div',
28740             cls : 'btn-group',
28741             cn : [
28742                 {
28743                     tag : 'button',
28744                     cls : 'dropdown-button btn btn-' + this.weight,
28745                     cn : text
28746                 },
28747                 {
28748                     tag : 'button',
28749                     cls : 'dropdown-toggle btn btn-' + this.weight,
28750                     cn : [
28751                         {
28752                             tag : 'span',
28753                             cls : 'caret'
28754                         }
28755                     ]
28756                 },
28757                 {
28758                     tag : 'ul',
28759                     cls : 'dropdown-menu'
28760                 }
28761             ]
28762             
28763         };
28764         
28765         if(this.pos == 'top'){
28766             cfg.cls += ' dropup';
28767         }
28768         
28769         if(this.isSubMenu){
28770             cfg = {
28771                 tag : 'ul',
28772                 cls : 'dropdown-menu'
28773             }
28774         }
28775         
28776         return cfg;
28777     },
28778     
28779     onRender : function(ct, position)
28780     {
28781         this.isSubMenu = ct.hasClass('dropdown-submenu');
28782         
28783         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28784     },
28785     
28786     initEvents : function() 
28787     {
28788         if(this.isSubMenu){
28789             return;
28790         }
28791         
28792         this.hidden = true;
28793         
28794         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28795         this.triggerEl.on('click', this.onTriggerPress, this);
28796         
28797         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28798         this.buttonEl.on('click', this.onClick, this);
28799         
28800     },
28801     
28802     list : function()
28803     {
28804         if(this.isSubMenu){
28805             return this.el;
28806         }
28807         
28808         return this.el.select('ul.dropdown-menu', true).first();
28809     },
28810     
28811     onClick : function(e)
28812     {
28813         this.fireEvent("click", this, e);
28814     },
28815     
28816     onTriggerPress  : function(e)
28817     {   
28818         if (this.isVisible()) {
28819             this.hide();
28820         } else {
28821             this.show();
28822         }
28823     },
28824     
28825     isVisible : function(){
28826         return !this.hidden;
28827     },
28828     
28829     show : function()
28830     {
28831         this.fireEvent("beforeshow", this);
28832         
28833         this.hidden = false;
28834         this.el.addClass('open');
28835         
28836         Roo.get(document).on("mouseup", this.onMouseUp, this);
28837         
28838         this.fireEvent("show", this);
28839         
28840         
28841     },
28842     
28843     hide : function()
28844     {
28845         this.fireEvent("beforehide", this);
28846         
28847         this.hidden = true;
28848         this.el.removeClass('open');
28849         
28850         Roo.get(document).un("mouseup", this.onMouseUp);
28851         
28852         this.fireEvent("hide", this);
28853     },
28854     
28855     onMouseUp : function()
28856     {
28857         this.hide();
28858     }
28859     
28860 });
28861
28862  
28863  /*
28864  * - LGPL
28865  *
28866  * menu item
28867  * 
28868  */
28869 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28870
28871 /**
28872  * @class Roo.bootstrap.menu.Item
28873  * @extends Roo.bootstrap.Component
28874  * Bootstrap MenuItem class
28875  * @cfg {Boolean} submenu (true | false) default false
28876  * @cfg {String} html text of the item
28877  * @cfg {String} href the link
28878  * @cfg {Boolean} disable (true | false) default false
28879  * @cfg {Boolean} preventDefault (true | false) default true
28880  * @cfg {String} icon Font awesome icon
28881  * @cfg {String} pos Submenu align to (left | right) default right 
28882  * 
28883  * 
28884  * @constructor
28885  * Create a new Item
28886  * @param {Object} config The config object
28887  */
28888
28889
28890 Roo.bootstrap.menu.Item = function(config){
28891     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28892     this.addEvents({
28893         /**
28894          * @event mouseover
28895          * Fires when the mouse is hovering over this menu
28896          * @param {Roo.bootstrap.menu.Item} this
28897          * @param {Roo.EventObject} e
28898          */
28899         mouseover : true,
28900         /**
28901          * @event mouseout
28902          * Fires when the mouse exits this menu
28903          * @param {Roo.bootstrap.menu.Item} this
28904          * @param {Roo.EventObject} e
28905          */
28906         mouseout : true,
28907         // raw events
28908         /**
28909          * @event click
28910          * The raw click event for the entire grid.
28911          * @param {Roo.EventObject} e
28912          */
28913         click : true
28914     });
28915 };
28916
28917 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28918     
28919     submenu : false,
28920     href : '',
28921     html : '',
28922     preventDefault: true,
28923     disable : false,
28924     icon : false,
28925     pos : 'right',
28926     
28927     getAutoCreate : function()
28928     {
28929         var text = [
28930             {
28931                 tag : 'span',
28932                 cls : 'roo-menu-item-text',
28933                 html : this.html
28934             }
28935         ];
28936         
28937         if(this.icon){
28938             text.unshift({
28939                 tag : 'i',
28940                 cls : 'fa ' + this.icon
28941             })
28942         }
28943         
28944         var cfg = {
28945             tag : 'li',
28946             cn : [
28947                 {
28948                     tag : 'a',
28949                     href : this.href || '#',
28950                     cn : text
28951                 }
28952             ]
28953         };
28954         
28955         if(this.disable){
28956             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28957         }
28958         
28959         if(this.submenu){
28960             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28961             
28962             if(this.pos == 'left'){
28963                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28964             }
28965         }
28966         
28967         return cfg;
28968     },
28969     
28970     initEvents : function() 
28971     {
28972         this.el.on('mouseover', this.onMouseOver, this);
28973         this.el.on('mouseout', this.onMouseOut, this);
28974         
28975         this.el.select('a', true).first().on('click', this.onClick, this);
28976         
28977     },
28978     
28979     onClick : function(e)
28980     {
28981         if(this.preventDefault){
28982             e.preventDefault();
28983         }
28984         
28985         this.fireEvent("click", this, e);
28986     },
28987     
28988     onMouseOver : function(e)
28989     {
28990         if(this.submenu && this.pos == 'left'){
28991             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28992         }
28993         
28994         this.fireEvent("mouseover", this, e);
28995     },
28996     
28997     onMouseOut : function(e)
28998     {
28999         this.fireEvent("mouseout", this, e);
29000     }
29001 });
29002
29003  
29004
29005  /*
29006  * - LGPL
29007  *
29008  * menu separator
29009  * 
29010  */
29011 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29012
29013 /**
29014  * @class Roo.bootstrap.menu.Separator
29015  * @extends Roo.bootstrap.Component
29016  * Bootstrap Separator class
29017  * 
29018  * @constructor
29019  * Create a new Separator
29020  * @param {Object} config The config object
29021  */
29022
29023
29024 Roo.bootstrap.menu.Separator = function(config){
29025     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29026 };
29027
29028 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29029     
29030     getAutoCreate : function(){
29031         var cfg = {
29032             tag : 'li',
29033             cls: 'dropdown-divider divider'
29034         };
29035         
29036         return cfg;
29037     }
29038    
29039 });
29040
29041  
29042
29043  /*
29044  * - LGPL
29045  *
29046  * Tooltip
29047  * 
29048  */
29049
29050 /**
29051  * @class Roo.bootstrap.Tooltip
29052  * Bootstrap Tooltip class
29053  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29054  * to determine which dom element triggers the tooltip.
29055  * 
29056  * It needs to add support for additional attributes like tooltip-position
29057  * 
29058  * @constructor
29059  * Create a new Toolti
29060  * @param {Object} config The config object
29061  */
29062
29063 Roo.bootstrap.Tooltip = function(config){
29064     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29065     
29066     this.alignment = Roo.bootstrap.Tooltip.alignment;
29067     
29068     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29069         this.alignment = config.alignment;
29070     }
29071     
29072 };
29073
29074 Roo.apply(Roo.bootstrap.Tooltip, {
29075     /**
29076      * @function init initialize tooltip monitoring.
29077      * @static
29078      */
29079     currentEl : false,
29080     currentTip : false,
29081     currentRegion : false,
29082     
29083     //  init : delay?
29084     
29085     init : function()
29086     {
29087         Roo.get(document).on('mouseover', this.enter ,this);
29088         Roo.get(document).on('mouseout', this.leave, this);
29089          
29090         
29091         this.currentTip = new Roo.bootstrap.Tooltip();
29092     },
29093     
29094     enter : function(ev)
29095     {
29096         var dom = ev.getTarget();
29097         
29098         //Roo.log(['enter',dom]);
29099         var el = Roo.fly(dom);
29100         if (this.currentEl) {
29101             //Roo.log(dom);
29102             //Roo.log(this.currentEl);
29103             //Roo.log(this.currentEl.contains(dom));
29104             if (this.currentEl == el) {
29105                 return;
29106             }
29107             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29108                 return;
29109             }
29110
29111         }
29112         
29113         if (this.currentTip.el) {
29114             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29115         }    
29116         //Roo.log(ev);
29117         
29118         if(!el || el.dom == document){
29119             return;
29120         }
29121         
29122         var bindEl = el; 
29123         var pel = false;
29124         if (!el.attr('tooltip')) {
29125             pel = el.findParent("[tooltip]");
29126             if (pel) {
29127                 bindEl = Roo.get(pel);
29128             }
29129         }
29130         
29131        
29132         
29133         // you can not look for children, as if el is the body.. then everythign is the child..
29134         if (!pel && !el.attr('tooltip')) { //
29135             if (!el.select("[tooltip]").elements.length) {
29136                 return;
29137             }
29138             // is the mouse over this child...?
29139             bindEl = el.select("[tooltip]").first();
29140             var xy = ev.getXY();
29141             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29142                 //Roo.log("not in region.");
29143                 return;
29144             }
29145             //Roo.log("child element over..");
29146             
29147         }
29148         this.currentEl = el;
29149         this.currentTip.bind(bindEl);
29150         this.currentRegion = Roo.lib.Region.getRegion(dom);
29151         this.currentTip.enter();
29152         
29153     },
29154     leave : function(ev)
29155     {
29156         var dom = ev.getTarget();
29157         //Roo.log(['leave',dom]);
29158         if (!this.currentEl) {
29159             return;
29160         }
29161         
29162         
29163         if (dom != this.currentEl.dom) {
29164             return;
29165         }
29166         var xy = ev.getXY();
29167         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29168             return;
29169         }
29170         // only activate leave if mouse cursor is outside... bounding box..
29171         
29172         
29173         
29174         
29175         if (this.currentTip) {
29176             this.currentTip.leave();
29177         }
29178         //Roo.log('clear currentEl');
29179         this.currentEl = false;
29180         
29181         
29182     },
29183     alignment : {
29184         'left' : ['r-l', [-2,0], 'right'],
29185         'right' : ['l-r', [2,0], 'left'],
29186         'bottom' : ['t-b', [0,2], 'top'],
29187         'top' : [ 'b-t', [0,-2], 'bottom']
29188     }
29189     
29190 });
29191
29192
29193 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29194     
29195     
29196     bindEl : false,
29197     
29198     delay : null, // can be { show : 300 , hide: 500}
29199     
29200     timeout : null,
29201     
29202     hoverState : null, //???
29203     
29204     placement : 'bottom', 
29205     
29206     alignment : false,
29207     
29208     getAutoCreate : function(){
29209     
29210         var cfg = {
29211            cls : 'tooltip',   
29212            role : 'tooltip',
29213            cn : [
29214                 {
29215                     cls : 'tooltip-arrow arrow'
29216                 },
29217                 {
29218                     cls : 'tooltip-inner'
29219                 }
29220            ]
29221         };
29222         
29223         return cfg;
29224     },
29225     bind : function(el)
29226     {
29227         this.bindEl = el;
29228     },
29229     
29230     initEvents : function()
29231     {
29232         this.arrowEl = this.el.select('.arrow', true).first();
29233         this.innerEl = this.el.select('.tooltip-inner', true).first();
29234     },
29235     
29236     enter : function () {
29237        
29238         if (this.timeout != null) {
29239             clearTimeout(this.timeout);
29240         }
29241         
29242         this.hoverState = 'in';
29243          //Roo.log("enter - show");
29244         if (!this.delay || !this.delay.show) {
29245             this.show();
29246             return;
29247         }
29248         var _t = this;
29249         this.timeout = setTimeout(function () {
29250             if (_t.hoverState == 'in') {
29251                 _t.show();
29252             }
29253         }, this.delay.show);
29254     },
29255     leave : function()
29256     {
29257         clearTimeout(this.timeout);
29258     
29259         this.hoverState = 'out';
29260          if (!this.delay || !this.delay.hide) {
29261             this.hide();
29262             return;
29263         }
29264        
29265         var _t = this;
29266         this.timeout = setTimeout(function () {
29267             //Roo.log("leave - timeout");
29268             
29269             if (_t.hoverState == 'out') {
29270                 _t.hide();
29271                 Roo.bootstrap.Tooltip.currentEl = false;
29272             }
29273         }, delay);
29274     },
29275     
29276     show : function (msg)
29277     {
29278         if (!this.el) {
29279             this.render(document.body);
29280         }
29281         // set content.
29282         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29283         
29284         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29285         
29286         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29287         
29288         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29289                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29290         
29291         var placement = typeof this.placement == 'function' ?
29292             this.placement.call(this, this.el, on_el) :
29293             this.placement;
29294             
29295         var autoToken = /\s?auto?\s?/i;
29296         var autoPlace = autoToken.test(placement);
29297         if (autoPlace) {
29298             placement = placement.replace(autoToken, '') || 'top';
29299         }
29300         
29301         //this.el.detach()
29302         //this.el.setXY([0,0]);
29303         this.el.show();
29304         //this.el.dom.style.display='block';
29305         
29306         //this.el.appendTo(on_el);
29307         
29308         var p = this.getPosition();
29309         var box = this.el.getBox();
29310         
29311         if (autoPlace) {
29312             // fixme..
29313         }
29314         
29315         var align = this.alignment[placement];
29316         
29317         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29318         
29319         if(placement == 'top' || placement == 'bottom'){
29320             if(xy[0] < 0){
29321                 placement = 'right';
29322             }
29323             
29324             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29325                 placement = 'left';
29326             }
29327             
29328             var scroll = Roo.select('body', true).first().getScroll();
29329             
29330             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29331                 placement = 'top';
29332             }
29333             
29334             align = this.alignment[placement];
29335             
29336             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29337             
29338         }
29339         
29340         var elems = document.getElementsByTagName('div');
29341         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29342         for (var i = 0; i < elems.length; i++) {
29343           var zindex = Number.parseInt(
29344                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29345                 10
29346           );
29347           if (zindex > highest) {
29348             highest = zindex;
29349           }
29350         }
29351         
29352         
29353         
29354         this.el.dom.style.zIndex = highest;
29355         
29356         this.el.alignTo(this.bindEl, align[0],align[1]);
29357         //var arrow = this.el.select('.arrow',true).first();
29358         //arrow.set(align[2], 
29359         
29360         this.el.addClass(placement);
29361         this.el.addClass("bs-tooltip-"+ placement);
29362         
29363         this.el.addClass('in fade show');
29364         
29365         this.hoverState = null;
29366         
29367         if (this.el.hasClass('fade')) {
29368             // fade it?
29369         }
29370         
29371         
29372         
29373         
29374         
29375     },
29376     hide : function()
29377     {
29378          
29379         if (!this.el) {
29380             return;
29381         }
29382         //this.el.setXY([0,0]);
29383         this.el.removeClass(['show', 'in']);
29384         //this.el.hide();
29385         
29386     }
29387     
29388 });
29389  
29390
29391  /*
29392  * - LGPL
29393  *
29394  * Location Picker
29395  * 
29396  */
29397
29398 /**
29399  * @class Roo.bootstrap.LocationPicker
29400  * @extends Roo.bootstrap.Component
29401  * Bootstrap LocationPicker class
29402  * @cfg {Number} latitude Position when init default 0
29403  * @cfg {Number} longitude Position when init default 0
29404  * @cfg {Number} zoom default 15
29405  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29406  * @cfg {Boolean} mapTypeControl default false
29407  * @cfg {Boolean} disableDoubleClickZoom default false
29408  * @cfg {Boolean} scrollwheel default true
29409  * @cfg {Boolean} streetViewControl default false
29410  * @cfg {Number} radius default 0
29411  * @cfg {String} locationName
29412  * @cfg {Boolean} draggable default true
29413  * @cfg {Boolean} enableAutocomplete default false
29414  * @cfg {Boolean} enableReverseGeocode default true
29415  * @cfg {String} markerTitle
29416  * 
29417  * @constructor
29418  * Create a new LocationPicker
29419  * @param {Object} config The config object
29420  */
29421
29422
29423 Roo.bootstrap.LocationPicker = function(config){
29424     
29425     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29426     
29427     this.addEvents({
29428         /**
29429          * @event initial
29430          * Fires when the picker initialized.
29431          * @param {Roo.bootstrap.LocationPicker} this
29432          * @param {Google Location} location
29433          */
29434         initial : true,
29435         /**
29436          * @event positionchanged
29437          * Fires when the picker position changed.
29438          * @param {Roo.bootstrap.LocationPicker} this
29439          * @param {Google Location} location
29440          */
29441         positionchanged : true,
29442         /**
29443          * @event resize
29444          * Fires when the map resize.
29445          * @param {Roo.bootstrap.LocationPicker} this
29446          */
29447         resize : true,
29448         /**
29449          * @event show
29450          * Fires when the map show.
29451          * @param {Roo.bootstrap.LocationPicker} this
29452          */
29453         show : true,
29454         /**
29455          * @event hide
29456          * Fires when the map hide.
29457          * @param {Roo.bootstrap.LocationPicker} this
29458          */
29459         hide : true,
29460         /**
29461          * @event mapClick
29462          * Fires when click the map.
29463          * @param {Roo.bootstrap.LocationPicker} this
29464          * @param {Map event} e
29465          */
29466         mapClick : true,
29467         /**
29468          * @event mapRightClick
29469          * Fires when right click the map.
29470          * @param {Roo.bootstrap.LocationPicker} this
29471          * @param {Map event} e
29472          */
29473         mapRightClick : true,
29474         /**
29475          * @event markerClick
29476          * Fires when click the marker.
29477          * @param {Roo.bootstrap.LocationPicker} this
29478          * @param {Map event} e
29479          */
29480         markerClick : true,
29481         /**
29482          * @event markerRightClick
29483          * Fires when right click the marker.
29484          * @param {Roo.bootstrap.LocationPicker} this
29485          * @param {Map event} e
29486          */
29487         markerRightClick : true,
29488         /**
29489          * @event OverlayViewDraw
29490          * Fires when OverlayView Draw
29491          * @param {Roo.bootstrap.LocationPicker} this
29492          */
29493         OverlayViewDraw : true,
29494         /**
29495          * @event OverlayViewOnAdd
29496          * Fires when OverlayView Draw
29497          * @param {Roo.bootstrap.LocationPicker} this
29498          */
29499         OverlayViewOnAdd : true,
29500         /**
29501          * @event OverlayViewOnRemove
29502          * Fires when OverlayView Draw
29503          * @param {Roo.bootstrap.LocationPicker} this
29504          */
29505         OverlayViewOnRemove : true,
29506         /**
29507          * @event OverlayViewShow
29508          * Fires when OverlayView Draw
29509          * @param {Roo.bootstrap.LocationPicker} this
29510          * @param {Pixel} cpx
29511          */
29512         OverlayViewShow : true,
29513         /**
29514          * @event OverlayViewHide
29515          * Fires when OverlayView Draw
29516          * @param {Roo.bootstrap.LocationPicker} this
29517          */
29518         OverlayViewHide : true,
29519         /**
29520          * @event loadexception
29521          * Fires when load google lib failed.
29522          * @param {Roo.bootstrap.LocationPicker} this
29523          */
29524         loadexception : true
29525     });
29526         
29527 };
29528
29529 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29530     
29531     gMapContext: false,
29532     
29533     latitude: 0,
29534     longitude: 0,
29535     zoom: 15,
29536     mapTypeId: false,
29537     mapTypeControl: false,
29538     disableDoubleClickZoom: false,
29539     scrollwheel: true,
29540     streetViewControl: false,
29541     radius: 0,
29542     locationName: '',
29543     draggable: true,
29544     enableAutocomplete: false,
29545     enableReverseGeocode: true,
29546     markerTitle: '',
29547     
29548     getAutoCreate: function()
29549     {
29550
29551         var cfg = {
29552             tag: 'div',
29553             cls: 'roo-location-picker'
29554         };
29555         
29556         return cfg
29557     },
29558     
29559     initEvents: function(ct, position)
29560     {       
29561         if(!this.el.getWidth() || this.isApplied()){
29562             return;
29563         }
29564         
29565         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29566         
29567         this.initial();
29568     },
29569     
29570     initial: function()
29571     {
29572         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29573             this.fireEvent('loadexception', this);
29574             return;
29575         }
29576         
29577         if(!this.mapTypeId){
29578             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29579         }
29580         
29581         this.gMapContext = this.GMapContext();
29582         
29583         this.initOverlayView();
29584         
29585         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29586         
29587         var _this = this;
29588                 
29589         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29590             _this.setPosition(_this.gMapContext.marker.position);
29591         });
29592         
29593         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29594             _this.fireEvent('mapClick', this, event);
29595             
29596         });
29597
29598         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29599             _this.fireEvent('mapRightClick', this, event);
29600             
29601         });
29602         
29603         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29604             _this.fireEvent('markerClick', this, event);
29605             
29606         });
29607
29608         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29609             _this.fireEvent('markerRightClick', this, event);
29610             
29611         });
29612         
29613         this.setPosition(this.gMapContext.location);
29614         
29615         this.fireEvent('initial', this, this.gMapContext.location);
29616     },
29617     
29618     initOverlayView: function()
29619     {
29620         var _this = this;
29621         
29622         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29623             
29624             draw: function()
29625             {
29626                 _this.fireEvent('OverlayViewDraw', _this);
29627             },
29628             
29629             onAdd: function()
29630             {
29631                 _this.fireEvent('OverlayViewOnAdd', _this);
29632             },
29633             
29634             onRemove: function()
29635             {
29636                 _this.fireEvent('OverlayViewOnRemove', _this);
29637             },
29638             
29639             show: function(cpx)
29640             {
29641                 _this.fireEvent('OverlayViewShow', _this, cpx);
29642             },
29643             
29644             hide: function()
29645             {
29646                 _this.fireEvent('OverlayViewHide', _this);
29647             }
29648             
29649         });
29650     },
29651     
29652     fromLatLngToContainerPixel: function(event)
29653     {
29654         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29655     },
29656     
29657     isApplied: function() 
29658     {
29659         return this.getGmapContext() == false ? false : true;
29660     },
29661     
29662     getGmapContext: function() 
29663     {
29664         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29665     },
29666     
29667     GMapContext: function() 
29668     {
29669         var position = new google.maps.LatLng(this.latitude, this.longitude);
29670         
29671         var _map = new google.maps.Map(this.el.dom, {
29672             center: position,
29673             zoom: this.zoom,
29674             mapTypeId: this.mapTypeId,
29675             mapTypeControl: this.mapTypeControl,
29676             disableDoubleClickZoom: this.disableDoubleClickZoom,
29677             scrollwheel: this.scrollwheel,
29678             streetViewControl: this.streetViewControl,
29679             locationName: this.locationName,
29680             draggable: this.draggable,
29681             enableAutocomplete: this.enableAutocomplete,
29682             enableReverseGeocode: this.enableReverseGeocode
29683         });
29684         
29685         var _marker = new google.maps.Marker({
29686             position: position,
29687             map: _map,
29688             title: this.markerTitle,
29689             draggable: this.draggable
29690         });
29691         
29692         return {
29693             map: _map,
29694             marker: _marker,
29695             circle: null,
29696             location: position,
29697             radius: this.radius,
29698             locationName: this.locationName,
29699             addressComponents: {
29700                 formatted_address: null,
29701                 addressLine1: null,
29702                 addressLine2: null,
29703                 streetName: null,
29704                 streetNumber: null,
29705                 city: null,
29706                 district: null,
29707                 state: null,
29708                 stateOrProvince: null
29709             },
29710             settings: this,
29711             domContainer: this.el.dom,
29712             geodecoder: new google.maps.Geocoder()
29713         };
29714     },
29715     
29716     drawCircle: function(center, radius, options) 
29717     {
29718         if (this.gMapContext.circle != null) {
29719             this.gMapContext.circle.setMap(null);
29720         }
29721         if (radius > 0) {
29722             radius *= 1;
29723             options = Roo.apply({}, options, {
29724                 strokeColor: "#0000FF",
29725                 strokeOpacity: .35,
29726                 strokeWeight: 2,
29727                 fillColor: "#0000FF",
29728                 fillOpacity: .2
29729             });
29730             
29731             options.map = this.gMapContext.map;
29732             options.radius = radius;
29733             options.center = center;
29734             this.gMapContext.circle = new google.maps.Circle(options);
29735             return this.gMapContext.circle;
29736         }
29737         
29738         return null;
29739     },
29740     
29741     setPosition: function(location) 
29742     {
29743         this.gMapContext.location = location;
29744         this.gMapContext.marker.setPosition(location);
29745         this.gMapContext.map.panTo(location);
29746         this.drawCircle(location, this.gMapContext.radius, {});
29747         
29748         var _this = this;
29749         
29750         if (this.gMapContext.settings.enableReverseGeocode) {
29751             this.gMapContext.geodecoder.geocode({
29752                 latLng: this.gMapContext.location
29753             }, function(results, status) {
29754                 
29755                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29756                     _this.gMapContext.locationName = results[0].formatted_address;
29757                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29758                     
29759                     _this.fireEvent('positionchanged', this, location);
29760                 }
29761             });
29762             
29763             return;
29764         }
29765         
29766         this.fireEvent('positionchanged', this, location);
29767     },
29768     
29769     resize: function()
29770     {
29771         google.maps.event.trigger(this.gMapContext.map, "resize");
29772         
29773         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29774         
29775         this.fireEvent('resize', this);
29776     },
29777     
29778     setPositionByLatLng: function(latitude, longitude)
29779     {
29780         this.setPosition(new google.maps.LatLng(latitude, longitude));
29781     },
29782     
29783     getCurrentPosition: function() 
29784     {
29785         return {
29786             latitude: this.gMapContext.location.lat(),
29787             longitude: this.gMapContext.location.lng()
29788         };
29789     },
29790     
29791     getAddressName: function() 
29792     {
29793         return this.gMapContext.locationName;
29794     },
29795     
29796     getAddressComponents: function() 
29797     {
29798         return this.gMapContext.addressComponents;
29799     },
29800     
29801     address_component_from_google_geocode: function(address_components) 
29802     {
29803         var result = {};
29804         
29805         for (var i = 0; i < address_components.length; i++) {
29806             var component = address_components[i];
29807             if (component.types.indexOf("postal_code") >= 0) {
29808                 result.postalCode = component.short_name;
29809             } else if (component.types.indexOf("street_number") >= 0) {
29810                 result.streetNumber = component.short_name;
29811             } else if (component.types.indexOf("route") >= 0) {
29812                 result.streetName = component.short_name;
29813             } else if (component.types.indexOf("neighborhood") >= 0) {
29814                 result.city = component.short_name;
29815             } else if (component.types.indexOf("locality") >= 0) {
29816                 result.city = component.short_name;
29817             } else if (component.types.indexOf("sublocality") >= 0) {
29818                 result.district = component.short_name;
29819             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29820                 result.stateOrProvince = component.short_name;
29821             } else if (component.types.indexOf("country") >= 0) {
29822                 result.country = component.short_name;
29823             }
29824         }
29825         
29826         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29827         result.addressLine2 = "";
29828         return result;
29829     },
29830     
29831     setZoomLevel: function(zoom)
29832     {
29833         this.gMapContext.map.setZoom(zoom);
29834     },
29835     
29836     show: function()
29837     {
29838         if(!this.el){
29839             return;
29840         }
29841         
29842         this.el.show();
29843         
29844         this.resize();
29845         
29846         this.fireEvent('show', this);
29847     },
29848     
29849     hide: function()
29850     {
29851         if(!this.el){
29852             return;
29853         }
29854         
29855         this.el.hide();
29856         
29857         this.fireEvent('hide', this);
29858     }
29859     
29860 });
29861
29862 Roo.apply(Roo.bootstrap.LocationPicker, {
29863     
29864     OverlayView : function(map, options)
29865     {
29866         options = options || {};
29867         
29868         this.setMap(map);
29869     }
29870     
29871     
29872 });/**
29873  * @class Roo.bootstrap.Alert
29874  * @extends Roo.bootstrap.Component
29875  * Bootstrap Alert class - shows an alert area box
29876  * eg
29877  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29878   Enter a valid email address
29879 </div>
29880  * @licence LGPL
29881  * @cfg {String} title The title of alert
29882  * @cfg {String} html The content of alert
29883  * @cfg {String} weight (  success | info | warning | danger )
29884  * @cfg {String} fa font-awesomeicon
29885  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29886  * @cfg {Boolean} close true to show a x closer
29887  * 
29888  * 
29889  * @constructor
29890  * Create a new alert
29891  * @param {Object} config The config object
29892  */
29893
29894
29895 Roo.bootstrap.Alert = function(config){
29896     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29897     
29898 };
29899
29900 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29901     
29902     title: '',
29903     html: '',
29904     weight: false,
29905     fa: false,
29906     faicon: false, // BC
29907     close : false,
29908     
29909     
29910     getAutoCreate : function()
29911     {
29912         
29913         var cfg = {
29914             tag : 'div',
29915             cls : 'alert',
29916             cn : [
29917                 {
29918                     tag: 'button',
29919                     type :  "button",
29920                     cls: "close",
29921                     html : '×',
29922                     style : this.close ? '' : 'display:none'
29923                 },
29924                 {
29925                     tag : 'i',
29926                     cls : 'roo-alert-icon'
29927                     
29928                 },
29929                 {
29930                     tag : 'b',
29931                     cls : 'roo-alert-title',
29932                     html : this.title
29933                 },
29934                 {
29935                     tag : 'span',
29936                     cls : 'roo-alert-text',
29937                     html : this.html
29938                 }
29939             ]
29940         };
29941         
29942         if(this.faicon){
29943             cfg.cn[0].cls += ' fa ' + this.faicon;
29944         }
29945         if(this.fa){
29946             cfg.cn[0].cls += ' fa ' + this.fa;
29947         }
29948         
29949         if(this.weight){
29950             cfg.cls += ' alert-' + this.weight;
29951         }
29952         
29953         return cfg;
29954     },
29955     
29956     initEvents: function() 
29957     {
29958         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29959         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29960         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29961         if (this.seconds > 0) {
29962             this.hide.defer(this.seconds, this);
29963         }
29964     },
29965     
29966     setTitle : function(str)
29967     {
29968         this.titleEl.dom.innerHTML = str;
29969     },
29970     
29971     setText : function(str)
29972     {
29973         this.titleEl.dom.innerHTML = str;
29974     },
29975     
29976     setWeight : function(weight)
29977     {
29978         if(this.weight){
29979             this.el.removeClass('alert-' + this.weight);
29980         }
29981         
29982         this.weight = weight;
29983         
29984         this.el.addClass('alert-' + this.weight);
29985     },
29986     
29987     setIcon : function(icon)
29988     {
29989         if(this.faicon){
29990             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29991         }
29992         
29993         this.faicon = icon;
29994         
29995         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29996     },
29997     
29998     hide: function() 
29999     {
30000         this.el.hide();   
30001     },
30002     
30003     show: function() 
30004     {  
30005         this.el.show();   
30006     }
30007     
30008 });
30009
30010  
30011 /*
30012 * Licence: LGPL
30013 */
30014
30015 /**
30016  * @class Roo.bootstrap.UploadCropbox
30017  * @extends Roo.bootstrap.Component
30018  * Bootstrap UploadCropbox class
30019  * @cfg {String} emptyText show when image has been loaded
30020  * @cfg {String} rotateNotify show when image too small to rotate
30021  * @cfg {Number} errorTimeout default 3000
30022  * @cfg {Number} minWidth default 300
30023  * @cfg {Number} minHeight default 300
30024  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30025  * @cfg {Boolean} isDocument (true|false) default false
30026  * @cfg {String} url action url
30027  * @cfg {String} paramName default 'imageUpload'
30028  * @cfg {String} method default POST
30029  * @cfg {Boolean} loadMask (true|false) default true
30030  * @cfg {Boolean} loadingText default 'Loading...'
30031  * 
30032  * @constructor
30033  * Create a new UploadCropbox
30034  * @param {Object} config The config object
30035  */
30036
30037 Roo.bootstrap.UploadCropbox = function(config){
30038     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30039     
30040     this.addEvents({
30041         /**
30042          * @event beforeselectfile
30043          * Fire before select file
30044          * @param {Roo.bootstrap.UploadCropbox} this
30045          */
30046         "beforeselectfile" : true,
30047         /**
30048          * @event initial
30049          * Fire after initEvent
30050          * @param {Roo.bootstrap.UploadCropbox} this
30051          */
30052         "initial" : true,
30053         /**
30054          * @event crop
30055          * Fire after initEvent
30056          * @param {Roo.bootstrap.UploadCropbox} this
30057          * @param {String} data
30058          */
30059         "crop" : true,
30060         /**
30061          * @event prepare
30062          * Fire when preparing the file data
30063          * @param {Roo.bootstrap.UploadCropbox} this
30064          * @param {Object} file
30065          */
30066         "prepare" : true,
30067         /**
30068          * @event exception
30069          * Fire when get exception
30070          * @param {Roo.bootstrap.UploadCropbox} this
30071          * @param {XMLHttpRequest} xhr
30072          */
30073         "exception" : true,
30074         /**
30075          * @event beforeloadcanvas
30076          * Fire before load the canvas
30077          * @param {Roo.bootstrap.UploadCropbox} this
30078          * @param {String} src
30079          */
30080         "beforeloadcanvas" : true,
30081         /**
30082          * @event trash
30083          * Fire when trash image
30084          * @param {Roo.bootstrap.UploadCropbox} this
30085          */
30086         "trash" : true,
30087         /**
30088          * @event download
30089          * Fire when download the image
30090          * @param {Roo.bootstrap.UploadCropbox} this
30091          */
30092         "download" : true,
30093         /**
30094          * @event footerbuttonclick
30095          * Fire when footerbuttonclick
30096          * @param {Roo.bootstrap.UploadCropbox} this
30097          * @param {String} type
30098          */
30099         "footerbuttonclick" : true,
30100         /**
30101          * @event resize
30102          * Fire when resize
30103          * @param {Roo.bootstrap.UploadCropbox} this
30104          */
30105         "resize" : true,
30106         /**
30107          * @event rotate
30108          * Fire when rotate the image
30109          * @param {Roo.bootstrap.UploadCropbox} this
30110          * @param {String} pos
30111          */
30112         "rotate" : true,
30113         /**
30114          * @event inspect
30115          * Fire when inspect the file
30116          * @param {Roo.bootstrap.UploadCropbox} this
30117          * @param {Object} file
30118          */
30119         "inspect" : true,
30120         /**
30121          * @event upload
30122          * Fire when xhr upload the file
30123          * @param {Roo.bootstrap.UploadCropbox} this
30124          * @param {Object} data
30125          */
30126         "upload" : true,
30127         /**
30128          * @event arrange
30129          * Fire when arrange the file data
30130          * @param {Roo.bootstrap.UploadCropbox} this
30131          * @param {Object} formData
30132          */
30133         "arrange" : true
30134     });
30135     
30136     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30137 };
30138
30139 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30140     
30141     emptyText : 'Click to upload image',
30142     rotateNotify : 'Image is too small to rotate',
30143     errorTimeout : 3000,
30144     scale : 0,
30145     baseScale : 1,
30146     rotate : 0,
30147     dragable : false,
30148     pinching : false,
30149     mouseX : 0,
30150     mouseY : 0,
30151     cropData : false,
30152     minWidth : 300,
30153     minHeight : 300,
30154     file : false,
30155     exif : {},
30156     baseRotate : 1,
30157     cropType : 'image/jpeg',
30158     buttons : false,
30159     canvasLoaded : false,
30160     isDocument : false,
30161     method : 'POST',
30162     paramName : 'imageUpload',
30163     loadMask : true,
30164     loadingText : 'Loading...',
30165     maskEl : false,
30166     
30167     getAutoCreate : function()
30168     {
30169         var cfg = {
30170             tag : 'div',
30171             cls : 'roo-upload-cropbox',
30172             cn : [
30173                 {
30174                     tag : 'input',
30175                     cls : 'roo-upload-cropbox-selector',
30176                     type : 'file'
30177                 },
30178                 {
30179                     tag : 'div',
30180                     cls : 'roo-upload-cropbox-body',
30181                     style : 'cursor:pointer',
30182                     cn : [
30183                         {
30184                             tag : 'div',
30185                             cls : 'roo-upload-cropbox-preview'
30186                         },
30187                         {
30188                             tag : 'div',
30189                             cls : 'roo-upload-cropbox-thumb'
30190                         },
30191                         {
30192                             tag : 'div',
30193                             cls : 'roo-upload-cropbox-empty-notify',
30194                             html : this.emptyText
30195                         },
30196                         {
30197                             tag : 'div',
30198                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30199                             html : this.rotateNotify
30200                         }
30201                     ]
30202                 },
30203                 {
30204                     tag : 'div',
30205                     cls : 'roo-upload-cropbox-footer',
30206                     cn : {
30207                         tag : 'div',
30208                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30209                         cn : []
30210                     }
30211                 }
30212             ]
30213         };
30214         
30215         return cfg;
30216     },
30217     
30218     onRender : function(ct, position)
30219     {
30220         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30221         
30222         if (this.buttons.length) {
30223             
30224             Roo.each(this.buttons, function(bb) {
30225                 
30226                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30227                 
30228                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30229                 
30230             }, this);
30231         }
30232         
30233         if(this.loadMask){
30234             this.maskEl = this.el;
30235         }
30236     },
30237     
30238     initEvents : function()
30239     {
30240         this.urlAPI = (window.createObjectURL && window) || 
30241                                 (window.URL && URL.revokeObjectURL && URL) || 
30242                                 (window.webkitURL && webkitURL);
30243                         
30244         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30245         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30246         
30247         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30248         this.selectorEl.hide();
30249         
30250         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30251         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30252         
30253         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30254         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30255         this.thumbEl.hide();
30256         
30257         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30258         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30259         
30260         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30261         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30262         this.errorEl.hide();
30263         
30264         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30265         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30266         this.footerEl.hide();
30267         
30268         this.setThumbBoxSize();
30269         
30270         this.bind();
30271         
30272         this.resize();
30273         
30274         this.fireEvent('initial', this);
30275     },
30276
30277     bind : function()
30278     {
30279         var _this = this;
30280         
30281         window.addEventListener("resize", function() { _this.resize(); } );
30282         
30283         this.bodyEl.on('click', this.beforeSelectFile, this);
30284         
30285         if(Roo.isTouch){
30286             this.bodyEl.on('touchstart', this.onTouchStart, this);
30287             this.bodyEl.on('touchmove', this.onTouchMove, this);
30288             this.bodyEl.on('touchend', this.onTouchEnd, this);
30289         }
30290         
30291         if(!Roo.isTouch){
30292             this.bodyEl.on('mousedown', this.onMouseDown, this);
30293             this.bodyEl.on('mousemove', this.onMouseMove, this);
30294             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30295             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30296             Roo.get(document).on('mouseup', this.onMouseUp, this);
30297         }
30298         
30299         this.selectorEl.on('change', this.onFileSelected, this);
30300     },
30301     
30302     reset : function()
30303     {    
30304         this.scale = 0;
30305         this.baseScale = 1;
30306         this.rotate = 0;
30307         this.baseRotate = 1;
30308         this.dragable = false;
30309         this.pinching = false;
30310         this.mouseX = 0;
30311         this.mouseY = 0;
30312         this.cropData = false;
30313         this.notifyEl.dom.innerHTML = this.emptyText;
30314         
30315         this.selectorEl.dom.value = '';
30316         
30317     },
30318     
30319     resize : function()
30320     {
30321         if(this.fireEvent('resize', this) != false){
30322             this.setThumbBoxPosition();
30323             this.setCanvasPosition();
30324         }
30325     },
30326     
30327     onFooterButtonClick : function(e, el, o, type)
30328     {
30329         switch (type) {
30330             case 'rotate-left' :
30331                 this.onRotateLeft(e);
30332                 break;
30333             case 'rotate-right' :
30334                 this.onRotateRight(e);
30335                 break;
30336             case 'picture' :
30337                 this.beforeSelectFile(e);
30338                 break;
30339             case 'trash' :
30340                 this.trash(e);
30341                 break;
30342             case 'crop' :
30343                 this.crop(e);
30344                 break;
30345             case 'download' :
30346                 this.download(e);
30347                 break;
30348             default :
30349                 break;
30350         }
30351         
30352         this.fireEvent('footerbuttonclick', this, type);
30353     },
30354     
30355     beforeSelectFile : function(e)
30356     {
30357         e.preventDefault();
30358         
30359         if(this.fireEvent('beforeselectfile', this) != false){
30360             this.selectorEl.dom.click();
30361         }
30362     },
30363     
30364     onFileSelected : function(e)
30365     {
30366         e.preventDefault();
30367         
30368         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30369             return;
30370         }
30371         
30372         var file = this.selectorEl.dom.files[0];
30373         
30374         if(this.fireEvent('inspect', this, file) != false){
30375             this.prepare(file);
30376         }
30377         
30378     },
30379     
30380     trash : function(e)
30381     {
30382         this.fireEvent('trash', this);
30383     },
30384     
30385     download : function(e)
30386     {
30387         this.fireEvent('download', this);
30388     },
30389     
30390     loadCanvas : function(src)
30391     {   
30392         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30393             
30394             this.reset();
30395             
30396             this.imageEl = document.createElement('img');
30397             
30398             var _this = this;
30399             
30400             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30401             
30402             this.imageEl.src = src;
30403         }
30404     },
30405     
30406     onLoadCanvas : function()
30407     {   
30408         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30409         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30410         
30411         this.bodyEl.un('click', this.beforeSelectFile, this);
30412         
30413         this.notifyEl.hide();
30414         this.thumbEl.show();
30415         this.footerEl.show();
30416         
30417         this.baseRotateLevel();
30418         
30419         if(this.isDocument){
30420             this.setThumbBoxSize();
30421         }
30422         
30423         this.setThumbBoxPosition();
30424         
30425         this.baseScaleLevel();
30426         
30427         this.draw();
30428         
30429         this.resize();
30430         
30431         this.canvasLoaded = true;
30432         
30433         if(this.loadMask){
30434             this.maskEl.unmask();
30435         }
30436         
30437     },
30438     
30439     setCanvasPosition : function()
30440     {   
30441         if(!this.canvasEl){
30442             return;
30443         }
30444         
30445         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30446         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30447         
30448         this.previewEl.setLeft(pw);
30449         this.previewEl.setTop(ph);
30450         
30451     },
30452     
30453     onMouseDown : function(e)
30454     {   
30455         e.stopEvent();
30456         
30457         this.dragable = true;
30458         this.pinching = false;
30459         
30460         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30461             this.dragable = false;
30462             return;
30463         }
30464         
30465         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30466         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30467         
30468     },
30469     
30470     onMouseMove : function(e)
30471     {   
30472         e.stopEvent();
30473         
30474         if(!this.canvasLoaded){
30475             return;
30476         }
30477         
30478         if (!this.dragable){
30479             return;
30480         }
30481         
30482         var minX = Math.ceil(this.thumbEl.getLeft(true));
30483         var minY = Math.ceil(this.thumbEl.getTop(true));
30484         
30485         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30486         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30487         
30488         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30489         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30490         
30491         x = x - this.mouseX;
30492         y = y - this.mouseY;
30493         
30494         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30495         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30496         
30497         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30498         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30499         
30500         this.previewEl.setLeft(bgX);
30501         this.previewEl.setTop(bgY);
30502         
30503         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30504         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30505     },
30506     
30507     onMouseUp : function(e)
30508     {   
30509         e.stopEvent();
30510         
30511         this.dragable = false;
30512     },
30513     
30514     onMouseWheel : function(e)
30515     {   
30516         e.stopEvent();
30517         
30518         this.startScale = this.scale;
30519         
30520         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30521         
30522         if(!this.zoomable()){
30523             this.scale = this.startScale;
30524             return;
30525         }
30526         
30527         this.draw();
30528         
30529         return;
30530     },
30531     
30532     zoomable : function()
30533     {
30534         var minScale = this.thumbEl.getWidth() / this.minWidth;
30535         
30536         if(this.minWidth < this.minHeight){
30537             minScale = this.thumbEl.getHeight() / this.minHeight;
30538         }
30539         
30540         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30541         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30542         
30543         if(
30544                 this.isDocument &&
30545                 (this.rotate == 0 || this.rotate == 180) && 
30546                 (
30547                     width > this.imageEl.OriginWidth || 
30548                     height > this.imageEl.OriginHeight ||
30549                     (width < this.minWidth && height < this.minHeight)
30550                 )
30551         ){
30552             return false;
30553         }
30554         
30555         if(
30556                 this.isDocument &&
30557                 (this.rotate == 90 || this.rotate == 270) && 
30558                 (
30559                     width > this.imageEl.OriginWidth || 
30560                     height > this.imageEl.OriginHeight ||
30561                     (width < this.minHeight && height < this.minWidth)
30562                 )
30563         ){
30564             return false;
30565         }
30566         
30567         if(
30568                 !this.isDocument &&
30569                 (this.rotate == 0 || this.rotate == 180) && 
30570                 (
30571                     width < this.minWidth || 
30572                     width > this.imageEl.OriginWidth || 
30573                     height < this.minHeight || 
30574                     height > this.imageEl.OriginHeight
30575                 )
30576         ){
30577             return false;
30578         }
30579         
30580         if(
30581                 !this.isDocument &&
30582                 (this.rotate == 90 || this.rotate == 270) && 
30583                 (
30584                     width < this.minHeight || 
30585                     width > this.imageEl.OriginWidth || 
30586                     height < this.minWidth || 
30587                     height > this.imageEl.OriginHeight
30588                 )
30589         ){
30590             return false;
30591         }
30592         
30593         return true;
30594         
30595     },
30596     
30597     onRotateLeft : function(e)
30598     {   
30599         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30600             
30601             var minScale = this.thumbEl.getWidth() / this.minWidth;
30602             
30603             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30604             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30605             
30606             this.startScale = this.scale;
30607             
30608             while (this.getScaleLevel() < minScale){
30609             
30610                 this.scale = this.scale + 1;
30611                 
30612                 if(!this.zoomable()){
30613                     break;
30614                 }
30615                 
30616                 if(
30617                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30618                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30619                 ){
30620                     continue;
30621                 }
30622                 
30623                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30624
30625                 this.draw();
30626                 
30627                 return;
30628             }
30629             
30630             this.scale = this.startScale;
30631             
30632             this.onRotateFail();
30633             
30634             return false;
30635         }
30636         
30637         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30638
30639         if(this.isDocument){
30640             this.setThumbBoxSize();
30641             this.setThumbBoxPosition();
30642             this.setCanvasPosition();
30643         }
30644         
30645         this.draw();
30646         
30647         this.fireEvent('rotate', this, 'left');
30648         
30649     },
30650     
30651     onRotateRight : function(e)
30652     {
30653         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30654             
30655             var minScale = this.thumbEl.getWidth() / this.minWidth;
30656         
30657             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30658             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30659             
30660             this.startScale = this.scale;
30661             
30662             while (this.getScaleLevel() < minScale){
30663             
30664                 this.scale = this.scale + 1;
30665                 
30666                 if(!this.zoomable()){
30667                     break;
30668                 }
30669                 
30670                 if(
30671                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30672                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30673                 ){
30674                     continue;
30675                 }
30676                 
30677                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30678
30679                 this.draw();
30680                 
30681                 return;
30682             }
30683             
30684             this.scale = this.startScale;
30685             
30686             this.onRotateFail();
30687             
30688             return false;
30689         }
30690         
30691         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30692
30693         if(this.isDocument){
30694             this.setThumbBoxSize();
30695             this.setThumbBoxPosition();
30696             this.setCanvasPosition();
30697         }
30698         
30699         this.draw();
30700         
30701         this.fireEvent('rotate', this, 'right');
30702     },
30703     
30704     onRotateFail : function()
30705     {
30706         this.errorEl.show(true);
30707         
30708         var _this = this;
30709         
30710         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30711     },
30712     
30713     draw : function()
30714     {
30715         this.previewEl.dom.innerHTML = '';
30716         
30717         var canvasEl = document.createElement("canvas");
30718         
30719         var contextEl = canvasEl.getContext("2d");
30720         
30721         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30722         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30723         var center = this.imageEl.OriginWidth / 2;
30724         
30725         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30726             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30727             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30728             center = this.imageEl.OriginHeight / 2;
30729         }
30730         
30731         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30732         
30733         contextEl.translate(center, center);
30734         contextEl.rotate(this.rotate * Math.PI / 180);
30735
30736         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30737         
30738         this.canvasEl = document.createElement("canvas");
30739         
30740         this.contextEl = this.canvasEl.getContext("2d");
30741         
30742         switch (this.rotate) {
30743             case 0 :
30744                 
30745                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30746                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30747                 
30748                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30749                 
30750                 break;
30751             case 90 : 
30752                 
30753                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30754                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30755                 
30756                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30757                     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);
30758                     break;
30759                 }
30760                 
30761                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30762                 
30763                 break;
30764             case 180 :
30765                 
30766                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30767                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30768                 
30769                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30770                     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);
30771                     break;
30772                 }
30773                 
30774                 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);
30775                 
30776                 break;
30777             case 270 :
30778                 
30779                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30780                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30781         
30782                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30783                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30784                     break;
30785                 }
30786                 
30787                 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);
30788                 
30789                 break;
30790             default : 
30791                 break;
30792         }
30793         
30794         this.previewEl.appendChild(this.canvasEl);
30795         
30796         this.setCanvasPosition();
30797     },
30798     
30799     crop : function()
30800     {
30801         if(!this.canvasLoaded){
30802             return;
30803         }
30804         
30805         var imageCanvas = document.createElement("canvas");
30806         
30807         var imageContext = imageCanvas.getContext("2d");
30808         
30809         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30810         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30811         
30812         var center = imageCanvas.width / 2;
30813         
30814         imageContext.translate(center, center);
30815         
30816         imageContext.rotate(this.rotate * Math.PI / 180);
30817         
30818         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30819         
30820         var canvas = document.createElement("canvas");
30821         
30822         var context = canvas.getContext("2d");
30823                 
30824         canvas.width = this.minWidth;
30825         canvas.height = this.minHeight;
30826
30827         switch (this.rotate) {
30828             case 0 :
30829                 
30830                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30831                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30832                 
30833                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30834                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30835                 
30836                 var targetWidth = this.minWidth - 2 * x;
30837                 var targetHeight = this.minHeight - 2 * y;
30838                 
30839                 var scale = 1;
30840                 
30841                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30842                     scale = targetWidth / width;
30843                 }
30844                 
30845                 if(x > 0 && y == 0){
30846                     scale = targetHeight / height;
30847                 }
30848                 
30849                 if(x > 0 && y > 0){
30850                     scale = targetWidth / width;
30851                     
30852                     if(width < height){
30853                         scale = targetHeight / height;
30854                     }
30855                 }
30856                 
30857                 context.scale(scale, scale);
30858                 
30859                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30860                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30861
30862                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30863                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30864
30865                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30866                 
30867                 break;
30868             case 90 : 
30869                 
30870                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30871                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30872                 
30873                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30874                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30875                 
30876                 var targetWidth = this.minWidth - 2 * x;
30877                 var targetHeight = this.minHeight - 2 * y;
30878                 
30879                 var scale = 1;
30880                 
30881                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30882                     scale = targetWidth / width;
30883                 }
30884                 
30885                 if(x > 0 && y == 0){
30886                     scale = targetHeight / height;
30887                 }
30888                 
30889                 if(x > 0 && y > 0){
30890                     scale = targetWidth / width;
30891                     
30892                     if(width < height){
30893                         scale = targetHeight / height;
30894                     }
30895                 }
30896                 
30897                 context.scale(scale, scale);
30898                 
30899                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30900                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30901
30902                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30903                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30904                 
30905                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30906                 
30907                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30908                 
30909                 break;
30910             case 180 :
30911                 
30912                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30913                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30914                 
30915                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30916                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30917                 
30918                 var targetWidth = this.minWidth - 2 * x;
30919                 var targetHeight = this.minHeight - 2 * y;
30920                 
30921                 var scale = 1;
30922                 
30923                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30924                     scale = targetWidth / width;
30925                 }
30926                 
30927                 if(x > 0 && y == 0){
30928                     scale = targetHeight / height;
30929                 }
30930                 
30931                 if(x > 0 && y > 0){
30932                     scale = targetWidth / width;
30933                     
30934                     if(width < height){
30935                         scale = targetHeight / height;
30936                     }
30937                 }
30938                 
30939                 context.scale(scale, scale);
30940                 
30941                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30942                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30943
30944                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30945                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30946
30947                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30948                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30949                 
30950                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30951                 
30952                 break;
30953             case 270 :
30954                 
30955                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30956                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30957                 
30958                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30959                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30960                 
30961                 var targetWidth = this.minWidth - 2 * x;
30962                 var targetHeight = this.minHeight - 2 * y;
30963                 
30964                 var scale = 1;
30965                 
30966                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30967                     scale = targetWidth / width;
30968                 }
30969                 
30970                 if(x > 0 && y == 0){
30971                     scale = targetHeight / height;
30972                 }
30973                 
30974                 if(x > 0 && y > 0){
30975                     scale = targetWidth / width;
30976                     
30977                     if(width < height){
30978                         scale = targetHeight / height;
30979                     }
30980                 }
30981                 
30982                 context.scale(scale, scale);
30983                 
30984                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30985                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30986
30987                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30988                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30989                 
30990                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30991                 
30992                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30993                 
30994                 break;
30995             default : 
30996                 break;
30997         }
30998         
30999         this.cropData = canvas.toDataURL(this.cropType);
31000         
31001         if(this.fireEvent('crop', this, this.cropData) !== false){
31002             this.process(this.file, this.cropData);
31003         }
31004         
31005         return;
31006         
31007     },
31008     
31009     setThumbBoxSize : function()
31010     {
31011         var width, height;
31012         
31013         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31014             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31015             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31016             
31017             this.minWidth = width;
31018             this.minHeight = height;
31019             
31020             if(this.rotate == 90 || this.rotate == 270){
31021                 this.minWidth = height;
31022                 this.minHeight = width;
31023             }
31024         }
31025         
31026         height = 300;
31027         width = Math.ceil(this.minWidth * height / this.minHeight);
31028         
31029         if(this.minWidth > this.minHeight){
31030             width = 300;
31031             height = Math.ceil(this.minHeight * width / this.minWidth);
31032         }
31033         
31034         this.thumbEl.setStyle({
31035             width : width + 'px',
31036             height : height + 'px'
31037         });
31038
31039         return;
31040             
31041     },
31042     
31043     setThumbBoxPosition : function()
31044     {
31045         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31046         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31047         
31048         this.thumbEl.setLeft(x);
31049         this.thumbEl.setTop(y);
31050         
31051     },
31052     
31053     baseRotateLevel : function()
31054     {
31055         this.baseRotate = 1;
31056         
31057         if(
31058                 typeof(this.exif) != 'undefined' &&
31059                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31060                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31061         ){
31062             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31063         }
31064         
31065         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31066         
31067     },
31068     
31069     baseScaleLevel : function()
31070     {
31071         var width, height;
31072         
31073         if(this.isDocument){
31074             
31075             if(this.baseRotate == 6 || this.baseRotate == 8){
31076             
31077                 height = this.thumbEl.getHeight();
31078                 this.baseScale = height / this.imageEl.OriginWidth;
31079
31080                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31081                     width = this.thumbEl.getWidth();
31082                     this.baseScale = width / this.imageEl.OriginHeight;
31083                 }
31084
31085                 return;
31086             }
31087
31088             height = this.thumbEl.getHeight();
31089             this.baseScale = height / this.imageEl.OriginHeight;
31090
31091             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31092                 width = this.thumbEl.getWidth();
31093                 this.baseScale = width / this.imageEl.OriginWidth;
31094             }
31095
31096             return;
31097         }
31098         
31099         if(this.baseRotate == 6 || this.baseRotate == 8){
31100             
31101             width = this.thumbEl.getHeight();
31102             this.baseScale = width / this.imageEl.OriginHeight;
31103             
31104             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31105                 height = this.thumbEl.getWidth();
31106                 this.baseScale = height / this.imageEl.OriginHeight;
31107             }
31108             
31109             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31110                 height = this.thumbEl.getWidth();
31111                 this.baseScale = height / this.imageEl.OriginHeight;
31112                 
31113                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31114                     width = this.thumbEl.getHeight();
31115                     this.baseScale = width / this.imageEl.OriginWidth;
31116                 }
31117             }
31118             
31119             return;
31120         }
31121         
31122         width = this.thumbEl.getWidth();
31123         this.baseScale = width / this.imageEl.OriginWidth;
31124         
31125         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31126             height = this.thumbEl.getHeight();
31127             this.baseScale = height / this.imageEl.OriginHeight;
31128         }
31129         
31130         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31131             
31132             height = this.thumbEl.getHeight();
31133             this.baseScale = height / this.imageEl.OriginHeight;
31134             
31135             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31136                 width = this.thumbEl.getWidth();
31137                 this.baseScale = width / this.imageEl.OriginWidth;
31138             }
31139             
31140         }
31141         
31142         return;
31143     },
31144     
31145     getScaleLevel : function()
31146     {
31147         return this.baseScale * Math.pow(1.1, this.scale);
31148     },
31149     
31150     onTouchStart : function(e)
31151     {
31152         if(!this.canvasLoaded){
31153             this.beforeSelectFile(e);
31154             return;
31155         }
31156         
31157         var touches = e.browserEvent.touches;
31158         
31159         if(!touches){
31160             return;
31161         }
31162         
31163         if(touches.length == 1){
31164             this.onMouseDown(e);
31165             return;
31166         }
31167         
31168         if(touches.length != 2){
31169             return;
31170         }
31171         
31172         var coords = [];
31173         
31174         for(var i = 0, finger; finger = touches[i]; i++){
31175             coords.push(finger.pageX, finger.pageY);
31176         }
31177         
31178         var x = Math.pow(coords[0] - coords[2], 2);
31179         var y = Math.pow(coords[1] - coords[3], 2);
31180         
31181         this.startDistance = Math.sqrt(x + y);
31182         
31183         this.startScale = this.scale;
31184         
31185         this.pinching = true;
31186         this.dragable = false;
31187         
31188     },
31189     
31190     onTouchMove : function(e)
31191     {
31192         if(!this.pinching && !this.dragable){
31193             return;
31194         }
31195         
31196         var touches = e.browserEvent.touches;
31197         
31198         if(!touches){
31199             return;
31200         }
31201         
31202         if(this.dragable){
31203             this.onMouseMove(e);
31204             return;
31205         }
31206         
31207         var coords = [];
31208         
31209         for(var i = 0, finger; finger = touches[i]; i++){
31210             coords.push(finger.pageX, finger.pageY);
31211         }
31212         
31213         var x = Math.pow(coords[0] - coords[2], 2);
31214         var y = Math.pow(coords[1] - coords[3], 2);
31215         
31216         this.endDistance = Math.sqrt(x + y);
31217         
31218         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31219         
31220         if(!this.zoomable()){
31221             this.scale = this.startScale;
31222             return;
31223         }
31224         
31225         this.draw();
31226         
31227     },
31228     
31229     onTouchEnd : function(e)
31230     {
31231         this.pinching = false;
31232         this.dragable = false;
31233         
31234     },
31235     
31236     process : function(file, crop)
31237     {
31238         if(this.loadMask){
31239             this.maskEl.mask(this.loadingText);
31240         }
31241         
31242         this.xhr = new XMLHttpRequest();
31243         
31244         file.xhr = this.xhr;
31245
31246         this.xhr.open(this.method, this.url, true);
31247         
31248         var headers = {
31249             "Accept": "application/json",
31250             "Cache-Control": "no-cache",
31251             "X-Requested-With": "XMLHttpRequest"
31252         };
31253         
31254         for (var headerName in headers) {
31255             var headerValue = headers[headerName];
31256             if (headerValue) {
31257                 this.xhr.setRequestHeader(headerName, headerValue);
31258             }
31259         }
31260         
31261         var _this = this;
31262         
31263         this.xhr.onload = function()
31264         {
31265             _this.xhrOnLoad(_this.xhr);
31266         }
31267         
31268         this.xhr.onerror = function()
31269         {
31270             _this.xhrOnError(_this.xhr);
31271         }
31272         
31273         var formData = new FormData();
31274
31275         formData.append('returnHTML', 'NO');
31276         
31277         if(crop){
31278             formData.append('crop', crop);
31279         }
31280         
31281         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31282             formData.append(this.paramName, file, file.name);
31283         }
31284         
31285         if(typeof(file.filename) != 'undefined'){
31286             formData.append('filename', file.filename);
31287         }
31288         
31289         if(typeof(file.mimetype) != 'undefined'){
31290             formData.append('mimetype', file.mimetype);
31291         }
31292         
31293         if(this.fireEvent('arrange', this, formData) != false){
31294             this.xhr.send(formData);
31295         };
31296     },
31297     
31298     xhrOnLoad : function(xhr)
31299     {
31300         if(this.loadMask){
31301             this.maskEl.unmask();
31302         }
31303         
31304         if (xhr.readyState !== 4) {
31305             this.fireEvent('exception', this, xhr);
31306             return;
31307         }
31308
31309         var response = Roo.decode(xhr.responseText);
31310         
31311         if(!response.success){
31312             this.fireEvent('exception', this, xhr);
31313             return;
31314         }
31315         
31316         var response = Roo.decode(xhr.responseText);
31317         
31318         this.fireEvent('upload', this, response);
31319         
31320     },
31321     
31322     xhrOnError : function()
31323     {
31324         if(this.loadMask){
31325             this.maskEl.unmask();
31326         }
31327         
31328         Roo.log('xhr on error');
31329         
31330         var response = Roo.decode(xhr.responseText);
31331           
31332         Roo.log(response);
31333         
31334     },
31335     
31336     prepare : function(file)
31337     {   
31338         if(this.loadMask){
31339             this.maskEl.mask(this.loadingText);
31340         }
31341         
31342         this.file = false;
31343         this.exif = {};
31344         
31345         if(typeof(file) === 'string'){
31346             this.loadCanvas(file);
31347             return;
31348         }
31349         
31350         if(!file || !this.urlAPI){
31351             return;
31352         }
31353         
31354         this.file = file;
31355         this.cropType = file.type;
31356         
31357         var _this = this;
31358         
31359         if(this.fireEvent('prepare', this, this.file) != false){
31360             
31361             var reader = new FileReader();
31362             
31363             reader.onload = function (e) {
31364                 if (e.target.error) {
31365                     Roo.log(e.target.error);
31366                     return;
31367                 }
31368                 
31369                 var buffer = e.target.result,
31370                     dataView = new DataView(buffer),
31371                     offset = 2,
31372                     maxOffset = dataView.byteLength - 4,
31373                     markerBytes,
31374                     markerLength;
31375                 
31376                 if (dataView.getUint16(0) === 0xffd8) {
31377                     while (offset < maxOffset) {
31378                         markerBytes = dataView.getUint16(offset);
31379                         
31380                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31381                             markerLength = dataView.getUint16(offset + 2) + 2;
31382                             if (offset + markerLength > dataView.byteLength) {
31383                                 Roo.log('Invalid meta data: Invalid segment size.');
31384                                 break;
31385                             }
31386                             
31387                             if(markerBytes == 0xffe1){
31388                                 _this.parseExifData(
31389                                     dataView,
31390                                     offset,
31391                                     markerLength
31392                                 );
31393                             }
31394                             
31395                             offset += markerLength;
31396                             
31397                             continue;
31398                         }
31399                         
31400                         break;
31401                     }
31402                     
31403                 }
31404                 
31405                 var url = _this.urlAPI.createObjectURL(_this.file);
31406                 
31407                 _this.loadCanvas(url);
31408                 
31409                 return;
31410             }
31411             
31412             reader.readAsArrayBuffer(this.file);
31413             
31414         }
31415         
31416     },
31417     
31418     parseExifData : function(dataView, offset, length)
31419     {
31420         var tiffOffset = offset + 10,
31421             littleEndian,
31422             dirOffset;
31423     
31424         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31425             // No Exif data, might be XMP data instead
31426             return;
31427         }
31428         
31429         // Check for the ASCII code for "Exif" (0x45786966):
31430         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31431             // No Exif data, might be XMP data instead
31432             return;
31433         }
31434         if (tiffOffset + 8 > dataView.byteLength) {
31435             Roo.log('Invalid Exif data: Invalid segment size.');
31436             return;
31437         }
31438         // Check for the two null bytes:
31439         if (dataView.getUint16(offset + 8) !== 0x0000) {
31440             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31441             return;
31442         }
31443         // Check the byte alignment:
31444         switch (dataView.getUint16(tiffOffset)) {
31445         case 0x4949:
31446             littleEndian = true;
31447             break;
31448         case 0x4D4D:
31449             littleEndian = false;
31450             break;
31451         default:
31452             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31453             return;
31454         }
31455         // Check for the TIFF tag marker (0x002A):
31456         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31457             Roo.log('Invalid Exif data: Missing TIFF marker.');
31458             return;
31459         }
31460         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31461         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31462         
31463         this.parseExifTags(
31464             dataView,
31465             tiffOffset,
31466             tiffOffset + dirOffset,
31467             littleEndian
31468         );
31469     },
31470     
31471     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31472     {
31473         var tagsNumber,
31474             dirEndOffset,
31475             i;
31476         if (dirOffset + 6 > dataView.byteLength) {
31477             Roo.log('Invalid Exif data: Invalid directory offset.');
31478             return;
31479         }
31480         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31481         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31482         if (dirEndOffset + 4 > dataView.byteLength) {
31483             Roo.log('Invalid Exif data: Invalid directory size.');
31484             return;
31485         }
31486         for (i = 0; i < tagsNumber; i += 1) {
31487             this.parseExifTag(
31488                 dataView,
31489                 tiffOffset,
31490                 dirOffset + 2 + 12 * i, // tag offset
31491                 littleEndian
31492             );
31493         }
31494         // Return the offset to the next directory:
31495         return dataView.getUint32(dirEndOffset, littleEndian);
31496     },
31497     
31498     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31499     {
31500         var tag = dataView.getUint16(offset, littleEndian);
31501         
31502         this.exif[tag] = this.getExifValue(
31503             dataView,
31504             tiffOffset,
31505             offset,
31506             dataView.getUint16(offset + 2, littleEndian), // tag type
31507             dataView.getUint32(offset + 4, littleEndian), // tag length
31508             littleEndian
31509         );
31510     },
31511     
31512     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31513     {
31514         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31515             tagSize,
31516             dataOffset,
31517             values,
31518             i,
31519             str,
31520             c;
31521     
31522         if (!tagType) {
31523             Roo.log('Invalid Exif data: Invalid tag type.');
31524             return;
31525         }
31526         
31527         tagSize = tagType.size * length;
31528         // Determine if the value is contained in the dataOffset bytes,
31529         // or if the value at the dataOffset is a pointer to the actual data:
31530         dataOffset = tagSize > 4 ?
31531                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31532         if (dataOffset + tagSize > dataView.byteLength) {
31533             Roo.log('Invalid Exif data: Invalid data offset.');
31534             return;
31535         }
31536         if (length === 1) {
31537             return tagType.getValue(dataView, dataOffset, littleEndian);
31538         }
31539         values = [];
31540         for (i = 0; i < length; i += 1) {
31541             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31542         }
31543         
31544         if (tagType.ascii) {
31545             str = '';
31546             // Concatenate the chars:
31547             for (i = 0; i < values.length; i += 1) {
31548                 c = values[i];
31549                 // Ignore the terminating NULL byte(s):
31550                 if (c === '\u0000') {
31551                     break;
31552                 }
31553                 str += c;
31554             }
31555             return str;
31556         }
31557         return values;
31558     }
31559     
31560 });
31561
31562 Roo.apply(Roo.bootstrap.UploadCropbox, {
31563     tags : {
31564         'Orientation': 0x0112
31565     },
31566     
31567     Orientation: {
31568             1: 0, //'top-left',
31569 //            2: 'top-right',
31570             3: 180, //'bottom-right',
31571 //            4: 'bottom-left',
31572 //            5: 'left-top',
31573             6: 90, //'right-top',
31574 //            7: 'right-bottom',
31575             8: 270 //'left-bottom'
31576     },
31577     
31578     exifTagTypes : {
31579         // byte, 8-bit unsigned int:
31580         1: {
31581             getValue: function (dataView, dataOffset) {
31582                 return dataView.getUint8(dataOffset);
31583             },
31584             size: 1
31585         },
31586         // ascii, 8-bit byte:
31587         2: {
31588             getValue: function (dataView, dataOffset) {
31589                 return String.fromCharCode(dataView.getUint8(dataOffset));
31590             },
31591             size: 1,
31592             ascii: true
31593         },
31594         // short, 16 bit int:
31595         3: {
31596             getValue: function (dataView, dataOffset, littleEndian) {
31597                 return dataView.getUint16(dataOffset, littleEndian);
31598             },
31599             size: 2
31600         },
31601         // long, 32 bit int:
31602         4: {
31603             getValue: function (dataView, dataOffset, littleEndian) {
31604                 return dataView.getUint32(dataOffset, littleEndian);
31605             },
31606             size: 4
31607         },
31608         // rational = two long values, first is numerator, second is denominator:
31609         5: {
31610             getValue: function (dataView, dataOffset, littleEndian) {
31611                 return dataView.getUint32(dataOffset, littleEndian) /
31612                     dataView.getUint32(dataOffset + 4, littleEndian);
31613             },
31614             size: 8
31615         },
31616         // slong, 32 bit signed int:
31617         9: {
31618             getValue: function (dataView, dataOffset, littleEndian) {
31619                 return dataView.getInt32(dataOffset, littleEndian);
31620             },
31621             size: 4
31622         },
31623         // srational, two slongs, first is numerator, second is denominator:
31624         10: {
31625             getValue: function (dataView, dataOffset, littleEndian) {
31626                 return dataView.getInt32(dataOffset, littleEndian) /
31627                     dataView.getInt32(dataOffset + 4, littleEndian);
31628             },
31629             size: 8
31630         }
31631     },
31632     
31633     footer : {
31634         STANDARD : [
31635             {
31636                 tag : 'div',
31637                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31638                 action : 'rotate-left',
31639                 cn : [
31640                     {
31641                         tag : 'button',
31642                         cls : 'btn btn-default',
31643                         html : '<i class="fa fa-undo"></i>'
31644                     }
31645                 ]
31646             },
31647             {
31648                 tag : 'div',
31649                 cls : 'btn-group roo-upload-cropbox-picture',
31650                 action : 'picture',
31651                 cn : [
31652                     {
31653                         tag : 'button',
31654                         cls : 'btn btn-default',
31655                         html : '<i class="fa fa-picture-o"></i>'
31656                     }
31657                 ]
31658             },
31659             {
31660                 tag : 'div',
31661                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31662                 action : 'rotate-right',
31663                 cn : [
31664                     {
31665                         tag : 'button',
31666                         cls : 'btn btn-default',
31667                         html : '<i class="fa fa-repeat"></i>'
31668                     }
31669                 ]
31670             }
31671         ],
31672         DOCUMENT : [
31673             {
31674                 tag : 'div',
31675                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31676                 action : 'rotate-left',
31677                 cn : [
31678                     {
31679                         tag : 'button',
31680                         cls : 'btn btn-default',
31681                         html : '<i class="fa fa-undo"></i>'
31682                     }
31683                 ]
31684             },
31685             {
31686                 tag : 'div',
31687                 cls : 'btn-group roo-upload-cropbox-download',
31688                 action : 'download',
31689                 cn : [
31690                     {
31691                         tag : 'button',
31692                         cls : 'btn btn-default',
31693                         html : '<i class="fa fa-download"></i>'
31694                     }
31695                 ]
31696             },
31697             {
31698                 tag : 'div',
31699                 cls : 'btn-group roo-upload-cropbox-crop',
31700                 action : 'crop',
31701                 cn : [
31702                     {
31703                         tag : 'button',
31704                         cls : 'btn btn-default',
31705                         html : '<i class="fa fa-crop"></i>'
31706                     }
31707                 ]
31708             },
31709             {
31710                 tag : 'div',
31711                 cls : 'btn-group roo-upload-cropbox-trash',
31712                 action : 'trash',
31713                 cn : [
31714                     {
31715                         tag : 'button',
31716                         cls : 'btn btn-default',
31717                         html : '<i class="fa fa-trash"></i>'
31718                     }
31719                 ]
31720             },
31721             {
31722                 tag : 'div',
31723                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31724                 action : 'rotate-right',
31725                 cn : [
31726                     {
31727                         tag : 'button',
31728                         cls : 'btn btn-default',
31729                         html : '<i class="fa fa-repeat"></i>'
31730                     }
31731                 ]
31732             }
31733         ],
31734         ROTATOR : [
31735             {
31736                 tag : 'div',
31737                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31738                 action : 'rotate-left',
31739                 cn : [
31740                     {
31741                         tag : 'button',
31742                         cls : 'btn btn-default',
31743                         html : '<i class="fa fa-undo"></i>'
31744                     }
31745                 ]
31746             },
31747             {
31748                 tag : 'div',
31749                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31750                 action : 'rotate-right',
31751                 cn : [
31752                     {
31753                         tag : 'button',
31754                         cls : 'btn btn-default',
31755                         html : '<i class="fa fa-repeat"></i>'
31756                     }
31757                 ]
31758             }
31759         ]
31760     }
31761 });
31762
31763 /*
31764 * Licence: LGPL
31765 */
31766
31767 /**
31768  * @class Roo.bootstrap.DocumentManager
31769  * @extends Roo.bootstrap.Component
31770  * Bootstrap DocumentManager class
31771  * @cfg {String} paramName default 'imageUpload'
31772  * @cfg {String} toolTipName default 'filename'
31773  * @cfg {String} method default POST
31774  * @cfg {String} url action url
31775  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31776  * @cfg {Boolean} multiple multiple upload default true
31777  * @cfg {Number} thumbSize default 300
31778  * @cfg {String} fieldLabel
31779  * @cfg {Number} labelWidth default 4
31780  * @cfg {String} labelAlign (left|top) default left
31781  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31782 * @cfg {Number} labellg set the width of label (1-12)
31783  * @cfg {Number} labelmd set the width of label (1-12)
31784  * @cfg {Number} labelsm set the width of label (1-12)
31785  * @cfg {Number} labelxs set the width of label (1-12)
31786  * 
31787  * @constructor
31788  * Create a new DocumentManager
31789  * @param {Object} config The config object
31790  */
31791
31792 Roo.bootstrap.DocumentManager = function(config){
31793     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31794     
31795     this.files = [];
31796     this.delegates = [];
31797     
31798     this.addEvents({
31799         /**
31800          * @event initial
31801          * Fire when initial the DocumentManager
31802          * @param {Roo.bootstrap.DocumentManager} this
31803          */
31804         "initial" : true,
31805         /**
31806          * @event inspect
31807          * inspect selected file
31808          * @param {Roo.bootstrap.DocumentManager} this
31809          * @param {File} file
31810          */
31811         "inspect" : true,
31812         /**
31813          * @event exception
31814          * Fire when xhr load exception
31815          * @param {Roo.bootstrap.DocumentManager} this
31816          * @param {XMLHttpRequest} xhr
31817          */
31818         "exception" : true,
31819         /**
31820          * @event afterupload
31821          * Fire when xhr load exception
31822          * @param {Roo.bootstrap.DocumentManager} this
31823          * @param {XMLHttpRequest} xhr
31824          */
31825         "afterupload" : true,
31826         /**
31827          * @event prepare
31828          * prepare the form data
31829          * @param {Roo.bootstrap.DocumentManager} this
31830          * @param {Object} formData
31831          */
31832         "prepare" : true,
31833         /**
31834          * @event remove
31835          * Fire when remove the file
31836          * @param {Roo.bootstrap.DocumentManager} this
31837          * @param {Object} file
31838          */
31839         "remove" : true,
31840         /**
31841          * @event refresh
31842          * Fire after refresh the file
31843          * @param {Roo.bootstrap.DocumentManager} this
31844          */
31845         "refresh" : true,
31846         /**
31847          * @event click
31848          * Fire after click the image
31849          * @param {Roo.bootstrap.DocumentManager} this
31850          * @param {Object} file
31851          */
31852         "click" : true,
31853         /**
31854          * @event edit
31855          * Fire when upload a image and editable set to true
31856          * @param {Roo.bootstrap.DocumentManager} this
31857          * @param {Object} file
31858          */
31859         "edit" : true,
31860         /**
31861          * @event beforeselectfile
31862          * Fire before select file
31863          * @param {Roo.bootstrap.DocumentManager} this
31864          */
31865         "beforeselectfile" : true,
31866         /**
31867          * @event process
31868          * Fire before process file
31869          * @param {Roo.bootstrap.DocumentManager} this
31870          * @param {Object} file
31871          */
31872         "process" : true,
31873         /**
31874          * @event previewrendered
31875          * Fire when preview rendered
31876          * @param {Roo.bootstrap.DocumentManager} this
31877          * @param {Object} file
31878          */
31879         "previewrendered" : true,
31880         /**
31881          */
31882         "previewResize" : true
31883         
31884     });
31885 };
31886
31887 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31888     
31889     boxes : 0,
31890     inputName : '',
31891     thumbSize : 300,
31892     multiple : true,
31893     files : false,
31894     method : 'POST',
31895     url : '',
31896     paramName : 'imageUpload',
31897     toolTipName : 'filename',
31898     fieldLabel : '',
31899     labelWidth : 4,
31900     labelAlign : 'left',
31901     editable : true,
31902     delegates : false,
31903     xhr : false, 
31904     
31905     labellg : 0,
31906     labelmd : 0,
31907     labelsm : 0,
31908     labelxs : 0,
31909     
31910     getAutoCreate : function()
31911     {   
31912         var managerWidget = {
31913             tag : 'div',
31914             cls : 'roo-document-manager',
31915             cn : [
31916                 {
31917                     tag : 'input',
31918                     cls : 'roo-document-manager-selector',
31919                     type : 'file'
31920                 },
31921                 {
31922                     tag : 'div',
31923                     cls : 'roo-document-manager-uploader',
31924                     cn : [
31925                         {
31926                             tag : 'div',
31927                             cls : 'roo-document-manager-upload-btn',
31928                             html : '<i class="fa fa-plus"></i>'
31929                         }
31930                     ]
31931                     
31932                 }
31933             ]
31934         };
31935         
31936         var content = [
31937             {
31938                 tag : 'div',
31939                 cls : 'column col-md-12',
31940                 cn : managerWidget
31941             }
31942         ];
31943         
31944         if(this.fieldLabel.length){
31945             
31946             content = [
31947                 {
31948                     tag : 'div',
31949                     cls : 'column col-md-12',
31950                     html : this.fieldLabel
31951                 },
31952                 {
31953                     tag : 'div',
31954                     cls : 'column col-md-12',
31955                     cn : managerWidget
31956                 }
31957             ];
31958
31959             if(this.labelAlign == 'left'){
31960                 content = [
31961                     {
31962                         tag : 'div',
31963                         cls : 'column',
31964                         html : this.fieldLabel
31965                     },
31966                     {
31967                         tag : 'div',
31968                         cls : 'column',
31969                         cn : managerWidget
31970                     }
31971                 ];
31972                 
31973                 if(this.labelWidth > 12){
31974                     content[0].style = "width: " + this.labelWidth + 'px';
31975                 }
31976
31977                 if(this.labelWidth < 13 && this.labelmd == 0){
31978                     this.labelmd = this.labelWidth;
31979                 }
31980
31981                 if(this.labellg > 0){
31982                     content[0].cls += ' col-lg-' + this.labellg;
31983                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31984                 }
31985
31986                 if(this.labelmd > 0){
31987                     content[0].cls += ' col-md-' + this.labelmd;
31988                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31989                 }
31990
31991                 if(this.labelsm > 0){
31992                     content[0].cls += ' col-sm-' + this.labelsm;
31993                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31994                 }
31995
31996                 if(this.labelxs > 0){
31997                     content[0].cls += ' col-xs-' + this.labelxs;
31998                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31999                 }
32000                 
32001             }
32002         }
32003         
32004         var cfg = {
32005             tag : 'div',
32006             cls : 'row clearfix',
32007             cn : content
32008         };
32009         
32010         return cfg;
32011         
32012     },
32013     
32014     initEvents : function()
32015     {
32016         this.managerEl = this.el.select('.roo-document-manager', true).first();
32017         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32018         
32019         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32020         this.selectorEl.hide();
32021         
32022         if(this.multiple){
32023             this.selectorEl.attr('multiple', 'multiple');
32024         }
32025         
32026         this.selectorEl.on('change', this.onFileSelected, this);
32027         
32028         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32029         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32030         
32031         this.uploader.on('click', this.onUploaderClick, this);
32032         
32033         this.renderProgressDialog();
32034         
32035         var _this = this;
32036         
32037         window.addEventListener("resize", function() { _this.refresh(); } );
32038         
32039         this.fireEvent('initial', this);
32040     },
32041     
32042     renderProgressDialog : function()
32043     {
32044         var _this = this;
32045         
32046         this.progressDialog = new Roo.bootstrap.Modal({
32047             cls : 'roo-document-manager-progress-dialog',
32048             allow_close : false,
32049             animate : false,
32050             title : '',
32051             buttons : [
32052                 {
32053                     name  :'cancel',
32054                     weight : 'danger',
32055                     html : 'Cancel'
32056                 }
32057             ], 
32058             listeners : { 
32059                 btnclick : function() {
32060                     _this.uploadCancel();
32061                     this.hide();
32062                 }
32063             }
32064         });
32065          
32066         this.progressDialog.render(Roo.get(document.body));
32067          
32068         this.progress = new Roo.bootstrap.Progress({
32069             cls : 'roo-document-manager-progress',
32070             active : true,
32071             striped : true
32072         });
32073         
32074         this.progress.render(this.progressDialog.getChildContainer());
32075         
32076         this.progressBar = new Roo.bootstrap.ProgressBar({
32077             cls : 'roo-document-manager-progress-bar',
32078             aria_valuenow : 0,
32079             aria_valuemin : 0,
32080             aria_valuemax : 12,
32081             panel : 'success'
32082         });
32083         
32084         this.progressBar.render(this.progress.getChildContainer());
32085     },
32086     
32087     onUploaderClick : function(e)
32088     {
32089         e.preventDefault();
32090      
32091         if(this.fireEvent('beforeselectfile', this) != false){
32092             this.selectorEl.dom.click();
32093         }
32094         
32095     },
32096     
32097     onFileSelected : function(e)
32098     {
32099         e.preventDefault();
32100         
32101         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32102             return;
32103         }
32104         
32105         Roo.each(this.selectorEl.dom.files, function(file){
32106             if(this.fireEvent('inspect', this, file) != false){
32107                 this.files.push(file);
32108             }
32109         }, this);
32110         
32111         this.queue();
32112         
32113     },
32114     
32115     queue : function()
32116     {
32117         this.selectorEl.dom.value = '';
32118         
32119         if(!this.files || !this.files.length){
32120             return;
32121         }
32122         
32123         if(this.boxes > 0 && this.files.length > this.boxes){
32124             this.files = this.files.slice(0, this.boxes);
32125         }
32126         
32127         this.uploader.show();
32128         
32129         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32130             this.uploader.hide();
32131         }
32132         
32133         var _this = this;
32134         
32135         var files = [];
32136         
32137         var docs = [];
32138         
32139         Roo.each(this.files, function(file){
32140             
32141             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32142                 var f = this.renderPreview(file);
32143                 files.push(f);
32144                 return;
32145             }
32146             
32147             if(file.type.indexOf('image') != -1){
32148                 this.delegates.push(
32149                     (function(){
32150                         _this.process(file);
32151                     }).createDelegate(this)
32152                 );
32153         
32154                 return;
32155             }
32156             
32157             docs.push(
32158                 (function(){
32159                     _this.process(file);
32160                 }).createDelegate(this)
32161             );
32162             
32163         }, this);
32164         
32165         this.files = files;
32166         
32167         this.delegates = this.delegates.concat(docs);
32168         
32169         if(!this.delegates.length){
32170             this.refresh();
32171             return;
32172         }
32173         
32174         this.progressBar.aria_valuemax = this.delegates.length;
32175         
32176         this.arrange();
32177         
32178         return;
32179     },
32180     
32181     arrange : function()
32182     {
32183         if(!this.delegates.length){
32184             this.progressDialog.hide();
32185             this.refresh();
32186             return;
32187         }
32188         
32189         var delegate = this.delegates.shift();
32190         
32191         this.progressDialog.show();
32192         
32193         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32194         
32195         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32196         
32197         delegate();
32198     },
32199     
32200     refresh : function()
32201     {
32202         this.uploader.show();
32203         
32204         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32205             this.uploader.hide();
32206         }
32207         
32208         Roo.isTouch ? this.closable(false) : this.closable(true);
32209         
32210         this.fireEvent('refresh', this);
32211     },
32212     
32213     onRemove : function(e, el, o)
32214     {
32215         e.preventDefault();
32216         
32217         this.fireEvent('remove', this, o);
32218         
32219     },
32220     
32221     remove : function(o)
32222     {
32223         var files = [];
32224         
32225         Roo.each(this.files, function(file){
32226             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32227                 files.push(file);
32228                 return;
32229             }
32230
32231             o.target.remove();
32232
32233         }, this);
32234         
32235         this.files = files;
32236         
32237         this.refresh();
32238     },
32239     
32240     clear : function()
32241     {
32242         Roo.each(this.files, function(file){
32243             if(!file.target){
32244                 return;
32245             }
32246             
32247             file.target.remove();
32248
32249         }, this);
32250         
32251         this.files = [];
32252         
32253         this.refresh();
32254     },
32255     
32256     onClick : function(e, el, o)
32257     {
32258         e.preventDefault();
32259         
32260         this.fireEvent('click', this, o);
32261         
32262     },
32263     
32264     closable : function(closable)
32265     {
32266         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32267             
32268             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32269             
32270             if(closable){
32271                 el.show();
32272                 return;
32273             }
32274             
32275             el.hide();
32276             
32277         }, this);
32278     },
32279     
32280     xhrOnLoad : function(xhr)
32281     {
32282         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32283             el.remove();
32284         }, this);
32285         
32286         if (xhr.readyState !== 4) {
32287             this.arrange();
32288             this.fireEvent('exception', this, xhr);
32289             return;
32290         }
32291
32292         var response = Roo.decode(xhr.responseText);
32293         
32294         if(!response.success){
32295             this.arrange();
32296             this.fireEvent('exception', this, xhr);
32297             return;
32298         }
32299         
32300         var file = this.renderPreview(response.data);
32301         
32302         this.files.push(file);
32303         
32304         this.arrange();
32305         
32306         this.fireEvent('afterupload', this, xhr);
32307         
32308     },
32309     
32310     xhrOnError : function(xhr)
32311     {
32312         Roo.log('xhr on error');
32313         
32314         var response = Roo.decode(xhr.responseText);
32315           
32316         Roo.log(response);
32317         
32318         this.arrange();
32319     },
32320     
32321     process : function(file)
32322     {
32323         if(this.fireEvent('process', this, file) !== false){
32324             if(this.editable && file.type.indexOf('image') != -1){
32325                 this.fireEvent('edit', this, file);
32326                 return;
32327             }
32328
32329             this.uploadStart(file, false);
32330
32331             return;
32332         }
32333         
32334     },
32335     
32336     uploadStart : function(file, crop)
32337     {
32338         this.xhr = new XMLHttpRequest();
32339         
32340         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32341             this.arrange();
32342             return;
32343         }
32344         
32345         file.xhr = this.xhr;
32346             
32347         this.managerEl.createChild({
32348             tag : 'div',
32349             cls : 'roo-document-manager-loading',
32350             cn : [
32351                 {
32352                     tag : 'div',
32353                     tooltip : file.name,
32354                     cls : 'roo-document-manager-thumb',
32355                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32356                 }
32357             ]
32358
32359         });
32360
32361         this.xhr.open(this.method, this.url, true);
32362         
32363         var headers = {
32364             "Accept": "application/json",
32365             "Cache-Control": "no-cache",
32366             "X-Requested-With": "XMLHttpRequest"
32367         };
32368         
32369         for (var headerName in headers) {
32370             var headerValue = headers[headerName];
32371             if (headerValue) {
32372                 this.xhr.setRequestHeader(headerName, headerValue);
32373             }
32374         }
32375         
32376         var _this = this;
32377         
32378         this.xhr.onload = function()
32379         {
32380             _this.xhrOnLoad(_this.xhr);
32381         }
32382         
32383         this.xhr.onerror = function()
32384         {
32385             _this.xhrOnError(_this.xhr);
32386         }
32387         
32388         var formData = new FormData();
32389
32390         formData.append('returnHTML', 'NO');
32391         
32392         if(crop){
32393             formData.append('crop', crop);
32394         }
32395         
32396         formData.append(this.paramName, file, file.name);
32397         
32398         var options = {
32399             file : file, 
32400             manually : false
32401         };
32402         
32403         if(this.fireEvent('prepare', this, formData, options) != false){
32404             
32405             if(options.manually){
32406                 return;
32407             }
32408             
32409             this.xhr.send(formData);
32410             return;
32411         };
32412         
32413         this.uploadCancel();
32414     },
32415     
32416     uploadCancel : function()
32417     {
32418         if (this.xhr) {
32419             this.xhr.abort();
32420         }
32421         
32422         this.delegates = [];
32423         
32424         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32425             el.remove();
32426         }, this);
32427         
32428         this.arrange();
32429     },
32430     
32431     renderPreview : function(file)
32432     {
32433         if(typeof(file.target) != 'undefined' && file.target){
32434             return file;
32435         }
32436         
32437         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32438         
32439         var previewEl = this.managerEl.createChild({
32440             tag : 'div',
32441             cls : 'roo-document-manager-preview',
32442             cn : [
32443                 {
32444                     tag : 'div',
32445                     tooltip : file[this.toolTipName],
32446                     cls : 'roo-document-manager-thumb',
32447                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32448                 },
32449                 {
32450                     tag : 'button',
32451                     cls : 'close',
32452                     html : '<i class="fa fa-times-circle"></i>'
32453                 }
32454             ]
32455         });
32456
32457         var close = previewEl.select('button.close', true).first();
32458
32459         close.on('click', this.onRemove, this, file);
32460
32461         file.target = previewEl;
32462
32463         var image = previewEl.select('img', true).first();
32464         
32465         var _this = this;
32466         
32467         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32468         
32469         image.on('click', this.onClick, this, file);
32470         
32471         this.fireEvent('previewrendered', this, file);
32472         
32473         return file;
32474         
32475     },
32476     
32477     onPreviewLoad : function(file, image)
32478     {
32479         if(typeof(file.target) == 'undefined' || !file.target){
32480             return;
32481         }
32482         
32483         var width = image.dom.naturalWidth || image.dom.width;
32484         var height = image.dom.naturalHeight || image.dom.height;
32485         
32486         if(!this.previewResize) {
32487             return;
32488         }
32489         
32490         if(width > height){
32491             file.target.addClass('wide');
32492             return;
32493         }
32494         
32495         file.target.addClass('tall');
32496         return;
32497         
32498     },
32499     
32500     uploadFromSource : function(file, crop)
32501     {
32502         this.xhr = new XMLHttpRequest();
32503         
32504         this.managerEl.createChild({
32505             tag : 'div',
32506             cls : 'roo-document-manager-loading',
32507             cn : [
32508                 {
32509                     tag : 'div',
32510                     tooltip : file.name,
32511                     cls : 'roo-document-manager-thumb',
32512                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32513                 }
32514             ]
32515
32516         });
32517
32518         this.xhr.open(this.method, this.url, true);
32519         
32520         var headers = {
32521             "Accept": "application/json",
32522             "Cache-Control": "no-cache",
32523             "X-Requested-With": "XMLHttpRequest"
32524         };
32525         
32526         for (var headerName in headers) {
32527             var headerValue = headers[headerName];
32528             if (headerValue) {
32529                 this.xhr.setRequestHeader(headerName, headerValue);
32530             }
32531         }
32532         
32533         var _this = this;
32534         
32535         this.xhr.onload = function()
32536         {
32537             _this.xhrOnLoad(_this.xhr);
32538         }
32539         
32540         this.xhr.onerror = function()
32541         {
32542             _this.xhrOnError(_this.xhr);
32543         }
32544         
32545         var formData = new FormData();
32546
32547         formData.append('returnHTML', 'NO');
32548         
32549         formData.append('crop', crop);
32550         
32551         if(typeof(file.filename) != 'undefined'){
32552             formData.append('filename', file.filename);
32553         }
32554         
32555         if(typeof(file.mimetype) != 'undefined'){
32556             formData.append('mimetype', file.mimetype);
32557         }
32558         
32559         Roo.log(formData);
32560         
32561         if(this.fireEvent('prepare', this, formData) != false){
32562             this.xhr.send(formData);
32563         };
32564     }
32565 });
32566
32567 /*
32568 * Licence: LGPL
32569 */
32570
32571 /**
32572  * @class Roo.bootstrap.DocumentViewer
32573  * @extends Roo.bootstrap.Component
32574  * Bootstrap DocumentViewer class
32575  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32576  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32577  * 
32578  * @constructor
32579  * Create a new DocumentViewer
32580  * @param {Object} config The config object
32581  */
32582
32583 Roo.bootstrap.DocumentViewer = function(config){
32584     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32585     
32586     this.addEvents({
32587         /**
32588          * @event initial
32589          * Fire after initEvent
32590          * @param {Roo.bootstrap.DocumentViewer} this
32591          */
32592         "initial" : true,
32593         /**
32594          * @event click
32595          * Fire after click
32596          * @param {Roo.bootstrap.DocumentViewer} this
32597          */
32598         "click" : true,
32599         /**
32600          * @event download
32601          * Fire after download button
32602          * @param {Roo.bootstrap.DocumentViewer} this
32603          */
32604         "download" : true,
32605         /**
32606          * @event trash
32607          * Fire after trash button
32608          * @param {Roo.bootstrap.DocumentViewer} this
32609          */
32610         "trash" : true
32611         
32612     });
32613 };
32614
32615 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32616     
32617     showDownload : true,
32618     
32619     showTrash : true,
32620     
32621     getAutoCreate : function()
32622     {
32623         var cfg = {
32624             tag : 'div',
32625             cls : 'roo-document-viewer',
32626             cn : [
32627                 {
32628                     tag : 'div',
32629                     cls : 'roo-document-viewer-body',
32630                     cn : [
32631                         {
32632                             tag : 'div',
32633                             cls : 'roo-document-viewer-thumb',
32634                             cn : [
32635                                 {
32636                                     tag : 'img',
32637                                     cls : 'roo-document-viewer-image'
32638                                 }
32639                             ]
32640                         }
32641                     ]
32642                 },
32643                 {
32644                     tag : 'div',
32645                     cls : 'roo-document-viewer-footer',
32646                     cn : {
32647                         tag : 'div',
32648                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32649                         cn : [
32650                             {
32651                                 tag : 'div',
32652                                 cls : 'btn-group roo-document-viewer-download',
32653                                 cn : [
32654                                     {
32655                                         tag : 'button',
32656                                         cls : 'btn btn-default',
32657                                         html : '<i class="fa fa-download"></i>'
32658                                     }
32659                                 ]
32660                             },
32661                             {
32662                                 tag : 'div',
32663                                 cls : 'btn-group roo-document-viewer-trash',
32664                                 cn : [
32665                                     {
32666                                         tag : 'button',
32667                                         cls : 'btn btn-default',
32668                                         html : '<i class="fa fa-trash"></i>'
32669                                     }
32670                                 ]
32671                             }
32672                         ]
32673                     }
32674                 }
32675             ]
32676         };
32677         
32678         return cfg;
32679     },
32680     
32681     initEvents : function()
32682     {
32683         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32684         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32685         
32686         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32687         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32688         
32689         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32690         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32691         
32692         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32693         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32694         
32695         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32696         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32697         
32698         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32699         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32700         
32701         this.bodyEl.on('click', this.onClick, this);
32702         this.downloadBtn.on('click', this.onDownload, this);
32703         this.trashBtn.on('click', this.onTrash, this);
32704         
32705         this.downloadBtn.hide();
32706         this.trashBtn.hide();
32707         
32708         if(this.showDownload){
32709             this.downloadBtn.show();
32710         }
32711         
32712         if(this.showTrash){
32713             this.trashBtn.show();
32714         }
32715         
32716         if(!this.showDownload && !this.showTrash) {
32717             this.footerEl.hide();
32718         }
32719         
32720     },
32721     
32722     initial : function()
32723     {
32724         this.fireEvent('initial', this);
32725         
32726     },
32727     
32728     onClick : function(e)
32729     {
32730         e.preventDefault();
32731         
32732         this.fireEvent('click', this);
32733     },
32734     
32735     onDownload : function(e)
32736     {
32737         e.preventDefault();
32738         
32739         this.fireEvent('download', this);
32740     },
32741     
32742     onTrash : function(e)
32743     {
32744         e.preventDefault();
32745         
32746         this.fireEvent('trash', this);
32747     }
32748     
32749 });
32750 /*
32751  * - LGPL
32752  *
32753  * nav progress bar
32754  * 
32755  */
32756
32757 /**
32758  * @class Roo.bootstrap.NavProgressBar
32759  * @extends Roo.bootstrap.Component
32760  * Bootstrap NavProgressBar class
32761  * 
32762  * @constructor
32763  * Create a new nav progress bar
32764  * @param {Object} config The config object
32765  */
32766
32767 Roo.bootstrap.NavProgressBar = function(config){
32768     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32769
32770     this.bullets = this.bullets || [];
32771    
32772 //    Roo.bootstrap.NavProgressBar.register(this);
32773      this.addEvents({
32774         /**
32775              * @event changed
32776              * Fires when the active item changes
32777              * @param {Roo.bootstrap.NavProgressBar} this
32778              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32779              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32780          */
32781         'changed': true
32782      });
32783     
32784 };
32785
32786 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32787     
32788     bullets : [],
32789     barItems : [],
32790     
32791     getAutoCreate : function()
32792     {
32793         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32794         
32795         cfg = {
32796             tag : 'div',
32797             cls : 'roo-navigation-bar-group',
32798             cn : [
32799                 {
32800                     tag : 'div',
32801                     cls : 'roo-navigation-top-bar'
32802                 },
32803                 {
32804                     tag : 'div',
32805                     cls : 'roo-navigation-bullets-bar',
32806                     cn : [
32807                         {
32808                             tag : 'ul',
32809                             cls : 'roo-navigation-bar'
32810                         }
32811                     ]
32812                 },
32813                 
32814                 {
32815                     tag : 'div',
32816                     cls : 'roo-navigation-bottom-bar'
32817                 }
32818             ]
32819             
32820         };
32821         
32822         return cfg;
32823         
32824     },
32825     
32826     initEvents: function() 
32827     {
32828         
32829     },
32830     
32831     onRender : function(ct, position) 
32832     {
32833         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32834         
32835         if(this.bullets.length){
32836             Roo.each(this.bullets, function(b){
32837                this.addItem(b);
32838             }, this);
32839         }
32840         
32841         this.format();
32842         
32843     },
32844     
32845     addItem : function(cfg)
32846     {
32847         var item = new Roo.bootstrap.NavProgressItem(cfg);
32848         
32849         item.parentId = this.id;
32850         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32851         
32852         if(cfg.html){
32853             var top = new Roo.bootstrap.Element({
32854                 tag : 'div',
32855                 cls : 'roo-navigation-bar-text'
32856             });
32857             
32858             var bottom = new Roo.bootstrap.Element({
32859                 tag : 'div',
32860                 cls : 'roo-navigation-bar-text'
32861             });
32862             
32863             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32864             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32865             
32866             var topText = new Roo.bootstrap.Element({
32867                 tag : 'span',
32868                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32869             });
32870             
32871             var bottomText = new Roo.bootstrap.Element({
32872                 tag : 'span',
32873                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32874             });
32875             
32876             topText.onRender(top.el, null);
32877             bottomText.onRender(bottom.el, null);
32878             
32879             item.topEl = top;
32880             item.bottomEl = bottom;
32881         }
32882         
32883         this.barItems.push(item);
32884         
32885         return item;
32886     },
32887     
32888     getActive : function()
32889     {
32890         var active = false;
32891         
32892         Roo.each(this.barItems, function(v){
32893             
32894             if (!v.isActive()) {
32895                 return;
32896             }
32897             
32898             active = v;
32899             return false;
32900             
32901         });
32902         
32903         return active;
32904     },
32905     
32906     setActiveItem : function(item)
32907     {
32908         var prev = false;
32909         
32910         Roo.each(this.barItems, function(v){
32911             if (v.rid == item.rid) {
32912                 return ;
32913             }
32914             
32915             if (v.isActive()) {
32916                 v.setActive(false);
32917                 prev = v;
32918             }
32919         });
32920
32921         item.setActive(true);
32922         
32923         this.fireEvent('changed', this, item, prev);
32924     },
32925     
32926     getBarItem: function(rid)
32927     {
32928         var ret = false;
32929         
32930         Roo.each(this.barItems, function(e) {
32931             if (e.rid != rid) {
32932                 return;
32933             }
32934             
32935             ret =  e;
32936             return false;
32937         });
32938         
32939         return ret;
32940     },
32941     
32942     indexOfItem : function(item)
32943     {
32944         var index = false;
32945         
32946         Roo.each(this.barItems, function(v, i){
32947             
32948             if (v.rid != item.rid) {
32949                 return;
32950             }
32951             
32952             index = i;
32953             return false
32954         });
32955         
32956         return index;
32957     },
32958     
32959     setActiveNext : function()
32960     {
32961         var i = this.indexOfItem(this.getActive());
32962         
32963         if (i > this.barItems.length) {
32964             return;
32965         }
32966         
32967         this.setActiveItem(this.barItems[i+1]);
32968     },
32969     
32970     setActivePrev : function()
32971     {
32972         var i = this.indexOfItem(this.getActive());
32973         
32974         if (i  < 1) {
32975             return;
32976         }
32977         
32978         this.setActiveItem(this.barItems[i-1]);
32979     },
32980     
32981     format : function()
32982     {
32983         if(!this.barItems.length){
32984             return;
32985         }
32986      
32987         var width = 100 / this.barItems.length;
32988         
32989         Roo.each(this.barItems, function(i){
32990             i.el.setStyle('width', width + '%');
32991             i.topEl.el.setStyle('width', width + '%');
32992             i.bottomEl.el.setStyle('width', width + '%');
32993         }, this);
32994         
32995     }
32996     
32997 });
32998 /*
32999  * - LGPL
33000  *
33001  * Nav Progress Item
33002  * 
33003  */
33004
33005 /**
33006  * @class Roo.bootstrap.NavProgressItem
33007  * @extends Roo.bootstrap.Component
33008  * Bootstrap NavProgressItem class
33009  * @cfg {String} rid the reference id
33010  * @cfg {Boolean} active (true|false) Is item active default false
33011  * @cfg {Boolean} disabled (true|false) Is item active default false
33012  * @cfg {String} html
33013  * @cfg {String} position (top|bottom) text position default bottom
33014  * @cfg {String} icon show icon instead of number
33015  * 
33016  * @constructor
33017  * Create a new NavProgressItem
33018  * @param {Object} config The config object
33019  */
33020 Roo.bootstrap.NavProgressItem = function(config){
33021     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33022     this.addEvents({
33023         // raw events
33024         /**
33025          * @event click
33026          * The raw click event for the entire grid.
33027          * @param {Roo.bootstrap.NavProgressItem} this
33028          * @param {Roo.EventObject} e
33029          */
33030         "click" : true
33031     });
33032    
33033 };
33034
33035 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33036     
33037     rid : '',
33038     active : false,
33039     disabled : false,
33040     html : '',
33041     position : 'bottom',
33042     icon : false,
33043     
33044     getAutoCreate : function()
33045     {
33046         var iconCls = 'roo-navigation-bar-item-icon';
33047         
33048         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33049         
33050         var cfg = {
33051             tag: 'li',
33052             cls: 'roo-navigation-bar-item',
33053             cn : [
33054                 {
33055                     tag : 'i',
33056                     cls : iconCls
33057                 }
33058             ]
33059         };
33060         
33061         if(this.active){
33062             cfg.cls += ' active';
33063         }
33064         if(this.disabled){
33065             cfg.cls += ' disabled';
33066         }
33067         
33068         return cfg;
33069     },
33070     
33071     disable : function()
33072     {
33073         this.setDisabled(true);
33074     },
33075     
33076     enable : function()
33077     {
33078         this.setDisabled(false);
33079     },
33080     
33081     initEvents: function() 
33082     {
33083         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33084         
33085         this.iconEl.on('click', this.onClick, this);
33086     },
33087     
33088     onClick : function(e)
33089     {
33090         e.preventDefault();
33091         
33092         if(this.disabled){
33093             return;
33094         }
33095         
33096         if(this.fireEvent('click', this, e) === false){
33097             return;
33098         };
33099         
33100         this.parent().setActiveItem(this);
33101     },
33102     
33103     isActive: function () 
33104     {
33105         return this.active;
33106     },
33107     
33108     setActive : function(state)
33109     {
33110         if(this.active == state){
33111             return;
33112         }
33113         
33114         this.active = state;
33115         
33116         if (state) {
33117             this.el.addClass('active');
33118             return;
33119         }
33120         
33121         this.el.removeClass('active');
33122         
33123         return;
33124     },
33125     
33126     setDisabled : function(state)
33127     {
33128         if(this.disabled == state){
33129             return;
33130         }
33131         
33132         this.disabled = state;
33133         
33134         if (state) {
33135             this.el.addClass('disabled');
33136             return;
33137         }
33138         
33139         this.el.removeClass('disabled');
33140     },
33141     
33142     tooltipEl : function()
33143     {
33144         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33145     }
33146 });
33147  
33148
33149  /*
33150  * - LGPL
33151  *
33152  * FieldLabel
33153  * 
33154  */
33155
33156 /**
33157  * @class Roo.bootstrap.FieldLabel
33158  * @extends Roo.bootstrap.Component
33159  * Bootstrap FieldLabel class
33160  * @cfg {String} html contents of the element
33161  * @cfg {String} tag tag of the element default label
33162  * @cfg {String} cls class of the element
33163  * @cfg {String} target label target 
33164  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33165  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33166  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33167  * @cfg {String} iconTooltip default "This field is required"
33168  * @cfg {String} indicatorpos (left|right) default left
33169  * 
33170  * @constructor
33171  * Create a new FieldLabel
33172  * @param {Object} config The config object
33173  */
33174
33175 Roo.bootstrap.FieldLabel = function(config){
33176     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33177     
33178     this.addEvents({
33179             /**
33180              * @event invalid
33181              * Fires after the field has been marked as invalid.
33182              * @param {Roo.form.FieldLabel} this
33183              * @param {String} msg The validation message
33184              */
33185             invalid : true,
33186             /**
33187              * @event valid
33188              * Fires after the field has been validated with no errors.
33189              * @param {Roo.form.FieldLabel} this
33190              */
33191             valid : true
33192         });
33193 };
33194
33195 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33196     
33197     tag: 'label',
33198     cls: '',
33199     html: '',
33200     target: '',
33201     allowBlank : true,
33202     invalidClass : 'has-warning',
33203     validClass : 'has-success',
33204     iconTooltip : 'This field is required',
33205     indicatorpos : 'left',
33206     
33207     getAutoCreate : function(){
33208         
33209         var cls = "";
33210         if (!this.allowBlank) {
33211             cls  = "visible";
33212         }
33213         
33214         var cfg = {
33215             tag : this.tag,
33216             cls : 'roo-bootstrap-field-label ' + this.cls,
33217             for : this.target,
33218             cn : [
33219                 {
33220                     tag : 'i',
33221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33222                     tooltip : this.iconTooltip
33223                 },
33224                 {
33225                     tag : 'span',
33226                     html : this.html
33227                 }
33228             ] 
33229         };
33230         
33231         if(this.indicatorpos == 'right'){
33232             var cfg = {
33233                 tag : this.tag,
33234                 cls : 'roo-bootstrap-field-label ' + this.cls,
33235                 for : this.target,
33236                 cn : [
33237                     {
33238                         tag : 'span',
33239                         html : this.html
33240                     },
33241                     {
33242                         tag : 'i',
33243                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33244                         tooltip : this.iconTooltip
33245                     }
33246                 ] 
33247             };
33248         }
33249         
33250         return cfg;
33251     },
33252     
33253     initEvents: function() 
33254     {
33255         Roo.bootstrap.Element.superclass.initEvents.call(this);
33256         
33257         this.indicator = this.indicatorEl();
33258         
33259         if(this.indicator){
33260             this.indicator.removeClass('visible');
33261             this.indicator.addClass('invisible');
33262         }
33263         
33264         Roo.bootstrap.FieldLabel.register(this);
33265     },
33266     
33267     indicatorEl : function()
33268     {
33269         var indicator = this.el.select('i.roo-required-indicator',true).first();
33270         
33271         if(!indicator){
33272             return false;
33273         }
33274         
33275         return indicator;
33276         
33277     },
33278     
33279     /**
33280      * Mark this field as valid
33281      */
33282     markValid : function()
33283     {
33284         if(this.indicator){
33285             this.indicator.removeClass('visible');
33286             this.indicator.addClass('invisible');
33287         }
33288         if (Roo.bootstrap.version == 3) {
33289             this.el.removeClass(this.invalidClass);
33290             this.el.addClass(this.validClass);
33291         } else {
33292             this.el.removeClass('is-invalid');
33293             this.el.addClass('is-valid');
33294         }
33295         
33296         
33297         this.fireEvent('valid', this);
33298     },
33299     
33300     /**
33301      * Mark this field as invalid
33302      * @param {String} msg The validation message
33303      */
33304     markInvalid : function(msg)
33305     {
33306         if(this.indicator){
33307             this.indicator.removeClass('invisible');
33308             this.indicator.addClass('visible');
33309         }
33310           if (Roo.bootstrap.version == 3) {
33311             this.el.removeClass(this.validClass);
33312             this.el.addClass(this.invalidClass);
33313         } else {
33314             this.el.removeClass('is-valid');
33315             this.el.addClass('is-invalid');
33316         }
33317         
33318         
33319         this.fireEvent('invalid', this, msg);
33320     }
33321     
33322    
33323 });
33324
33325 Roo.apply(Roo.bootstrap.FieldLabel, {
33326     
33327     groups: {},
33328     
33329      /**
33330     * register a FieldLabel Group
33331     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33332     */
33333     register : function(label)
33334     {
33335         if(this.groups.hasOwnProperty(label.target)){
33336             return;
33337         }
33338      
33339         this.groups[label.target] = label;
33340         
33341     },
33342     /**
33343     * fetch a FieldLabel Group based on the target
33344     * @param {string} target
33345     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33346     */
33347     get: function(target) {
33348         if (typeof(this.groups[target]) == 'undefined') {
33349             return false;
33350         }
33351         
33352         return this.groups[target] ;
33353     }
33354 });
33355
33356  
33357
33358  /*
33359  * - LGPL
33360  *
33361  * page DateSplitField.
33362  * 
33363  */
33364
33365
33366 /**
33367  * @class Roo.bootstrap.DateSplitField
33368  * @extends Roo.bootstrap.Component
33369  * Bootstrap DateSplitField class
33370  * @cfg {string} fieldLabel - the label associated
33371  * @cfg {Number} labelWidth set the width of label (0-12)
33372  * @cfg {String} labelAlign (top|left)
33373  * @cfg {Boolean} dayAllowBlank (true|false) default false
33374  * @cfg {Boolean} monthAllowBlank (true|false) default false
33375  * @cfg {Boolean} yearAllowBlank (true|false) default false
33376  * @cfg {string} dayPlaceholder 
33377  * @cfg {string} monthPlaceholder
33378  * @cfg {string} yearPlaceholder
33379  * @cfg {string} dayFormat default 'd'
33380  * @cfg {string} monthFormat default 'm'
33381  * @cfg {string} yearFormat default 'Y'
33382  * @cfg {Number} labellg set the width of label (1-12)
33383  * @cfg {Number} labelmd set the width of label (1-12)
33384  * @cfg {Number} labelsm set the width of label (1-12)
33385  * @cfg {Number} labelxs set the width of label (1-12)
33386
33387  *     
33388  * @constructor
33389  * Create a new DateSplitField
33390  * @param {Object} config The config object
33391  */
33392
33393 Roo.bootstrap.DateSplitField = function(config){
33394     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33395     
33396     this.addEvents({
33397         // raw events
33398          /**
33399          * @event years
33400          * getting the data of years
33401          * @param {Roo.bootstrap.DateSplitField} this
33402          * @param {Object} years
33403          */
33404         "years" : true,
33405         /**
33406          * @event days
33407          * getting the data of days
33408          * @param {Roo.bootstrap.DateSplitField} this
33409          * @param {Object} days
33410          */
33411         "days" : true,
33412         /**
33413          * @event invalid
33414          * Fires after the field has been marked as invalid.
33415          * @param {Roo.form.Field} this
33416          * @param {String} msg The validation message
33417          */
33418         invalid : true,
33419        /**
33420          * @event valid
33421          * Fires after the field has been validated with no errors.
33422          * @param {Roo.form.Field} this
33423          */
33424         valid : true
33425     });
33426 };
33427
33428 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33429     
33430     fieldLabel : '',
33431     labelAlign : 'top',
33432     labelWidth : 3,
33433     dayAllowBlank : false,
33434     monthAllowBlank : false,
33435     yearAllowBlank : false,
33436     dayPlaceholder : '',
33437     monthPlaceholder : '',
33438     yearPlaceholder : '',
33439     dayFormat : 'd',
33440     monthFormat : 'm',
33441     yearFormat : 'Y',
33442     isFormField : true,
33443     labellg : 0,
33444     labelmd : 0,
33445     labelsm : 0,
33446     labelxs : 0,
33447     
33448     getAutoCreate : function()
33449     {
33450         var cfg = {
33451             tag : 'div',
33452             cls : 'row roo-date-split-field-group',
33453             cn : [
33454                 {
33455                     tag : 'input',
33456                     type : 'hidden',
33457                     cls : 'form-hidden-field roo-date-split-field-group-value',
33458                     name : this.name
33459                 }
33460             ]
33461         };
33462         
33463         var labelCls = 'col-md-12';
33464         var contentCls = 'col-md-4';
33465         
33466         if(this.fieldLabel){
33467             
33468             var label = {
33469                 tag : 'div',
33470                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33471                 cn : [
33472                     {
33473                         tag : 'label',
33474                         html : this.fieldLabel
33475                     }
33476                 ]
33477             };
33478             
33479             if(this.labelAlign == 'left'){
33480             
33481                 if(this.labelWidth > 12){
33482                     label.style = "width: " + this.labelWidth + 'px';
33483                 }
33484
33485                 if(this.labelWidth < 13 && this.labelmd == 0){
33486                     this.labelmd = this.labelWidth;
33487                 }
33488
33489                 if(this.labellg > 0){
33490                     labelCls = ' col-lg-' + this.labellg;
33491                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33492                 }
33493
33494                 if(this.labelmd > 0){
33495                     labelCls = ' col-md-' + this.labelmd;
33496                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33497                 }
33498
33499                 if(this.labelsm > 0){
33500                     labelCls = ' col-sm-' + this.labelsm;
33501                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33502                 }
33503
33504                 if(this.labelxs > 0){
33505                     labelCls = ' col-xs-' + this.labelxs;
33506                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33507                 }
33508             }
33509             
33510             label.cls += ' ' + labelCls;
33511             
33512             cfg.cn.push(label);
33513         }
33514         
33515         Roo.each(['day', 'month', 'year'], function(t){
33516             cfg.cn.push({
33517                 tag : 'div',
33518                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33519             });
33520         }, this);
33521         
33522         return cfg;
33523     },
33524     
33525     inputEl: function ()
33526     {
33527         return this.el.select('.roo-date-split-field-group-value', true).first();
33528     },
33529     
33530     onRender : function(ct, position) 
33531     {
33532         var _this = this;
33533         
33534         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33535         
33536         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33537         
33538         this.dayField = new Roo.bootstrap.ComboBox({
33539             allowBlank : this.dayAllowBlank,
33540             alwaysQuery : true,
33541             displayField : 'value',
33542             editable : false,
33543             fieldLabel : '',
33544             forceSelection : true,
33545             mode : 'local',
33546             placeholder : this.dayPlaceholder,
33547             selectOnFocus : true,
33548             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33549             triggerAction : 'all',
33550             typeAhead : true,
33551             valueField : 'value',
33552             store : new Roo.data.SimpleStore({
33553                 data : (function() {    
33554                     var days = [];
33555                     _this.fireEvent('days', _this, days);
33556                     return days;
33557                 })(),
33558                 fields : [ 'value' ]
33559             }),
33560             listeners : {
33561                 select : function (_self, record, index)
33562                 {
33563                     _this.setValue(_this.getValue());
33564                 }
33565             }
33566         });
33567
33568         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33569         
33570         this.monthField = new Roo.bootstrap.MonthField({
33571             after : '<i class=\"fa fa-calendar\"></i>',
33572             allowBlank : this.monthAllowBlank,
33573             placeholder : this.monthPlaceholder,
33574             readOnly : true,
33575             listeners : {
33576                 render : function (_self)
33577                 {
33578                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33579                         e.preventDefault();
33580                         _self.focus();
33581                     });
33582                 },
33583                 select : function (_self, oldvalue, newvalue)
33584                 {
33585                     _this.setValue(_this.getValue());
33586                 }
33587             }
33588         });
33589         
33590         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33591         
33592         this.yearField = new Roo.bootstrap.ComboBox({
33593             allowBlank : this.yearAllowBlank,
33594             alwaysQuery : true,
33595             displayField : 'value',
33596             editable : false,
33597             fieldLabel : '',
33598             forceSelection : true,
33599             mode : 'local',
33600             placeholder : this.yearPlaceholder,
33601             selectOnFocus : true,
33602             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33603             triggerAction : 'all',
33604             typeAhead : true,
33605             valueField : 'value',
33606             store : new Roo.data.SimpleStore({
33607                 data : (function() {
33608                     var years = [];
33609                     _this.fireEvent('years', _this, years);
33610                     return years;
33611                 })(),
33612                 fields : [ 'value' ]
33613             }),
33614             listeners : {
33615                 select : function (_self, record, index)
33616                 {
33617                     _this.setValue(_this.getValue());
33618                 }
33619             }
33620         });
33621
33622         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33623     },
33624     
33625     setValue : function(v, format)
33626     {
33627         this.inputEl.dom.value = v;
33628         
33629         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33630         
33631         var d = Date.parseDate(v, f);
33632         
33633         if(!d){
33634             this.validate();
33635             return;
33636         }
33637         
33638         this.setDay(d.format(this.dayFormat));
33639         this.setMonth(d.format(this.monthFormat));
33640         this.setYear(d.format(this.yearFormat));
33641         
33642         this.validate();
33643         
33644         return;
33645     },
33646     
33647     setDay : function(v)
33648     {
33649         this.dayField.setValue(v);
33650         this.inputEl.dom.value = this.getValue();
33651         this.validate();
33652         return;
33653     },
33654     
33655     setMonth : function(v)
33656     {
33657         this.monthField.setValue(v, true);
33658         this.inputEl.dom.value = this.getValue();
33659         this.validate();
33660         return;
33661     },
33662     
33663     setYear : function(v)
33664     {
33665         this.yearField.setValue(v);
33666         this.inputEl.dom.value = this.getValue();
33667         this.validate();
33668         return;
33669     },
33670     
33671     getDay : function()
33672     {
33673         return this.dayField.getValue();
33674     },
33675     
33676     getMonth : function()
33677     {
33678         return this.monthField.getValue();
33679     },
33680     
33681     getYear : function()
33682     {
33683         return this.yearField.getValue();
33684     },
33685     
33686     getValue : function()
33687     {
33688         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33689         
33690         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33691         
33692         return date;
33693     },
33694     
33695     reset : function()
33696     {
33697         this.setDay('');
33698         this.setMonth('');
33699         this.setYear('');
33700         this.inputEl.dom.value = '';
33701         this.validate();
33702         return;
33703     },
33704     
33705     validate : function()
33706     {
33707         var d = this.dayField.validate();
33708         var m = this.monthField.validate();
33709         var y = this.yearField.validate();
33710         
33711         var valid = true;
33712         
33713         if(
33714                 (!this.dayAllowBlank && !d) ||
33715                 (!this.monthAllowBlank && !m) ||
33716                 (!this.yearAllowBlank && !y)
33717         ){
33718             valid = false;
33719         }
33720         
33721         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33722             return valid;
33723         }
33724         
33725         if(valid){
33726             this.markValid();
33727             return valid;
33728         }
33729         
33730         this.markInvalid();
33731         
33732         return valid;
33733     },
33734     
33735     markValid : function()
33736     {
33737         
33738         var label = this.el.select('label', true).first();
33739         var icon = this.el.select('i.fa-star', true).first();
33740
33741         if(label && icon){
33742             icon.remove();
33743         }
33744         
33745         this.fireEvent('valid', this);
33746     },
33747     
33748      /**
33749      * Mark this field as invalid
33750      * @param {String} msg The validation message
33751      */
33752     markInvalid : function(msg)
33753     {
33754         
33755         var label = this.el.select('label', true).first();
33756         var icon = this.el.select('i.fa-star', true).first();
33757
33758         if(label && !icon){
33759             this.el.select('.roo-date-split-field-label', true).createChild({
33760                 tag : 'i',
33761                 cls : 'text-danger fa fa-lg fa-star',
33762                 tooltip : 'This field is required',
33763                 style : 'margin-right:5px;'
33764             }, label, true);
33765         }
33766         
33767         this.fireEvent('invalid', this, msg);
33768     },
33769     
33770     clearInvalid : function()
33771     {
33772         var label = this.el.select('label', true).first();
33773         var icon = this.el.select('i.fa-star', true).first();
33774
33775         if(label && icon){
33776             icon.remove();
33777         }
33778         
33779         this.fireEvent('valid', this);
33780     },
33781     
33782     getName: function()
33783     {
33784         return this.name;
33785     }
33786     
33787 });
33788
33789  /**
33790  *
33791  * This is based on 
33792  * http://masonry.desandro.com
33793  *
33794  * The idea is to render all the bricks based on vertical width...
33795  *
33796  * The original code extends 'outlayer' - we might need to use that....
33797  * 
33798  */
33799
33800
33801 /**
33802  * @class Roo.bootstrap.LayoutMasonry
33803  * @extends Roo.bootstrap.Component
33804  * Bootstrap Layout Masonry class
33805  * 
33806  * @constructor
33807  * Create a new Element
33808  * @param {Object} config The config object
33809  */
33810
33811 Roo.bootstrap.LayoutMasonry = function(config){
33812     
33813     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33814     
33815     this.bricks = [];
33816     
33817     Roo.bootstrap.LayoutMasonry.register(this);
33818     
33819     this.addEvents({
33820         // raw events
33821         /**
33822          * @event layout
33823          * Fire after layout the items
33824          * @param {Roo.bootstrap.LayoutMasonry} this
33825          * @param {Roo.EventObject} e
33826          */
33827         "layout" : true
33828     });
33829     
33830 };
33831
33832 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33833     
33834     /**
33835      * @cfg {Boolean} isLayoutInstant = no animation?
33836      */   
33837     isLayoutInstant : false, // needed?
33838    
33839     /**
33840      * @cfg {Number} boxWidth  width of the columns
33841      */   
33842     boxWidth : 450,
33843     
33844       /**
33845      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33846      */   
33847     boxHeight : 0,
33848     
33849     /**
33850      * @cfg {Number} padWidth padding below box..
33851      */   
33852     padWidth : 10, 
33853     
33854     /**
33855      * @cfg {Number} gutter gutter width..
33856      */   
33857     gutter : 10,
33858     
33859      /**
33860      * @cfg {Number} maxCols maximum number of columns
33861      */   
33862     
33863     maxCols: 0,
33864     
33865     /**
33866      * @cfg {Boolean} isAutoInitial defalut true
33867      */   
33868     isAutoInitial : true, 
33869     
33870     containerWidth: 0,
33871     
33872     /**
33873      * @cfg {Boolean} isHorizontal defalut false
33874      */   
33875     isHorizontal : false, 
33876
33877     currentSize : null,
33878     
33879     tag: 'div',
33880     
33881     cls: '',
33882     
33883     bricks: null, //CompositeElement
33884     
33885     cols : 1,
33886     
33887     _isLayoutInited : false,
33888     
33889 //    isAlternative : false, // only use for vertical layout...
33890     
33891     /**
33892      * @cfg {Number} alternativePadWidth padding below box..
33893      */   
33894     alternativePadWidth : 50,
33895     
33896     selectedBrick : [],
33897     
33898     getAutoCreate : function(){
33899         
33900         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33901         
33902         var cfg = {
33903             tag: this.tag,
33904             cls: 'blog-masonary-wrapper ' + this.cls,
33905             cn : {
33906                 cls : 'mas-boxes masonary'
33907             }
33908         };
33909         
33910         return cfg;
33911     },
33912     
33913     getChildContainer: function( )
33914     {
33915         if (this.boxesEl) {
33916             return this.boxesEl;
33917         }
33918         
33919         this.boxesEl = this.el.select('.mas-boxes').first();
33920         
33921         return this.boxesEl;
33922     },
33923     
33924     
33925     initEvents : function()
33926     {
33927         var _this = this;
33928         
33929         if(this.isAutoInitial){
33930             Roo.log('hook children rendered');
33931             this.on('childrenrendered', function() {
33932                 Roo.log('children rendered');
33933                 _this.initial();
33934             } ,this);
33935         }
33936     },
33937     
33938     initial : function()
33939     {
33940         this.selectedBrick = [];
33941         
33942         this.currentSize = this.el.getBox(true);
33943         
33944         Roo.EventManager.onWindowResize(this.resize, this); 
33945
33946         if(!this.isAutoInitial){
33947             this.layout();
33948             return;
33949         }
33950         
33951         this.layout();
33952         
33953         return;
33954         //this.layout.defer(500,this);
33955         
33956     },
33957     
33958     resize : function()
33959     {
33960         var cs = this.el.getBox(true);
33961         
33962         if (
33963                 this.currentSize.width == cs.width && 
33964                 this.currentSize.x == cs.x && 
33965                 this.currentSize.height == cs.height && 
33966                 this.currentSize.y == cs.y 
33967         ) {
33968             Roo.log("no change in with or X or Y");
33969             return;
33970         }
33971         
33972         this.currentSize = cs;
33973         
33974         this.layout();
33975         
33976     },
33977     
33978     layout : function()
33979     {   
33980         this._resetLayout();
33981         
33982         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33983         
33984         this.layoutItems( isInstant );
33985       
33986         this._isLayoutInited = true;
33987         
33988         this.fireEvent('layout', this);
33989         
33990     },
33991     
33992     _resetLayout : function()
33993     {
33994         if(this.isHorizontal){
33995             this.horizontalMeasureColumns();
33996             return;
33997         }
33998         
33999         this.verticalMeasureColumns();
34000         
34001     },
34002     
34003     verticalMeasureColumns : function()
34004     {
34005         this.getContainerWidth();
34006         
34007 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34008 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34009 //            return;
34010 //        }
34011         
34012         var boxWidth = this.boxWidth + this.padWidth;
34013         
34014         if(this.containerWidth < this.boxWidth){
34015             boxWidth = this.containerWidth
34016         }
34017         
34018         var containerWidth = this.containerWidth;
34019         
34020         var cols = Math.floor(containerWidth / boxWidth);
34021         
34022         this.cols = Math.max( cols, 1 );
34023         
34024         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34025         
34026         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34027         
34028         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34029         
34030         this.colWidth = boxWidth + avail - this.padWidth;
34031         
34032         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34033         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34034     },
34035     
34036     horizontalMeasureColumns : function()
34037     {
34038         this.getContainerWidth();
34039         
34040         var boxWidth = this.boxWidth;
34041         
34042         if(this.containerWidth < boxWidth){
34043             boxWidth = this.containerWidth;
34044         }
34045         
34046         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34047         
34048         this.el.setHeight(boxWidth);
34049         
34050     },
34051     
34052     getContainerWidth : function()
34053     {
34054         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34055     },
34056     
34057     layoutItems : function( isInstant )
34058     {
34059         Roo.log(this.bricks);
34060         
34061         var items = Roo.apply([], this.bricks);
34062         
34063         if(this.isHorizontal){
34064             this._horizontalLayoutItems( items , isInstant );
34065             return;
34066         }
34067         
34068 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34069 //            this._verticalAlternativeLayoutItems( items , isInstant );
34070 //            return;
34071 //        }
34072         
34073         this._verticalLayoutItems( items , isInstant );
34074         
34075     },
34076     
34077     _verticalLayoutItems : function ( items , isInstant)
34078     {
34079         if ( !items || !items.length ) {
34080             return;
34081         }
34082         
34083         var standard = [
34084             ['xs', 'xs', 'xs', 'tall'],
34085             ['xs', 'xs', 'tall'],
34086             ['xs', 'xs', 'sm'],
34087             ['xs', 'xs', 'xs'],
34088             ['xs', 'tall'],
34089             ['xs', 'sm'],
34090             ['xs', 'xs'],
34091             ['xs'],
34092             
34093             ['sm', 'xs', 'xs'],
34094             ['sm', 'xs'],
34095             ['sm'],
34096             
34097             ['tall', 'xs', 'xs', 'xs'],
34098             ['tall', 'xs', 'xs'],
34099             ['tall', 'xs'],
34100             ['tall']
34101             
34102         ];
34103         
34104         var queue = [];
34105         
34106         var boxes = [];
34107         
34108         var box = [];
34109         
34110         Roo.each(items, function(item, k){
34111             
34112             switch (item.size) {
34113                 // these layouts take up a full box,
34114                 case 'md' :
34115                 case 'md-left' :
34116                 case 'md-right' :
34117                 case 'wide' :
34118                     
34119                     if(box.length){
34120                         boxes.push(box);
34121                         box = [];
34122                     }
34123                     
34124                     boxes.push([item]);
34125                     
34126                     break;
34127                     
34128                 case 'xs' :
34129                 case 'sm' :
34130                 case 'tall' :
34131                     
34132                     box.push(item);
34133                     
34134                     break;
34135                 default :
34136                     break;
34137                     
34138             }
34139             
34140         }, this);
34141         
34142         if(box.length){
34143             boxes.push(box);
34144             box = [];
34145         }
34146         
34147         var filterPattern = function(box, length)
34148         {
34149             if(!box.length){
34150                 return;
34151             }
34152             
34153             var match = false;
34154             
34155             var pattern = box.slice(0, length);
34156             
34157             var format = [];
34158             
34159             Roo.each(pattern, function(i){
34160                 format.push(i.size);
34161             }, this);
34162             
34163             Roo.each(standard, function(s){
34164                 
34165                 if(String(s) != String(format)){
34166                     return;
34167                 }
34168                 
34169                 match = true;
34170                 return false;
34171                 
34172             }, this);
34173             
34174             if(!match && length == 1){
34175                 return;
34176             }
34177             
34178             if(!match){
34179                 filterPattern(box, length - 1);
34180                 return;
34181             }
34182                 
34183             queue.push(pattern);
34184
34185             box = box.slice(length, box.length);
34186
34187             filterPattern(box, 4);
34188
34189             return;
34190             
34191         }
34192         
34193         Roo.each(boxes, function(box, k){
34194             
34195             if(!box.length){
34196                 return;
34197             }
34198             
34199             if(box.length == 1){
34200                 queue.push(box);
34201                 return;
34202             }
34203             
34204             filterPattern(box, 4);
34205             
34206         }, this);
34207         
34208         this._processVerticalLayoutQueue( queue, isInstant );
34209         
34210     },
34211     
34212 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34213 //    {
34214 //        if ( !items || !items.length ) {
34215 //            return;
34216 //        }
34217 //
34218 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34219 //        
34220 //    },
34221     
34222     _horizontalLayoutItems : function ( items , isInstant)
34223     {
34224         if ( !items || !items.length || items.length < 3) {
34225             return;
34226         }
34227         
34228         items.reverse();
34229         
34230         var eItems = items.slice(0, 3);
34231         
34232         items = items.slice(3, items.length);
34233         
34234         var standard = [
34235             ['xs', 'xs', 'xs', 'wide'],
34236             ['xs', 'xs', 'wide'],
34237             ['xs', 'xs', 'sm'],
34238             ['xs', 'xs', 'xs'],
34239             ['xs', 'wide'],
34240             ['xs', 'sm'],
34241             ['xs', 'xs'],
34242             ['xs'],
34243             
34244             ['sm', 'xs', 'xs'],
34245             ['sm', 'xs'],
34246             ['sm'],
34247             
34248             ['wide', 'xs', 'xs', 'xs'],
34249             ['wide', 'xs', 'xs'],
34250             ['wide', 'xs'],
34251             ['wide'],
34252             
34253             ['wide-thin']
34254         ];
34255         
34256         var queue = [];
34257         
34258         var boxes = [];
34259         
34260         var box = [];
34261         
34262         Roo.each(items, function(item, k){
34263             
34264             switch (item.size) {
34265                 case 'md' :
34266                 case 'md-left' :
34267                 case 'md-right' :
34268                 case 'tall' :
34269                     
34270                     if(box.length){
34271                         boxes.push(box);
34272                         box = [];
34273                     }
34274                     
34275                     boxes.push([item]);
34276                     
34277                     break;
34278                     
34279                 case 'xs' :
34280                 case 'sm' :
34281                 case 'wide' :
34282                 case 'wide-thin' :
34283                     
34284                     box.push(item);
34285                     
34286                     break;
34287                 default :
34288                     break;
34289                     
34290             }
34291             
34292         }, this);
34293         
34294         if(box.length){
34295             boxes.push(box);
34296             box = [];
34297         }
34298         
34299         var filterPattern = function(box, length)
34300         {
34301             if(!box.length){
34302                 return;
34303             }
34304             
34305             var match = false;
34306             
34307             var pattern = box.slice(0, length);
34308             
34309             var format = [];
34310             
34311             Roo.each(pattern, function(i){
34312                 format.push(i.size);
34313             }, this);
34314             
34315             Roo.each(standard, function(s){
34316                 
34317                 if(String(s) != String(format)){
34318                     return;
34319                 }
34320                 
34321                 match = true;
34322                 return false;
34323                 
34324             }, this);
34325             
34326             if(!match && length == 1){
34327                 return;
34328             }
34329             
34330             if(!match){
34331                 filterPattern(box, length - 1);
34332                 return;
34333             }
34334                 
34335             queue.push(pattern);
34336
34337             box = box.slice(length, box.length);
34338
34339             filterPattern(box, 4);
34340
34341             return;
34342             
34343         }
34344         
34345         Roo.each(boxes, function(box, k){
34346             
34347             if(!box.length){
34348                 return;
34349             }
34350             
34351             if(box.length == 1){
34352                 queue.push(box);
34353                 return;
34354             }
34355             
34356             filterPattern(box, 4);
34357             
34358         }, this);
34359         
34360         
34361         var prune = [];
34362         
34363         var pos = this.el.getBox(true);
34364         
34365         var minX = pos.x;
34366         
34367         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34368         
34369         var hit_end = false;
34370         
34371         Roo.each(queue, function(box){
34372             
34373             if(hit_end){
34374                 
34375                 Roo.each(box, function(b){
34376                 
34377                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34378                     b.el.hide();
34379
34380                 }, this);
34381
34382                 return;
34383             }
34384             
34385             var mx = 0;
34386             
34387             Roo.each(box, function(b){
34388                 
34389                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34390                 b.el.show();
34391
34392                 mx = Math.max(mx, b.x);
34393                 
34394             }, this);
34395             
34396             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34397             
34398             if(maxX < minX){
34399                 
34400                 Roo.each(box, function(b){
34401                 
34402                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34403                     b.el.hide();
34404                     
34405                 }, this);
34406                 
34407                 hit_end = true;
34408                 
34409                 return;
34410             }
34411             
34412             prune.push(box);
34413             
34414         }, this);
34415         
34416         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34417     },
34418     
34419     /** Sets position of item in DOM
34420     * @param {Element} item
34421     * @param {Number} x - horizontal position
34422     * @param {Number} y - vertical position
34423     * @param {Boolean} isInstant - disables transitions
34424     */
34425     _processVerticalLayoutQueue : function( queue, isInstant )
34426     {
34427         var pos = this.el.getBox(true);
34428         var x = pos.x;
34429         var y = pos.y;
34430         var maxY = [];
34431         
34432         for (var i = 0; i < this.cols; i++){
34433             maxY[i] = pos.y;
34434         }
34435         
34436         Roo.each(queue, function(box, k){
34437             
34438             var col = k % this.cols;
34439             
34440             Roo.each(box, function(b,kk){
34441                 
34442                 b.el.position('absolute');
34443                 
34444                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34445                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34446                 
34447                 if(b.size == 'md-left' || b.size == 'md-right'){
34448                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34449                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34450                 }
34451                 
34452                 b.el.setWidth(width);
34453                 b.el.setHeight(height);
34454                 // iframe?
34455                 b.el.select('iframe',true).setSize(width,height);
34456                 
34457             }, this);
34458             
34459             for (var i = 0; i < this.cols; i++){
34460                 
34461                 if(maxY[i] < maxY[col]){
34462                     col = i;
34463                     continue;
34464                 }
34465                 
34466                 col = Math.min(col, i);
34467                 
34468             }
34469             
34470             x = pos.x + col * (this.colWidth + this.padWidth);
34471             
34472             y = maxY[col];
34473             
34474             var positions = [];
34475             
34476             switch (box.length){
34477                 case 1 :
34478                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34479                     break;
34480                 case 2 :
34481                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34482                     break;
34483                 case 3 :
34484                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34485                     break;
34486                 case 4 :
34487                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34488                     break;
34489                 default :
34490                     break;
34491             }
34492             
34493             Roo.each(box, function(b,kk){
34494                 
34495                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34496                 
34497                 var sz = b.el.getSize();
34498                 
34499                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34500                 
34501             }, this);
34502             
34503         }, this);
34504         
34505         var mY = 0;
34506         
34507         for (var i = 0; i < this.cols; i++){
34508             mY = Math.max(mY, maxY[i]);
34509         }
34510         
34511         this.el.setHeight(mY - pos.y);
34512         
34513     },
34514     
34515 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34516 //    {
34517 //        var pos = this.el.getBox(true);
34518 //        var x = pos.x;
34519 //        var y = pos.y;
34520 //        var maxX = pos.right;
34521 //        
34522 //        var maxHeight = 0;
34523 //        
34524 //        Roo.each(items, function(item, k){
34525 //            
34526 //            var c = k % 2;
34527 //            
34528 //            item.el.position('absolute');
34529 //                
34530 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34531 //
34532 //            item.el.setWidth(width);
34533 //
34534 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34535 //
34536 //            item.el.setHeight(height);
34537 //            
34538 //            if(c == 0){
34539 //                item.el.setXY([x, y], isInstant ? false : true);
34540 //            } else {
34541 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34542 //            }
34543 //            
34544 //            y = y + height + this.alternativePadWidth;
34545 //            
34546 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34547 //            
34548 //        }, this);
34549 //        
34550 //        this.el.setHeight(maxHeight);
34551 //        
34552 //    },
34553     
34554     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34555     {
34556         var pos = this.el.getBox(true);
34557         
34558         var minX = pos.x;
34559         var minY = pos.y;
34560         
34561         var maxX = pos.right;
34562         
34563         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34564         
34565         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34566         
34567         Roo.each(queue, function(box, k){
34568             
34569             Roo.each(box, function(b, kk){
34570                 
34571                 b.el.position('absolute');
34572                 
34573                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34574                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34575                 
34576                 if(b.size == 'md-left' || b.size == 'md-right'){
34577                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34578                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34579                 }
34580                 
34581                 b.el.setWidth(width);
34582                 b.el.setHeight(height);
34583                 
34584             }, this);
34585             
34586             if(!box.length){
34587                 return;
34588             }
34589             
34590             var positions = [];
34591             
34592             switch (box.length){
34593                 case 1 :
34594                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34595                     break;
34596                 case 2 :
34597                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34598                     break;
34599                 case 3 :
34600                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34601                     break;
34602                 case 4 :
34603                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34604                     break;
34605                 default :
34606                     break;
34607             }
34608             
34609             Roo.each(box, function(b,kk){
34610                 
34611                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34612                 
34613                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34614                 
34615             }, this);
34616             
34617         }, this);
34618         
34619     },
34620     
34621     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34622     {
34623         Roo.each(eItems, function(b,k){
34624             
34625             b.size = (k == 0) ? 'sm' : 'xs';
34626             b.x = (k == 0) ? 2 : 1;
34627             b.y = (k == 0) ? 2 : 1;
34628             
34629             b.el.position('absolute');
34630             
34631             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34632                 
34633             b.el.setWidth(width);
34634             
34635             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34636             
34637             b.el.setHeight(height);
34638             
34639         }, this);
34640
34641         var positions = [];
34642         
34643         positions.push({
34644             x : maxX - this.unitWidth * 2 - this.gutter,
34645             y : minY
34646         });
34647         
34648         positions.push({
34649             x : maxX - this.unitWidth,
34650             y : minY + (this.unitWidth + this.gutter) * 2
34651         });
34652         
34653         positions.push({
34654             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34655             y : minY
34656         });
34657         
34658         Roo.each(eItems, function(b,k){
34659             
34660             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34661
34662         }, this);
34663         
34664     },
34665     
34666     getVerticalOneBoxColPositions : function(x, y, box)
34667     {
34668         var pos = [];
34669         
34670         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34671         
34672         if(box[0].size == 'md-left'){
34673             rand = 0;
34674         }
34675         
34676         if(box[0].size == 'md-right'){
34677             rand = 1;
34678         }
34679         
34680         pos.push({
34681             x : x + (this.unitWidth + this.gutter) * rand,
34682             y : y
34683         });
34684         
34685         return pos;
34686     },
34687     
34688     getVerticalTwoBoxColPositions : function(x, y, box)
34689     {
34690         var pos = [];
34691         
34692         if(box[0].size == 'xs'){
34693             
34694             pos.push({
34695                 x : x,
34696                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34697             });
34698
34699             pos.push({
34700                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34701                 y : y
34702             });
34703             
34704             return pos;
34705             
34706         }
34707         
34708         pos.push({
34709             x : x,
34710             y : y
34711         });
34712
34713         pos.push({
34714             x : x + (this.unitWidth + this.gutter) * 2,
34715             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34716         });
34717         
34718         return pos;
34719         
34720     },
34721     
34722     getVerticalThreeBoxColPositions : function(x, y, box)
34723     {
34724         var pos = [];
34725         
34726         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34727             
34728             pos.push({
34729                 x : x,
34730                 y : y
34731             });
34732
34733             pos.push({
34734                 x : x + (this.unitWidth + this.gutter) * 1,
34735                 y : y
34736             });
34737             
34738             pos.push({
34739                 x : x + (this.unitWidth + this.gutter) * 2,
34740                 y : y
34741             });
34742             
34743             return pos;
34744             
34745         }
34746         
34747         if(box[0].size == 'xs' && box[1].size == 'xs'){
34748             
34749             pos.push({
34750                 x : x,
34751                 y : y
34752             });
34753
34754             pos.push({
34755                 x : x,
34756                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34757             });
34758             
34759             pos.push({
34760                 x : x + (this.unitWidth + this.gutter) * 1,
34761                 y : y
34762             });
34763             
34764             return pos;
34765             
34766         }
34767         
34768         pos.push({
34769             x : x,
34770             y : y
34771         });
34772
34773         pos.push({
34774             x : x + (this.unitWidth + this.gutter) * 2,
34775             y : y
34776         });
34777
34778         pos.push({
34779             x : x + (this.unitWidth + this.gutter) * 2,
34780             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34781         });
34782             
34783         return pos;
34784         
34785     },
34786     
34787     getVerticalFourBoxColPositions : function(x, y, box)
34788     {
34789         var pos = [];
34790         
34791         if(box[0].size == 'xs'){
34792             
34793             pos.push({
34794                 x : x,
34795                 y : y
34796             });
34797
34798             pos.push({
34799                 x : x,
34800                 y : y + (this.unitHeight + this.gutter) * 1
34801             });
34802             
34803             pos.push({
34804                 x : x,
34805                 y : y + (this.unitHeight + this.gutter) * 2
34806             });
34807             
34808             pos.push({
34809                 x : x + (this.unitWidth + this.gutter) * 1,
34810                 y : y
34811             });
34812             
34813             return pos;
34814             
34815         }
34816         
34817         pos.push({
34818             x : x,
34819             y : y
34820         });
34821
34822         pos.push({
34823             x : x + (this.unitWidth + this.gutter) * 2,
34824             y : y
34825         });
34826
34827         pos.push({
34828             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34829             y : y + (this.unitHeight + this.gutter) * 1
34830         });
34831
34832         pos.push({
34833             x : x + (this.unitWidth + this.gutter) * 2,
34834             y : y + (this.unitWidth + this.gutter) * 2
34835         });
34836
34837         return pos;
34838         
34839     },
34840     
34841     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34842     {
34843         var pos = [];
34844         
34845         if(box[0].size == 'md-left'){
34846             pos.push({
34847                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34848                 y : minY
34849             });
34850             
34851             return pos;
34852         }
34853         
34854         if(box[0].size == 'md-right'){
34855             pos.push({
34856                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34857                 y : minY + (this.unitWidth + this.gutter) * 1
34858             });
34859             
34860             return pos;
34861         }
34862         
34863         var rand = Math.floor(Math.random() * (4 - box[0].y));
34864         
34865         pos.push({
34866             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34867             y : minY + (this.unitWidth + this.gutter) * rand
34868         });
34869         
34870         return pos;
34871         
34872     },
34873     
34874     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34875     {
34876         var pos = [];
34877         
34878         if(box[0].size == 'xs'){
34879             
34880             pos.push({
34881                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34882                 y : minY
34883             });
34884
34885             pos.push({
34886                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34887                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34888             });
34889             
34890             return pos;
34891             
34892         }
34893         
34894         pos.push({
34895             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34896             y : minY
34897         });
34898
34899         pos.push({
34900             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34901             y : minY + (this.unitWidth + this.gutter) * 2
34902         });
34903         
34904         return pos;
34905         
34906     },
34907     
34908     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34909     {
34910         var pos = [];
34911         
34912         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34913             
34914             pos.push({
34915                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34916                 y : minY
34917             });
34918
34919             pos.push({
34920                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34921                 y : minY + (this.unitWidth + this.gutter) * 1
34922             });
34923             
34924             pos.push({
34925                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34926                 y : minY + (this.unitWidth + this.gutter) * 2
34927             });
34928             
34929             return pos;
34930             
34931         }
34932         
34933         if(box[0].size == 'xs' && box[1].size == 'xs'){
34934             
34935             pos.push({
34936                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34937                 y : minY
34938             });
34939
34940             pos.push({
34941                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34942                 y : minY
34943             });
34944             
34945             pos.push({
34946                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34947                 y : minY + (this.unitWidth + this.gutter) * 1
34948             });
34949             
34950             return pos;
34951             
34952         }
34953         
34954         pos.push({
34955             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34956             y : minY
34957         });
34958
34959         pos.push({
34960             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34961             y : minY + (this.unitWidth + this.gutter) * 2
34962         });
34963
34964         pos.push({
34965             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34966             y : minY + (this.unitWidth + this.gutter) * 2
34967         });
34968             
34969         return pos;
34970         
34971     },
34972     
34973     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34974     {
34975         var pos = [];
34976         
34977         if(box[0].size == 'xs'){
34978             
34979             pos.push({
34980                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34981                 y : minY
34982             });
34983
34984             pos.push({
34985                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34986                 y : minY
34987             });
34988             
34989             pos.push({
34990                 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),
34991                 y : minY
34992             });
34993             
34994             pos.push({
34995                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34996                 y : minY + (this.unitWidth + this.gutter) * 1
34997             });
34998             
34999             return pos;
35000             
35001         }
35002         
35003         pos.push({
35004             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35005             y : minY
35006         });
35007         
35008         pos.push({
35009             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35010             y : minY + (this.unitWidth + this.gutter) * 2
35011         });
35012         
35013         pos.push({
35014             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35015             y : minY + (this.unitWidth + this.gutter) * 2
35016         });
35017         
35018         pos.push({
35019             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),
35020             y : minY + (this.unitWidth + this.gutter) * 2
35021         });
35022
35023         return pos;
35024         
35025     },
35026     
35027     /**
35028     * remove a Masonry Brick
35029     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35030     */
35031     removeBrick : function(brick_id)
35032     {
35033         if (!brick_id) {
35034             return;
35035         }
35036         
35037         for (var i = 0; i<this.bricks.length; i++) {
35038             if (this.bricks[i].id == brick_id) {
35039                 this.bricks.splice(i,1);
35040                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35041                 this.initial();
35042             }
35043         }
35044     },
35045     
35046     /**
35047     * adds a Masonry Brick
35048     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35049     */
35050     addBrick : function(cfg)
35051     {
35052         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35053         //this.register(cn);
35054         cn.parentId = this.id;
35055         cn.render(this.el);
35056         return cn;
35057     },
35058     
35059     /**
35060     * register a Masonry Brick
35061     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35062     */
35063     
35064     register : function(brick)
35065     {
35066         this.bricks.push(brick);
35067         brick.masonryId = this.id;
35068     },
35069     
35070     /**
35071     * clear all the Masonry Brick
35072     */
35073     clearAll : function()
35074     {
35075         this.bricks = [];
35076         //this.getChildContainer().dom.innerHTML = "";
35077         this.el.dom.innerHTML = '';
35078     },
35079     
35080     getSelected : function()
35081     {
35082         if (!this.selectedBrick) {
35083             return false;
35084         }
35085         
35086         return this.selectedBrick;
35087     }
35088 });
35089
35090 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35091     
35092     groups: {},
35093      /**
35094     * register a Masonry Layout
35095     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35096     */
35097     
35098     register : function(layout)
35099     {
35100         this.groups[layout.id] = layout;
35101     },
35102     /**
35103     * fetch a  Masonry Layout based on the masonry layout ID
35104     * @param {string} the masonry layout to add
35105     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35106     */
35107     
35108     get: function(layout_id) {
35109         if (typeof(this.groups[layout_id]) == 'undefined') {
35110             return false;
35111         }
35112         return this.groups[layout_id] ;
35113     }
35114     
35115     
35116     
35117 });
35118
35119  
35120
35121  /**
35122  *
35123  * This is based on 
35124  * http://masonry.desandro.com
35125  *
35126  * The idea is to render all the bricks based on vertical width...
35127  *
35128  * The original code extends 'outlayer' - we might need to use that....
35129  * 
35130  */
35131
35132
35133 /**
35134  * @class Roo.bootstrap.LayoutMasonryAuto
35135  * @extends Roo.bootstrap.Component
35136  * Bootstrap Layout Masonry class
35137  * 
35138  * @constructor
35139  * Create a new Element
35140  * @param {Object} config The config object
35141  */
35142
35143 Roo.bootstrap.LayoutMasonryAuto = function(config){
35144     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35145 };
35146
35147 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35148     
35149       /**
35150      * @cfg {Boolean} isFitWidth  - resize the width..
35151      */   
35152     isFitWidth : false,  // options..
35153     /**
35154      * @cfg {Boolean} isOriginLeft = left align?
35155      */   
35156     isOriginLeft : true,
35157     /**
35158      * @cfg {Boolean} isOriginTop = top align?
35159      */   
35160     isOriginTop : false,
35161     /**
35162      * @cfg {Boolean} isLayoutInstant = no animation?
35163      */   
35164     isLayoutInstant : false, // needed?
35165     /**
35166      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35167      */   
35168     isResizingContainer : true,
35169     /**
35170      * @cfg {Number} columnWidth  width of the columns 
35171      */   
35172     
35173     columnWidth : 0,
35174     
35175     /**
35176      * @cfg {Number} maxCols maximum number of columns
35177      */   
35178     
35179     maxCols: 0,
35180     /**
35181      * @cfg {Number} padHeight padding below box..
35182      */   
35183     
35184     padHeight : 10, 
35185     
35186     /**
35187      * @cfg {Boolean} isAutoInitial defalut true
35188      */   
35189     
35190     isAutoInitial : true, 
35191     
35192     // private?
35193     gutter : 0,
35194     
35195     containerWidth: 0,
35196     initialColumnWidth : 0,
35197     currentSize : null,
35198     
35199     colYs : null, // array.
35200     maxY : 0,
35201     padWidth: 10,
35202     
35203     
35204     tag: 'div',
35205     cls: '',
35206     bricks: null, //CompositeElement
35207     cols : 0, // array?
35208     // element : null, // wrapped now this.el
35209     _isLayoutInited : null, 
35210     
35211     
35212     getAutoCreate : function(){
35213         
35214         var cfg = {
35215             tag: this.tag,
35216             cls: 'blog-masonary-wrapper ' + this.cls,
35217             cn : {
35218                 cls : 'mas-boxes masonary'
35219             }
35220         };
35221         
35222         return cfg;
35223     },
35224     
35225     getChildContainer: function( )
35226     {
35227         if (this.boxesEl) {
35228             return this.boxesEl;
35229         }
35230         
35231         this.boxesEl = this.el.select('.mas-boxes').first();
35232         
35233         return this.boxesEl;
35234     },
35235     
35236     
35237     initEvents : function()
35238     {
35239         var _this = this;
35240         
35241         if(this.isAutoInitial){
35242             Roo.log('hook children rendered');
35243             this.on('childrenrendered', function() {
35244                 Roo.log('children rendered');
35245                 _this.initial();
35246             } ,this);
35247         }
35248         
35249     },
35250     
35251     initial : function()
35252     {
35253         this.reloadItems();
35254
35255         this.currentSize = this.el.getBox(true);
35256
35257         /// was window resize... - let's see if this works..
35258         Roo.EventManager.onWindowResize(this.resize, this); 
35259
35260         if(!this.isAutoInitial){
35261             this.layout();
35262             return;
35263         }
35264         
35265         this.layout.defer(500,this);
35266     },
35267     
35268     reloadItems: function()
35269     {
35270         this.bricks = this.el.select('.masonry-brick', true);
35271         
35272         this.bricks.each(function(b) {
35273             //Roo.log(b.getSize());
35274             if (!b.attr('originalwidth')) {
35275                 b.attr('originalwidth',  b.getSize().width);
35276             }
35277             
35278         });
35279         
35280         Roo.log(this.bricks.elements.length);
35281     },
35282     
35283     resize : function()
35284     {
35285         Roo.log('resize');
35286         var cs = this.el.getBox(true);
35287         
35288         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35289             Roo.log("no change in with or X");
35290             return;
35291         }
35292         this.currentSize = cs;
35293         this.layout();
35294     },
35295     
35296     layout : function()
35297     {
35298          Roo.log('layout');
35299         this._resetLayout();
35300         //this._manageStamps();
35301       
35302         // don't animate first layout
35303         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35304         this.layoutItems( isInstant );
35305       
35306         // flag for initalized
35307         this._isLayoutInited = true;
35308     },
35309     
35310     layoutItems : function( isInstant )
35311     {
35312         //var items = this._getItemsForLayout( this.items );
35313         // original code supports filtering layout items.. we just ignore it..
35314         
35315         this._layoutItems( this.bricks , isInstant );
35316       
35317         this._postLayout();
35318     },
35319     _layoutItems : function ( items , isInstant)
35320     {
35321        //this.fireEvent( 'layout', this, items );
35322     
35323
35324         if ( !items || !items.elements.length ) {
35325           // no items, emit event with empty array
35326             return;
35327         }
35328
35329         var queue = [];
35330         items.each(function(item) {
35331             Roo.log("layout item");
35332             Roo.log(item);
35333             // get x/y object from method
35334             var position = this._getItemLayoutPosition( item );
35335             // enqueue
35336             position.item = item;
35337             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35338             queue.push( position );
35339         }, this);
35340       
35341         this._processLayoutQueue( queue );
35342     },
35343     /** Sets position of item in DOM
35344     * @param {Element} item
35345     * @param {Number} x - horizontal position
35346     * @param {Number} y - vertical position
35347     * @param {Boolean} isInstant - disables transitions
35348     */
35349     _processLayoutQueue : function( queue )
35350     {
35351         for ( var i=0, len = queue.length; i < len; i++ ) {
35352             var obj = queue[i];
35353             obj.item.position('absolute');
35354             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35355         }
35356     },
35357       
35358     
35359     /**
35360     * Any logic you want to do after each layout,
35361     * i.e. size the container
35362     */
35363     _postLayout : function()
35364     {
35365         this.resizeContainer();
35366     },
35367     
35368     resizeContainer : function()
35369     {
35370         if ( !this.isResizingContainer ) {
35371             return;
35372         }
35373         var size = this._getContainerSize();
35374         if ( size ) {
35375             this.el.setSize(size.width,size.height);
35376             this.boxesEl.setSize(size.width,size.height);
35377         }
35378     },
35379     
35380     
35381     
35382     _resetLayout : function()
35383     {
35384         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35385         this.colWidth = this.el.getWidth();
35386         //this.gutter = this.el.getWidth(); 
35387         
35388         this.measureColumns();
35389
35390         // reset column Y
35391         var i = this.cols;
35392         this.colYs = [];
35393         while (i--) {
35394             this.colYs.push( 0 );
35395         }
35396     
35397         this.maxY = 0;
35398     },
35399
35400     measureColumns : function()
35401     {
35402         this.getContainerWidth();
35403       // if columnWidth is 0, default to outerWidth of first item
35404         if ( !this.columnWidth ) {
35405             var firstItem = this.bricks.first();
35406             Roo.log(firstItem);
35407             this.columnWidth  = this.containerWidth;
35408             if (firstItem && firstItem.attr('originalwidth') ) {
35409                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35410             }
35411             // columnWidth fall back to item of first element
35412             Roo.log("set column width?");
35413                         this.initialColumnWidth = this.columnWidth  ;
35414
35415             // if first elem has no width, default to size of container
35416             
35417         }
35418         
35419         
35420         if (this.initialColumnWidth) {
35421             this.columnWidth = this.initialColumnWidth;
35422         }
35423         
35424         
35425             
35426         // column width is fixed at the top - however if container width get's smaller we should
35427         // reduce it...
35428         
35429         // this bit calcs how man columns..
35430             
35431         var columnWidth = this.columnWidth += this.gutter;
35432       
35433         // calculate columns
35434         var containerWidth = this.containerWidth + this.gutter;
35435         
35436         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35437         // fix rounding errors, typically with gutters
35438         var excess = columnWidth - containerWidth % columnWidth;
35439         
35440         
35441         // if overshoot is less than a pixel, round up, otherwise floor it
35442         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35443         cols = Math[ mathMethod ]( cols );
35444         this.cols = Math.max( cols, 1 );
35445         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35446         
35447          // padding positioning..
35448         var totalColWidth = this.cols * this.columnWidth;
35449         var padavail = this.containerWidth - totalColWidth;
35450         // so for 2 columns - we need 3 'pads'
35451         
35452         var padNeeded = (1+this.cols) * this.padWidth;
35453         
35454         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35455         
35456         this.columnWidth += padExtra
35457         //this.padWidth = Math.floor(padavail /  ( this.cols));
35458         
35459         // adjust colum width so that padding is fixed??
35460         
35461         // we have 3 columns ... total = width * 3
35462         // we have X left over... that should be used by 
35463         
35464         //if (this.expandC) {
35465             
35466         //}
35467         
35468         
35469         
35470     },
35471     
35472     getContainerWidth : function()
35473     {
35474        /* // container is parent if fit width
35475         var container = this.isFitWidth ? this.element.parentNode : this.element;
35476         // check that this.size and size are there
35477         // IE8 triggers resize on body size change, so they might not be
35478         
35479         var size = getSize( container );  //FIXME
35480         this.containerWidth = size && size.innerWidth; //FIXME
35481         */
35482          
35483         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35484         
35485     },
35486     
35487     _getItemLayoutPosition : function( item )  // what is item?
35488     {
35489         // we resize the item to our columnWidth..
35490       
35491         item.setWidth(this.columnWidth);
35492         item.autoBoxAdjust  = false;
35493         
35494         var sz = item.getSize();
35495  
35496         // how many columns does this brick span
35497         var remainder = this.containerWidth % this.columnWidth;
35498         
35499         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35500         // round if off by 1 pixel, otherwise use ceil
35501         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35502         colSpan = Math.min( colSpan, this.cols );
35503         
35504         // normally this should be '1' as we dont' currently allow multi width columns..
35505         
35506         var colGroup = this._getColGroup( colSpan );
35507         // get the minimum Y value from the columns
35508         var minimumY = Math.min.apply( Math, colGroup );
35509         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35510         
35511         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35512          
35513         // position the brick
35514         var position = {
35515             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35516             y: this.currentSize.y + minimumY + this.padHeight
35517         };
35518         
35519         Roo.log(position);
35520         // apply setHeight to necessary columns
35521         var setHeight = minimumY + sz.height + this.padHeight;
35522         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35523         
35524         var setSpan = this.cols + 1 - colGroup.length;
35525         for ( var i = 0; i < setSpan; i++ ) {
35526           this.colYs[ shortColIndex + i ] = setHeight ;
35527         }
35528       
35529         return position;
35530     },
35531     
35532     /**
35533      * @param {Number} colSpan - number of columns the element spans
35534      * @returns {Array} colGroup
35535      */
35536     _getColGroup : function( colSpan )
35537     {
35538         if ( colSpan < 2 ) {
35539           // if brick spans only one column, use all the column Ys
35540           return this.colYs;
35541         }
35542       
35543         var colGroup = [];
35544         // how many different places could this brick fit horizontally
35545         var groupCount = this.cols + 1 - colSpan;
35546         // for each group potential horizontal position
35547         for ( var i = 0; i < groupCount; i++ ) {
35548           // make an array of colY values for that one group
35549           var groupColYs = this.colYs.slice( i, i + colSpan );
35550           // and get the max value of the array
35551           colGroup[i] = Math.max.apply( Math, groupColYs );
35552         }
35553         return colGroup;
35554     },
35555     /*
35556     _manageStamp : function( stamp )
35557     {
35558         var stampSize =  stamp.getSize();
35559         var offset = stamp.getBox();
35560         // get the columns that this stamp affects
35561         var firstX = this.isOriginLeft ? offset.x : offset.right;
35562         var lastX = firstX + stampSize.width;
35563         var firstCol = Math.floor( firstX / this.columnWidth );
35564         firstCol = Math.max( 0, firstCol );
35565         
35566         var lastCol = Math.floor( lastX / this.columnWidth );
35567         // lastCol should not go over if multiple of columnWidth #425
35568         lastCol -= lastX % this.columnWidth ? 0 : 1;
35569         lastCol = Math.min( this.cols - 1, lastCol );
35570         
35571         // set colYs to bottom of the stamp
35572         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35573             stampSize.height;
35574             
35575         for ( var i = firstCol; i <= lastCol; i++ ) {
35576           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35577         }
35578     },
35579     */
35580     
35581     _getContainerSize : function()
35582     {
35583         this.maxY = Math.max.apply( Math, this.colYs );
35584         var size = {
35585             height: this.maxY
35586         };
35587       
35588         if ( this.isFitWidth ) {
35589             size.width = this._getContainerFitWidth();
35590         }
35591       
35592         return size;
35593     },
35594     
35595     _getContainerFitWidth : function()
35596     {
35597         var unusedCols = 0;
35598         // count unused columns
35599         var i = this.cols;
35600         while ( --i ) {
35601           if ( this.colYs[i] !== 0 ) {
35602             break;
35603           }
35604           unusedCols++;
35605         }
35606         // fit container to columns that have been used
35607         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35608     },
35609     
35610     needsResizeLayout : function()
35611     {
35612         var previousWidth = this.containerWidth;
35613         this.getContainerWidth();
35614         return previousWidth !== this.containerWidth;
35615     }
35616  
35617 });
35618
35619  
35620
35621  /*
35622  * - LGPL
35623  *
35624  * element
35625  * 
35626  */
35627
35628 /**
35629  * @class Roo.bootstrap.MasonryBrick
35630  * @extends Roo.bootstrap.Component
35631  * Bootstrap MasonryBrick class
35632  * 
35633  * @constructor
35634  * Create a new MasonryBrick
35635  * @param {Object} config The config object
35636  */
35637
35638 Roo.bootstrap.MasonryBrick = function(config){
35639     
35640     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35641     
35642     Roo.bootstrap.MasonryBrick.register(this);
35643     
35644     this.addEvents({
35645         // raw events
35646         /**
35647          * @event click
35648          * When a MasonryBrick is clcik
35649          * @param {Roo.bootstrap.MasonryBrick} this
35650          * @param {Roo.EventObject} e
35651          */
35652         "click" : true
35653     });
35654 };
35655
35656 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35657     
35658     /**
35659      * @cfg {String} title
35660      */   
35661     title : '',
35662     /**
35663      * @cfg {String} html
35664      */   
35665     html : '',
35666     /**
35667      * @cfg {String} bgimage
35668      */   
35669     bgimage : '',
35670     /**
35671      * @cfg {String} videourl
35672      */   
35673     videourl : '',
35674     /**
35675      * @cfg {String} cls
35676      */   
35677     cls : '',
35678     /**
35679      * @cfg {String} href
35680      */   
35681     href : '',
35682     /**
35683      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35684      */   
35685     size : 'xs',
35686     
35687     /**
35688      * @cfg {String} placetitle (center|bottom)
35689      */   
35690     placetitle : '',
35691     
35692     /**
35693      * @cfg {Boolean} isFitContainer defalut true
35694      */   
35695     isFitContainer : true, 
35696     
35697     /**
35698      * @cfg {Boolean} preventDefault defalut false
35699      */   
35700     preventDefault : false, 
35701     
35702     /**
35703      * @cfg {Boolean} inverse defalut false
35704      */   
35705     maskInverse : false, 
35706     
35707     getAutoCreate : function()
35708     {
35709         if(!this.isFitContainer){
35710             return this.getSplitAutoCreate();
35711         }
35712         
35713         var cls = 'masonry-brick masonry-brick-full';
35714         
35715         if(this.href.length){
35716             cls += ' masonry-brick-link';
35717         }
35718         
35719         if(this.bgimage.length){
35720             cls += ' masonry-brick-image';
35721         }
35722         
35723         if(this.maskInverse){
35724             cls += ' mask-inverse';
35725         }
35726         
35727         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35728             cls += ' enable-mask';
35729         }
35730         
35731         if(this.size){
35732             cls += ' masonry-' + this.size + '-brick';
35733         }
35734         
35735         if(this.placetitle.length){
35736             
35737             switch (this.placetitle) {
35738                 case 'center' :
35739                     cls += ' masonry-center-title';
35740                     break;
35741                 case 'bottom' :
35742                     cls += ' masonry-bottom-title';
35743                     break;
35744                 default:
35745                     break;
35746             }
35747             
35748         } else {
35749             if(!this.html.length && !this.bgimage.length){
35750                 cls += ' masonry-center-title';
35751             }
35752
35753             if(!this.html.length && this.bgimage.length){
35754                 cls += ' masonry-bottom-title';
35755             }
35756         }
35757         
35758         if(this.cls){
35759             cls += ' ' + this.cls;
35760         }
35761         
35762         var cfg = {
35763             tag: (this.href.length) ? 'a' : 'div',
35764             cls: cls,
35765             cn: [
35766                 {
35767                     tag: 'div',
35768                     cls: 'masonry-brick-mask'
35769                 },
35770                 {
35771                     tag: 'div',
35772                     cls: 'masonry-brick-paragraph',
35773                     cn: []
35774                 }
35775             ]
35776         };
35777         
35778         if(this.href.length){
35779             cfg.href = this.href;
35780         }
35781         
35782         var cn = cfg.cn[1].cn;
35783         
35784         if(this.title.length){
35785             cn.push({
35786                 tag: 'h4',
35787                 cls: 'masonry-brick-title',
35788                 html: this.title
35789             });
35790         }
35791         
35792         if(this.html.length){
35793             cn.push({
35794                 tag: 'p',
35795                 cls: 'masonry-brick-text',
35796                 html: this.html
35797             });
35798         }
35799         
35800         if (!this.title.length && !this.html.length) {
35801             cfg.cn[1].cls += ' hide';
35802         }
35803         
35804         if(this.bgimage.length){
35805             cfg.cn.push({
35806                 tag: 'img',
35807                 cls: 'masonry-brick-image-view',
35808                 src: this.bgimage
35809             });
35810         }
35811         
35812         if(this.videourl.length){
35813             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35814             // youtube support only?
35815             cfg.cn.push({
35816                 tag: 'iframe',
35817                 cls: 'masonry-brick-image-view',
35818                 src: vurl,
35819                 frameborder : 0,
35820                 allowfullscreen : true
35821             });
35822         }
35823         
35824         return cfg;
35825         
35826     },
35827     
35828     getSplitAutoCreate : function()
35829     {
35830         var cls = 'masonry-brick masonry-brick-split';
35831         
35832         if(this.href.length){
35833             cls += ' masonry-brick-link';
35834         }
35835         
35836         if(this.bgimage.length){
35837             cls += ' masonry-brick-image';
35838         }
35839         
35840         if(this.size){
35841             cls += ' masonry-' + this.size + '-brick';
35842         }
35843         
35844         switch (this.placetitle) {
35845             case 'center' :
35846                 cls += ' masonry-center-title';
35847                 break;
35848             case 'bottom' :
35849                 cls += ' masonry-bottom-title';
35850                 break;
35851             default:
35852                 if(!this.bgimage.length){
35853                     cls += ' masonry-center-title';
35854                 }
35855
35856                 if(this.bgimage.length){
35857                     cls += ' masonry-bottom-title';
35858                 }
35859                 break;
35860         }
35861         
35862         if(this.cls){
35863             cls += ' ' + this.cls;
35864         }
35865         
35866         var cfg = {
35867             tag: (this.href.length) ? 'a' : 'div',
35868             cls: cls,
35869             cn: [
35870                 {
35871                     tag: 'div',
35872                     cls: 'masonry-brick-split-head',
35873                     cn: [
35874                         {
35875                             tag: 'div',
35876                             cls: 'masonry-brick-paragraph',
35877                             cn: []
35878                         }
35879                     ]
35880                 },
35881                 {
35882                     tag: 'div',
35883                     cls: 'masonry-brick-split-body',
35884                     cn: []
35885                 }
35886             ]
35887         };
35888         
35889         if(this.href.length){
35890             cfg.href = this.href;
35891         }
35892         
35893         if(this.title.length){
35894             cfg.cn[0].cn[0].cn.push({
35895                 tag: 'h4',
35896                 cls: 'masonry-brick-title',
35897                 html: this.title
35898             });
35899         }
35900         
35901         if(this.html.length){
35902             cfg.cn[1].cn.push({
35903                 tag: 'p',
35904                 cls: 'masonry-brick-text',
35905                 html: this.html
35906             });
35907         }
35908
35909         if(this.bgimage.length){
35910             cfg.cn[0].cn.push({
35911                 tag: 'img',
35912                 cls: 'masonry-brick-image-view',
35913                 src: this.bgimage
35914             });
35915         }
35916         
35917         if(this.videourl.length){
35918             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35919             // youtube support only?
35920             cfg.cn[0].cn.cn.push({
35921                 tag: 'iframe',
35922                 cls: 'masonry-brick-image-view',
35923                 src: vurl,
35924                 frameborder : 0,
35925                 allowfullscreen : true
35926             });
35927         }
35928         
35929         return cfg;
35930     },
35931     
35932     initEvents: function() 
35933     {
35934         switch (this.size) {
35935             case 'xs' :
35936                 this.x = 1;
35937                 this.y = 1;
35938                 break;
35939             case 'sm' :
35940                 this.x = 2;
35941                 this.y = 2;
35942                 break;
35943             case 'md' :
35944             case 'md-left' :
35945             case 'md-right' :
35946                 this.x = 3;
35947                 this.y = 3;
35948                 break;
35949             case 'tall' :
35950                 this.x = 2;
35951                 this.y = 3;
35952                 break;
35953             case 'wide' :
35954                 this.x = 3;
35955                 this.y = 2;
35956                 break;
35957             case 'wide-thin' :
35958                 this.x = 3;
35959                 this.y = 1;
35960                 break;
35961                         
35962             default :
35963                 break;
35964         }
35965         
35966         if(Roo.isTouch){
35967             this.el.on('touchstart', this.onTouchStart, this);
35968             this.el.on('touchmove', this.onTouchMove, this);
35969             this.el.on('touchend', this.onTouchEnd, this);
35970             this.el.on('contextmenu', this.onContextMenu, this);
35971         } else {
35972             this.el.on('mouseenter'  ,this.enter, this);
35973             this.el.on('mouseleave', this.leave, this);
35974             this.el.on('click', this.onClick, this);
35975         }
35976         
35977         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35978             this.parent().bricks.push(this);   
35979         }
35980         
35981     },
35982     
35983     onClick: function(e, el)
35984     {
35985         var time = this.endTimer - this.startTimer;
35986         // Roo.log(e.preventDefault());
35987         if(Roo.isTouch){
35988             if(time > 1000){
35989                 e.preventDefault();
35990                 return;
35991             }
35992         }
35993         
35994         if(!this.preventDefault){
35995             return;
35996         }
35997         
35998         e.preventDefault();
35999         
36000         if (this.activeClass != '') {
36001             this.selectBrick();
36002         }
36003         
36004         this.fireEvent('click', this, e);
36005     },
36006     
36007     enter: function(e, el)
36008     {
36009         e.preventDefault();
36010         
36011         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36012             return;
36013         }
36014         
36015         if(this.bgimage.length && this.html.length){
36016             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36017         }
36018     },
36019     
36020     leave: function(e, el)
36021     {
36022         e.preventDefault();
36023         
36024         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36025             return;
36026         }
36027         
36028         if(this.bgimage.length && this.html.length){
36029             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36030         }
36031     },
36032     
36033     onTouchStart: function(e, el)
36034     {
36035 //        e.preventDefault();
36036         
36037         this.touchmoved = false;
36038         
36039         if(!this.isFitContainer){
36040             return;
36041         }
36042         
36043         if(!this.bgimage.length || !this.html.length){
36044             return;
36045         }
36046         
36047         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36048         
36049         this.timer = new Date().getTime();
36050         
36051     },
36052     
36053     onTouchMove: function(e, el)
36054     {
36055         this.touchmoved = true;
36056     },
36057     
36058     onContextMenu : function(e,el)
36059     {
36060         e.preventDefault();
36061         e.stopPropagation();
36062         return false;
36063     },
36064     
36065     onTouchEnd: function(e, el)
36066     {
36067 //        e.preventDefault();
36068         
36069         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36070         
36071             this.leave(e,el);
36072             
36073             return;
36074         }
36075         
36076         if(!this.bgimage.length || !this.html.length){
36077             
36078             if(this.href.length){
36079                 window.location.href = this.href;
36080             }
36081             
36082             return;
36083         }
36084         
36085         if(!this.isFitContainer){
36086             return;
36087         }
36088         
36089         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36090         
36091         window.location.href = this.href;
36092     },
36093     
36094     //selection on single brick only
36095     selectBrick : function() {
36096         
36097         if (!this.parentId) {
36098             return;
36099         }
36100         
36101         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36102         var index = m.selectedBrick.indexOf(this.id);
36103         
36104         if ( index > -1) {
36105             m.selectedBrick.splice(index,1);
36106             this.el.removeClass(this.activeClass);
36107             return;
36108         }
36109         
36110         for(var i = 0; i < m.selectedBrick.length; i++) {
36111             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36112             b.el.removeClass(b.activeClass);
36113         }
36114         
36115         m.selectedBrick = [];
36116         
36117         m.selectedBrick.push(this.id);
36118         this.el.addClass(this.activeClass);
36119         return;
36120     },
36121     
36122     isSelected : function(){
36123         return this.el.hasClass(this.activeClass);
36124         
36125     }
36126 });
36127
36128 Roo.apply(Roo.bootstrap.MasonryBrick, {
36129     
36130     //groups: {},
36131     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36132      /**
36133     * register a Masonry Brick
36134     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36135     */
36136     
36137     register : function(brick)
36138     {
36139         //this.groups[brick.id] = brick;
36140         this.groups.add(brick.id, brick);
36141     },
36142     /**
36143     * fetch a  masonry brick based on the masonry brick ID
36144     * @param {string} the masonry brick to add
36145     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36146     */
36147     
36148     get: function(brick_id) 
36149     {
36150         // if (typeof(this.groups[brick_id]) == 'undefined') {
36151         //     return false;
36152         // }
36153         // return this.groups[brick_id] ;
36154         
36155         if(this.groups.key(brick_id)) {
36156             return this.groups.key(brick_id);
36157         }
36158         
36159         return false;
36160     }
36161     
36162     
36163     
36164 });
36165
36166  /*
36167  * - LGPL
36168  *
36169  * element
36170  * 
36171  */
36172
36173 /**
36174  * @class Roo.bootstrap.Brick
36175  * @extends Roo.bootstrap.Component
36176  * Bootstrap Brick class
36177  * 
36178  * @constructor
36179  * Create a new Brick
36180  * @param {Object} config The config object
36181  */
36182
36183 Roo.bootstrap.Brick = function(config){
36184     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36185     
36186     this.addEvents({
36187         // raw events
36188         /**
36189          * @event click
36190          * When a Brick is click
36191          * @param {Roo.bootstrap.Brick} this
36192          * @param {Roo.EventObject} e
36193          */
36194         "click" : true
36195     });
36196 };
36197
36198 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36199     
36200     /**
36201      * @cfg {String} title
36202      */   
36203     title : '',
36204     /**
36205      * @cfg {String} html
36206      */   
36207     html : '',
36208     /**
36209      * @cfg {String} bgimage
36210      */   
36211     bgimage : '',
36212     /**
36213      * @cfg {String} cls
36214      */   
36215     cls : '',
36216     /**
36217      * @cfg {String} href
36218      */   
36219     href : '',
36220     /**
36221      * @cfg {String} video
36222      */   
36223     video : '',
36224     /**
36225      * @cfg {Boolean} square
36226      */   
36227     square : true,
36228     
36229     getAutoCreate : function()
36230     {
36231         var cls = 'roo-brick';
36232         
36233         if(this.href.length){
36234             cls += ' roo-brick-link';
36235         }
36236         
36237         if(this.bgimage.length){
36238             cls += ' roo-brick-image';
36239         }
36240         
36241         if(!this.html.length && !this.bgimage.length){
36242             cls += ' roo-brick-center-title';
36243         }
36244         
36245         if(!this.html.length && this.bgimage.length){
36246             cls += ' roo-brick-bottom-title';
36247         }
36248         
36249         if(this.cls){
36250             cls += ' ' + this.cls;
36251         }
36252         
36253         var cfg = {
36254             tag: (this.href.length) ? 'a' : 'div',
36255             cls: cls,
36256             cn: [
36257                 {
36258                     tag: 'div',
36259                     cls: 'roo-brick-paragraph',
36260                     cn: []
36261                 }
36262             ]
36263         };
36264         
36265         if(this.href.length){
36266             cfg.href = this.href;
36267         }
36268         
36269         var cn = cfg.cn[0].cn;
36270         
36271         if(this.title.length){
36272             cn.push({
36273                 tag: 'h4',
36274                 cls: 'roo-brick-title',
36275                 html: this.title
36276             });
36277         }
36278         
36279         if(this.html.length){
36280             cn.push({
36281                 tag: 'p',
36282                 cls: 'roo-brick-text',
36283                 html: this.html
36284             });
36285         } else {
36286             cn.cls += ' hide';
36287         }
36288         
36289         if(this.bgimage.length){
36290             cfg.cn.push({
36291                 tag: 'img',
36292                 cls: 'roo-brick-image-view',
36293                 src: this.bgimage
36294             });
36295         }
36296         
36297         return cfg;
36298     },
36299     
36300     initEvents: function() 
36301     {
36302         if(this.title.length || this.html.length){
36303             this.el.on('mouseenter'  ,this.enter, this);
36304             this.el.on('mouseleave', this.leave, this);
36305         }
36306         
36307         Roo.EventManager.onWindowResize(this.resize, this); 
36308         
36309         if(this.bgimage.length){
36310             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36311             this.imageEl.on('load', this.onImageLoad, this);
36312             return;
36313         }
36314         
36315         this.resize();
36316     },
36317     
36318     onImageLoad : function()
36319     {
36320         this.resize();
36321     },
36322     
36323     resize : function()
36324     {
36325         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36326         
36327         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36328         
36329         if(this.bgimage.length){
36330             var image = this.el.select('.roo-brick-image-view', true).first();
36331             
36332             image.setWidth(paragraph.getWidth());
36333             
36334             if(this.square){
36335                 image.setHeight(paragraph.getWidth());
36336             }
36337             
36338             this.el.setHeight(image.getHeight());
36339             paragraph.setHeight(image.getHeight());
36340             
36341         }
36342         
36343     },
36344     
36345     enter: function(e, el)
36346     {
36347         e.preventDefault();
36348         
36349         if(this.bgimage.length){
36350             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36351             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36352         }
36353     },
36354     
36355     leave: function(e, el)
36356     {
36357         e.preventDefault();
36358         
36359         if(this.bgimage.length){
36360             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36361             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36362         }
36363     }
36364     
36365 });
36366
36367  
36368
36369  /*
36370  * - LGPL
36371  *
36372  * Number field 
36373  */
36374
36375 /**
36376  * @class Roo.bootstrap.NumberField
36377  * @extends Roo.bootstrap.Input
36378  * Bootstrap NumberField class
36379  * 
36380  * 
36381  * 
36382  * 
36383  * @constructor
36384  * Create a new NumberField
36385  * @param {Object} config The config object
36386  */
36387
36388 Roo.bootstrap.NumberField = function(config){
36389     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36390 };
36391
36392 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36393     
36394     /**
36395      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36396      */
36397     allowDecimals : true,
36398     /**
36399      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36400      */
36401     decimalSeparator : ".",
36402     /**
36403      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36404      */
36405     decimalPrecision : 2,
36406     /**
36407      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36408      */
36409     allowNegative : true,
36410     
36411     /**
36412      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36413      */
36414     allowZero: true,
36415     /**
36416      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36417      */
36418     minValue : Number.NEGATIVE_INFINITY,
36419     /**
36420      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36421      */
36422     maxValue : Number.MAX_VALUE,
36423     /**
36424      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36425      */
36426     minText : "The minimum value for this field is {0}",
36427     /**
36428      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36429      */
36430     maxText : "The maximum value for this field is {0}",
36431     /**
36432      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36433      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36434      */
36435     nanText : "{0} is not a valid number",
36436     /**
36437      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36438      */
36439     thousandsDelimiter : false,
36440     /**
36441      * @cfg {String} valueAlign alignment of value
36442      */
36443     valueAlign : "left",
36444
36445     getAutoCreate : function()
36446     {
36447         var hiddenInput = {
36448             tag: 'input',
36449             type: 'hidden',
36450             id: Roo.id(),
36451             cls: 'hidden-number-input'
36452         };
36453         
36454         if (this.name) {
36455             hiddenInput.name = this.name;
36456         }
36457         
36458         this.name = '';
36459         
36460         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36461         
36462         this.name = hiddenInput.name;
36463         
36464         if(cfg.cn.length > 0) {
36465             cfg.cn.push(hiddenInput);
36466         }
36467         
36468         return cfg;
36469     },
36470
36471     // private
36472     initEvents : function()
36473     {   
36474         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36475         
36476         var allowed = "0123456789";
36477         
36478         if(this.allowDecimals){
36479             allowed += this.decimalSeparator;
36480         }
36481         
36482         if(this.allowNegative){
36483             allowed += "-";
36484         }
36485         
36486         if(this.thousandsDelimiter) {
36487             allowed += ",";
36488         }
36489         
36490         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36491         
36492         var keyPress = function(e){
36493             
36494             var k = e.getKey();
36495             
36496             var c = e.getCharCode();
36497             
36498             if(
36499                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36500                     allowed.indexOf(String.fromCharCode(c)) === -1
36501             ){
36502                 e.stopEvent();
36503                 return;
36504             }
36505             
36506             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36507                 return;
36508             }
36509             
36510             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36511                 e.stopEvent();
36512             }
36513         };
36514         
36515         this.el.on("keypress", keyPress, this);
36516     },
36517     
36518     validateValue : function(value)
36519     {
36520         
36521         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36522             return false;
36523         }
36524         
36525         var num = this.parseValue(value);
36526         
36527         if(isNaN(num)){
36528             this.markInvalid(String.format(this.nanText, value));
36529             return false;
36530         }
36531         
36532         if(num < this.minValue){
36533             this.markInvalid(String.format(this.minText, this.minValue));
36534             return false;
36535         }
36536         
36537         if(num > this.maxValue){
36538             this.markInvalid(String.format(this.maxText, this.maxValue));
36539             return false;
36540         }
36541         
36542         return true;
36543     },
36544
36545     getValue : function()
36546     {
36547         var v = this.hiddenEl().getValue();
36548         
36549         return this.fixPrecision(this.parseValue(v));
36550     },
36551
36552     parseValue : function(value)
36553     {
36554         if(this.thousandsDelimiter) {
36555             value += "";
36556             r = new RegExp(",", "g");
36557             value = value.replace(r, "");
36558         }
36559         
36560         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36561         return isNaN(value) ? '' : value;
36562     },
36563
36564     fixPrecision : function(value)
36565     {
36566         if(this.thousandsDelimiter) {
36567             value += "";
36568             r = new RegExp(",", "g");
36569             value = value.replace(r, "");
36570         }
36571         
36572         var nan = isNaN(value);
36573         
36574         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36575             return nan ? '' : value;
36576         }
36577         return parseFloat(value).toFixed(this.decimalPrecision);
36578     },
36579
36580     setValue : function(v)
36581     {
36582         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36583         
36584         this.value = v;
36585         
36586         if(this.rendered){
36587             
36588             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36589             
36590             this.inputEl().dom.value = (v == '') ? '' :
36591                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36592             
36593             if(!this.allowZero && v === '0') {
36594                 this.hiddenEl().dom.value = '';
36595                 this.inputEl().dom.value = '';
36596             }
36597             
36598             this.validate();
36599         }
36600     },
36601
36602     decimalPrecisionFcn : function(v)
36603     {
36604         return Math.floor(v);
36605     },
36606
36607     beforeBlur : function()
36608     {
36609         var v = this.parseValue(this.getRawValue());
36610         
36611         if(v || v === 0 || v === ''){
36612             this.setValue(v);
36613         }
36614     },
36615     
36616     hiddenEl : function()
36617     {
36618         return this.el.select('input.hidden-number-input',true).first();
36619     }
36620     
36621 });
36622
36623  
36624
36625 /*
36626 * Licence: LGPL
36627 */
36628
36629 /**
36630  * @class Roo.bootstrap.DocumentSlider
36631  * @extends Roo.bootstrap.Component
36632  * Bootstrap DocumentSlider class
36633  * 
36634  * @constructor
36635  * Create a new DocumentViewer
36636  * @param {Object} config The config object
36637  */
36638
36639 Roo.bootstrap.DocumentSlider = function(config){
36640     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36641     
36642     this.files = [];
36643     
36644     this.addEvents({
36645         /**
36646          * @event initial
36647          * Fire after initEvent
36648          * @param {Roo.bootstrap.DocumentSlider} this
36649          */
36650         "initial" : true,
36651         /**
36652          * @event update
36653          * Fire after update
36654          * @param {Roo.bootstrap.DocumentSlider} this
36655          */
36656         "update" : true,
36657         /**
36658          * @event click
36659          * Fire after click
36660          * @param {Roo.bootstrap.DocumentSlider} this
36661          */
36662         "click" : true
36663     });
36664 };
36665
36666 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36667     
36668     files : false,
36669     
36670     indicator : 0,
36671     
36672     getAutoCreate : function()
36673     {
36674         var cfg = {
36675             tag : 'div',
36676             cls : 'roo-document-slider',
36677             cn : [
36678                 {
36679                     tag : 'div',
36680                     cls : 'roo-document-slider-header',
36681                     cn : [
36682                         {
36683                             tag : 'div',
36684                             cls : 'roo-document-slider-header-title'
36685                         }
36686                     ]
36687                 },
36688                 {
36689                     tag : 'div',
36690                     cls : 'roo-document-slider-body',
36691                     cn : [
36692                         {
36693                             tag : 'div',
36694                             cls : 'roo-document-slider-prev',
36695                             cn : [
36696                                 {
36697                                     tag : 'i',
36698                                     cls : 'fa fa-chevron-left'
36699                                 }
36700                             ]
36701                         },
36702                         {
36703                             tag : 'div',
36704                             cls : 'roo-document-slider-thumb',
36705                             cn : [
36706                                 {
36707                                     tag : 'img',
36708                                     cls : 'roo-document-slider-image'
36709                                 }
36710                             ]
36711                         },
36712                         {
36713                             tag : 'div',
36714                             cls : 'roo-document-slider-next',
36715                             cn : [
36716                                 {
36717                                     tag : 'i',
36718                                     cls : 'fa fa-chevron-right'
36719                                 }
36720                             ]
36721                         }
36722                     ]
36723                 }
36724             ]
36725         };
36726         
36727         return cfg;
36728     },
36729     
36730     initEvents : function()
36731     {
36732         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36733         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36734         
36735         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36736         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36737         
36738         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36739         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36740         
36741         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36742         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36743         
36744         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36745         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36746         
36747         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36748         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36749         
36750         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36751         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36752         
36753         this.thumbEl.on('click', this.onClick, this);
36754         
36755         this.prevIndicator.on('click', this.prev, this);
36756         
36757         this.nextIndicator.on('click', this.next, this);
36758         
36759     },
36760     
36761     initial : function()
36762     {
36763         if(this.files.length){
36764             this.indicator = 1;
36765             this.update()
36766         }
36767         
36768         this.fireEvent('initial', this);
36769     },
36770     
36771     update : function()
36772     {
36773         this.imageEl.attr('src', this.files[this.indicator - 1]);
36774         
36775         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36776         
36777         this.prevIndicator.show();
36778         
36779         if(this.indicator == 1){
36780             this.prevIndicator.hide();
36781         }
36782         
36783         this.nextIndicator.show();
36784         
36785         if(this.indicator == this.files.length){
36786             this.nextIndicator.hide();
36787         }
36788         
36789         this.thumbEl.scrollTo('top');
36790         
36791         this.fireEvent('update', this);
36792     },
36793     
36794     onClick : function(e)
36795     {
36796         e.preventDefault();
36797         
36798         this.fireEvent('click', this);
36799     },
36800     
36801     prev : function(e)
36802     {
36803         e.preventDefault();
36804         
36805         this.indicator = Math.max(1, this.indicator - 1);
36806         
36807         this.update();
36808     },
36809     
36810     next : function(e)
36811     {
36812         e.preventDefault();
36813         
36814         this.indicator = Math.min(this.files.length, this.indicator + 1);
36815         
36816         this.update();
36817     }
36818 });
36819 /*
36820  * - LGPL
36821  *
36822  * RadioSet
36823  *
36824  *
36825  */
36826
36827 /**
36828  * @class Roo.bootstrap.RadioSet
36829  * @extends Roo.bootstrap.Input
36830  * Bootstrap RadioSet class
36831  * @cfg {String} indicatorpos (left|right) default left
36832  * @cfg {Boolean} inline (true|false) inline the element (default true)
36833  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36834  * @constructor
36835  * Create a new RadioSet
36836  * @param {Object} config The config object
36837  */
36838
36839 Roo.bootstrap.RadioSet = function(config){
36840     
36841     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36842     
36843     this.radioes = [];
36844     
36845     Roo.bootstrap.RadioSet.register(this);
36846     
36847     this.addEvents({
36848         /**
36849         * @event check
36850         * Fires when the element is checked or unchecked.
36851         * @param {Roo.bootstrap.RadioSet} this This radio
36852         * @param {Roo.bootstrap.Radio} item The checked item
36853         */
36854        check : true,
36855        /**
36856         * @event click
36857         * Fires when the element is click.
36858         * @param {Roo.bootstrap.RadioSet} this This radio set
36859         * @param {Roo.bootstrap.Radio} item The checked item
36860         * @param {Roo.EventObject} e The event object
36861         */
36862        click : true
36863     });
36864     
36865 };
36866
36867 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36868
36869     radioes : false,
36870     
36871     inline : true,
36872     
36873     weight : '',
36874     
36875     indicatorpos : 'left',
36876     
36877     getAutoCreate : function()
36878     {
36879         var label = {
36880             tag : 'label',
36881             cls : 'roo-radio-set-label',
36882             cn : [
36883                 {
36884                     tag : 'span',
36885                     html : this.fieldLabel
36886                 }
36887             ]
36888         };
36889         if (Roo.bootstrap.version == 3) {
36890             
36891             
36892             if(this.indicatorpos == 'left'){
36893                 label.cn.unshift({
36894                     tag : 'i',
36895                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36896                     tooltip : 'This field is required'
36897                 });
36898             } else {
36899                 label.cn.push({
36900                     tag : 'i',
36901                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36902                     tooltip : 'This field is required'
36903                 });
36904             }
36905         }
36906         var items = {
36907             tag : 'div',
36908             cls : 'roo-radio-set-items'
36909         };
36910         
36911         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36912         
36913         if (align === 'left' && this.fieldLabel.length) {
36914             
36915             items = {
36916                 cls : "roo-radio-set-right", 
36917                 cn: [
36918                     items
36919                 ]
36920             };
36921             
36922             if(this.labelWidth > 12){
36923                 label.style = "width: " + this.labelWidth + 'px';
36924             }
36925             
36926             if(this.labelWidth < 13 && this.labelmd == 0){
36927                 this.labelmd = this.labelWidth;
36928             }
36929             
36930             if(this.labellg > 0){
36931                 label.cls += ' col-lg-' + this.labellg;
36932                 items.cls += ' col-lg-' + (12 - this.labellg);
36933             }
36934             
36935             if(this.labelmd > 0){
36936                 label.cls += ' col-md-' + this.labelmd;
36937                 items.cls += ' col-md-' + (12 - this.labelmd);
36938             }
36939             
36940             if(this.labelsm > 0){
36941                 label.cls += ' col-sm-' + this.labelsm;
36942                 items.cls += ' col-sm-' + (12 - this.labelsm);
36943             }
36944             
36945             if(this.labelxs > 0){
36946                 label.cls += ' col-xs-' + this.labelxs;
36947                 items.cls += ' col-xs-' + (12 - this.labelxs);
36948             }
36949         }
36950         
36951         var cfg = {
36952             tag : 'div',
36953             cls : 'roo-radio-set',
36954             cn : [
36955                 {
36956                     tag : 'input',
36957                     cls : 'roo-radio-set-input',
36958                     type : 'hidden',
36959                     name : this.name,
36960                     value : this.value ? this.value :  ''
36961                 },
36962                 label,
36963                 items
36964             ]
36965         };
36966         
36967         if(this.weight.length){
36968             cfg.cls += ' roo-radio-' + this.weight;
36969         }
36970         
36971         if(this.inline) {
36972             cfg.cls += ' roo-radio-set-inline';
36973         }
36974         
36975         var settings=this;
36976         ['xs','sm','md','lg'].map(function(size){
36977             if (settings[size]) {
36978                 cfg.cls += ' col-' + size + '-' + settings[size];
36979             }
36980         });
36981         
36982         return cfg;
36983         
36984     },
36985
36986     initEvents : function()
36987     {
36988         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36989         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36990         
36991         if(!this.fieldLabel.length){
36992             this.labelEl.hide();
36993         }
36994         
36995         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36996         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36997         
36998         this.indicator = this.indicatorEl();
36999         
37000         if(this.indicator){
37001             this.indicator.addClass('invisible');
37002         }
37003         
37004         this.originalValue = this.getValue();
37005         
37006     },
37007     
37008     inputEl: function ()
37009     {
37010         return this.el.select('.roo-radio-set-input', true).first();
37011     },
37012     
37013     getChildContainer : function()
37014     {
37015         return this.itemsEl;
37016     },
37017     
37018     register : function(item)
37019     {
37020         this.radioes.push(item);
37021         
37022     },
37023     
37024     validate : function()
37025     {   
37026         if(this.getVisibilityEl().hasClass('hidden')){
37027             return true;
37028         }
37029         
37030         var valid = false;
37031         
37032         Roo.each(this.radioes, function(i){
37033             if(!i.checked){
37034                 return;
37035             }
37036             
37037             valid = true;
37038             return false;
37039         });
37040         
37041         if(this.allowBlank) {
37042             return true;
37043         }
37044         
37045         if(this.disabled || valid){
37046             this.markValid();
37047             return true;
37048         }
37049         
37050         this.markInvalid();
37051         return false;
37052         
37053     },
37054     
37055     markValid : function()
37056     {
37057         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37058             this.indicatorEl().removeClass('visible');
37059             this.indicatorEl().addClass('invisible');
37060         }
37061         
37062         
37063         if (Roo.bootstrap.version == 3) {
37064             this.el.removeClass([this.invalidClass, this.validClass]);
37065             this.el.addClass(this.validClass);
37066         } else {
37067             this.el.removeClass(['is-invalid','is-valid']);
37068             this.el.addClass(['is-valid']);
37069         }
37070         this.fireEvent('valid', this);
37071     },
37072     
37073     markInvalid : function(msg)
37074     {
37075         if(this.allowBlank || this.disabled){
37076             return;
37077         }
37078         
37079         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37080             this.indicatorEl().removeClass('invisible');
37081             this.indicatorEl().addClass('visible');
37082         }
37083         if (Roo.bootstrap.version == 3) {
37084             this.el.removeClass([this.invalidClass, this.validClass]);
37085             this.el.addClass(this.invalidClass);
37086         } else {
37087             this.el.removeClass(['is-invalid','is-valid']);
37088             this.el.addClass(['is-invalid']);
37089         }
37090         
37091         this.fireEvent('invalid', this, msg);
37092         
37093     },
37094     
37095     setValue : function(v, suppressEvent)
37096     {   
37097         if(this.value === v){
37098             return;
37099         }
37100         
37101         this.value = v;
37102         
37103         if(this.rendered){
37104             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37105         }
37106         
37107         Roo.each(this.radioes, function(i){
37108             i.checked = false;
37109             i.el.removeClass('checked');
37110         });
37111         
37112         Roo.each(this.radioes, function(i){
37113             
37114             if(i.value === v || i.value.toString() === v.toString()){
37115                 i.checked = true;
37116                 i.el.addClass('checked');
37117                 
37118                 if(suppressEvent !== true){
37119                     this.fireEvent('check', this, i);
37120                 }
37121                 
37122                 return false;
37123             }
37124             
37125         }, this);
37126         
37127         this.validate();
37128     },
37129     
37130     clearInvalid : function(){
37131         
37132         if(!this.el || this.preventMark){
37133             return;
37134         }
37135         
37136         this.el.removeClass([this.invalidClass]);
37137         
37138         this.fireEvent('valid', this);
37139     }
37140     
37141 });
37142
37143 Roo.apply(Roo.bootstrap.RadioSet, {
37144     
37145     groups: {},
37146     
37147     register : function(set)
37148     {
37149         this.groups[set.name] = set;
37150     },
37151     
37152     get: function(name) 
37153     {
37154         if (typeof(this.groups[name]) == 'undefined') {
37155             return false;
37156         }
37157         
37158         return this.groups[name] ;
37159     }
37160     
37161 });
37162 /*
37163  * Based on:
37164  * Ext JS Library 1.1.1
37165  * Copyright(c) 2006-2007, Ext JS, LLC.
37166  *
37167  * Originally Released Under LGPL - original licence link has changed is not relivant.
37168  *
37169  * Fork - LGPL
37170  * <script type="text/javascript">
37171  */
37172
37173
37174 /**
37175  * @class Roo.bootstrap.SplitBar
37176  * @extends Roo.util.Observable
37177  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37178  * <br><br>
37179  * Usage:
37180  * <pre><code>
37181 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37182                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37183 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37184 split.minSize = 100;
37185 split.maxSize = 600;
37186 split.animate = true;
37187 split.on('moved', splitterMoved);
37188 </code></pre>
37189  * @constructor
37190  * Create a new SplitBar
37191  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37192  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37193  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37194  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37195                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37196                         position of the SplitBar).
37197  */
37198 Roo.bootstrap.SplitBar = function(cfg){
37199     
37200     /** @private */
37201     
37202     //{
37203     //  dragElement : elm
37204     //  resizingElement: el,
37205         // optional..
37206     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37207     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37208         // existingProxy ???
37209     //}
37210     
37211     this.el = Roo.get(cfg.dragElement, true);
37212     this.el.dom.unselectable = "on";
37213     /** @private */
37214     this.resizingEl = Roo.get(cfg.resizingElement, true);
37215
37216     /**
37217      * @private
37218      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37219      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37220      * @type Number
37221      */
37222     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37223     
37224     /**
37225      * The minimum size of the resizing element. (Defaults to 0)
37226      * @type Number
37227      */
37228     this.minSize = 0;
37229     
37230     /**
37231      * The maximum size of the resizing element. (Defaults to 2000)
37232      * @type Number
37233      */
37234     this.maxSize = 2000;
37235     
37236     /**
37237      * Whether to animate the transition to the new size
37238      * @type Boolean
37239      */
37240     this.animate = false;
37241     
37242     /**
37243      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37244      * @type Boolean
37245      */
37246     this.useShim = false;
37247     
37248     /** @private */
37249     this.shim = null;
37250     
37251     if(!cfg.existingProxy){
37252         /** @private */
37253         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37254     }else{
37255         this.proxy = Roo.get(cfg.existingProxy).dom;
37256     }
37257     /** @private */
37258     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37259     
37260     /** @private */
37261     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37262     
37263     /** @private */
37264     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37265     
37266     /** @private */
37267     this.dragSpecs = {};
37268     
37269     /**
37270      * @private The adapter to use to positon and resize elements
37271      */
37272     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37273     this.adapter.init(this);
37274     
37275     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37276         /** @private */
37277         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37278         this.el.addClass("roo-splitbar-h");
37279     }else{
37280         /** @private */
37281         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37282         this.el.addClass("roo-splitbar-v");
37283     }
37284     
37285     this.addEvents({
37286         /**
37287          * @event resize
37288          * Fires when the splitter is moved (alias for {@link #event-moved})
37289          * @param {Roo.bootstrap.SplitBar} this
37290          * @param {Number} newSize the new width or height
37291          */
37292         "resize" : true,
37293         /**
37294          * @event moved
37295          * Fires when the splitter is moved
37296          * @param {Roo.bootstrap.SplitBar} this
37297          * @param {Number} newSize the new width or height
37298          */
37299         "moved" : true,
37300         /**
37301          * @event beforeresize
37302          * Fires before the splitter is dragged
37303          * @param {Roo.bootstrap.SplitBar} this
37304          */
37305         "beforeresize" : true,
37306
37307         "beforeapply" : true
37308     });
37309
37310     Roo.util.Observable.call(this);
37311 };
37312
37313 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37314     onStartProxyDrag : function(x, y){
37315         this.fireEvent("beforeresize", this);
37316         if(!this.overlay){
37317             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37318             o.unselectable();
37319             o.enableDisplayMode("block");
37320             // all splitbars share the same overlay
37321             Roo.bootstrap.SplitBar.prototype.overlay = o;
37322         }
37323         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37324         this.overlay.show();
37325         Roo.get(this.proxy).setDisplayed("block");
37326         var size = this.adapter.getElementSize(this);
37327         this.activeMinSize = this.getMinimumSize();;
37328         this.activeMaxSize = this.getMaximumSize();;
37329         var c1 = size - this.activeMinSize;
37330         var c2 = Math.max(this.activeMaxSize - size, 0);
37331         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37332             this.dd.resetConstraints();
37333             this.dd.setXConstraint(
37334                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37335                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37336             );
37337             this.dd.setYConstraint(0, 0);
37338         }else{
37339             this.dd.resetConstraints();
37340             this.dd.setXConstraint(0, 0);
37341             this.dd.setYConstraint(
37342                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37343                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37344             );
37345          }
37346         this.dragSpecs.startSize = size;
37347         this.dragSpecs.startPoint = [x, y];
37348         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37349     },
37350     
37351     /** 
37352      * @private Called after the drag operation by the DDProxy
37353      */
37354     onEndProxyDrag : function(e){
37355         Roo.get(this.proxy).setDisplayed(false);
37356         var endPoint = Roo.lib.Event.getXY(e);
37357         if(this.overlay){
37358             this.overlay.hide();
37359         }
37360         var newSize;
37361         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37362             newSize = this.dragSpecs.startSize + 
37363                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37364                     endPoint[0] - this.dragSpecs.startPoint[0] :
37365                     this.dragSpecs.startPoint[0] - endPoint[0]
37366                 );
37367         }else{
37368             newSize = this.dragSpecs.startSize + 
37369                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37370                     endPoint[1] - this.dragSpecs.startPoint[1] :
37371                     this.dragSpecs.startPoint[1] - endPoint[1]
37372                 );
37373         }
37374         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37375         if(newSize != this.dragSpecs.startSize){
37376             if(this.fireEvent('beforeapply', this, newSize) !== false){
37377                 this.adapter.setElementSize(this, newSize);
37378                 this.fireEvent("moved", this, newSize);
37379                 this.fireEvent("resize", this, newSize);
37380             }
37381         }
37382     },
37383     
37384     /**
37385      * Get the adapter this SplitBar uses
37386      * @return The adapter object
37387      */
37388     getAdapter : function(){
37389         return this.adapter;
37390     },
37391     
37392     /**
37393      * Set the adapter this SplitBar uses
37394      * @param {Object} adapter A SplitBar adapter object
37395      */
37396     setAdapter : function(adapter){
37397         this.adapter = adapter;
37398         this.adapter.init(this);
37399     },
37400     
37401     /**
37402      * Gets the minimum size for the resizing element
37403      * @return {Number} The minimum size
37404      */
37405     getMinimumSize : function(){
37406         return this.minSize;
37407     },
37408     
37409     /**
37410      * Sets the minimum size for the resizing element
37411      * @param {Number} minSize The minimum size
37412      */
37413     setMinimumSize : function(minSize){
37414         this.minSize = minSize;
37415     },
37416     
37417     /**
37418      * Gets the maximum size for the resizing element
37419      * @return {Number} The maximum size
37420      */
37421     getMaximumSize : function(){
37422         return this.maxSize;
37423     },
37424     
37425     /**
37426      * Sets the maximum size for the resizing element
37427      * @param {Number} maxSize The maximum size
37428      */
37429     setMaximumSize : function(maxSize){
37430         this.maxSize = maxSize;
37431     },
37432     
37433     /**
37434      * Sets the initialize size for the resizing element
37435      * @param {Number} size The initial size
37436      */
37437     setCurrentSize : function(size){
37438         var oldAnimate = this.animate;
37439         this.animate = false;
37440         this.adapter.setElementSize(this, size);
37441         this.animate = oldAnimate;
37442     },
37443     
37444     /**
37445      * Destroy this splitbar. 
37446      * @param {Boolean} removeEl True to remove the element
37447      */
37448     destroy : function(removeEl){
37449         if(this.shim){
37450             this.shim.remove();
37451         }
37452         this.dd.unreg();
37453         this.proxy.parentNode.removeChild(this.proxy);
37454         if(removeEl){
37455             this.el.remove();
37456         }
37457     }
37458 });
37459
37460 /**
37461  * @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.
37462  */
37463 Roo.bootstrap.SplitBar.createProxy = function(dir){
37464     var proxy = new Roo.Element(document.createElement("div"));
37465     proxy.unselectable();
37466     var cls = 'roo-splitbar-proxy';
37467     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37468     document.body.appendChild(proxy.dom);
37469     return proxy.dom;
37470 };
37471
37472 /** 
37473  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37474  * Default Adapter. It assumes the splitter and resizing element are not positioned
37475  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37476  */
37477 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37478 };
37479
37480 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37481     // do nothing for now
37482     init : function(s){
37483     
37484     },
37485     /**
37486      * Called before drag operations to get the current size of the resizing element. 
37487      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37488      */
37489      getElementSize : function(s){
37490         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37491             return s.resizingEl.getWidth();
37492         }else{
37493             return s.resizingEl.getHeight();
37494         }
37495     },
37496     
37497     /**
37498      * Called after drag operations to set the size of the resizing element.
37499      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37500      * @param {Number} newSize The new size to set
37501      * @param {Function} onComplete A function to be invoked when resizing is complete
37502      */
37503     setElementSize : function(s, newSize, onComplete){
37504         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37505             if(!s.animate){
37506                 s.resizingEl.setWidth(newSize);
37507                 if(onComplete){
37508                     onComplete(s, newSize);
37509                 }
37510             }else{
37511                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37512             }
37513         }else{
37514             
37515             if(!s.animate){
37516                 s.resizingEl.setHeight(newSize);
37517                 if(onComplete){
37518                     onComplete(s, newSize);
37519                 }
37520             }else{
37521                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37522             }
37523         }
37524     }
37525 };
37526
37527 /** 
37528  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37529  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37530  * Adapter that  moves the splitter element to align with the resized sizing element. 
37531  * Used with an absolute positioned SplitBar.
37532  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37533  * document.body, make sure you assign an id to the body element.
37534  */
37535 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37536     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37537     this.container = Roo.get(container);
37538 };
37539
37540 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37541     init : function(s){
37542         this.basic.init(s);
37543     },
37544     
37545     getElementSize : function(s){
37546         return this.basic.getElementSize(s);
37547     },
37548     
37549     setElementSize : function(s, newSize, onComplete){
37550         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37551     },
37552     
37553     moveSplitter : function(s){
37554         var yes = Roo.bootstrap.SplitBar;
37555         switch(s.placement){
37556             case yes.LEFT:
37557                 s.el.setX(s.resizingEl.getRight());
37558                 break;
37559             case yes.RIGHT:
37560                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37561                 break;
37562             case yes.TOP:
37563                 s.el.setY(s.resizingEl.getBottom());
37564                 break;
37565             case yes.BOTTOM:
37566                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37567                 break;
37568         }
37569     }
37570 };
37571
37572 /**
37573  * Orientation constant - Create a vertical SplitBar
37574  * @static
37575  * @type Number
37576  */
37577 Roo.bootstrap.SplitBar.VERTICAL = 1;
37578
37579 /**
37580  * Orientation constant - Create a horizontal SplitBar
37581  * @static
37582  * @type Number
37583  */
37584 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37585
37586 /**
37587  * Placement constant - The resizing element is to the left of the splitter element
37588  * @static
37589  * @type Number
37590  */
37591 Roo.bootstrap.SplitBar.LEFT = 1;
37592
37593 /**
37594  * Placement constant - The resizing element is to the right of the splitter element
37595  * @static
37596  * @type Number
37597  */
37598 Roo.bootstrap.SplitBar.RIGHT = 2;
37599
37600 /**
37601  * Placement constant - The resizing element is positioned above the splitter element
37602  * @static
37603  * @type Number
37604  */
37605 Roo.bootstrap.SplitBar.TOP = 3;
37606
37607 /**
37608  * Placement constant - The resizing element is positioned under splitter element
37609  * @static
37610  * @type Number
37611  */
37612 Roo.bootstrap.SplitBar.BOTTOM = 4;
37613 Roo.namespace("Roo.bootstrap.layout");/*
37614  * Based on:
37615  * Ext JS Library 1.1.1
37616  * Copyright(c) 2006-2007, Ext JS, LLC.
37617  *
37618  * Originally Released Under LGPL - original licence link has changed is not relivant.
37619  *
37620  * Fork - LGPL
37621  * <script type="text/javascript">
37622  */
37623
37624 /**
37625  * @class Roo.bootstrap.layout.Manager
37626  * @extends Roo.bootstrap.Component
37627  * Base class for layout managers.
37628  */
37629 Roo.bootstrap.layout.Manager = function(config)
37630 {
37631     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37632
37633
37634
37635
37636
37637     /** false to disable window resize monitoring @type Boolean */
37638     this.monitorWindowResize = true;
37639     this.regions = {};
37640     this.addEvents({
37641         /**
37642          * @event layout
37643          * Fires when a layout is performed.
37644          * @param {Roo.LayoutManager} this
37645          */
37646         "layout" : true,
37647         /**
37648          * @event regionresized
37649          * Fires when the user resizes a region.
37650          * @param {Roo.LayoutRegion} region The resized region
37651          * @param {Number} newSize The new size (width for east/west, height for north/south)
37652          */
37653         "regionresized" : true,
37654         /**
37655          * @event regioncollapsed
37656          * Fires when a region is collapsed.
37657          * @param {Roo.LayoutRegion} region The collapsed region
37658          */
37659         "regioncollapsed" : true,
37660         /**
37661          * @event regionexpanded
37662          * Fires when a region is expanded.
37663          * @param {Roo.LayoutRegion} region The expanded region
37664          */
37665         "regionexpanded" : true
37666     });
37667     this.updating = false;
37668
37669     if (config.el) {
37670         this.el = Roo.get(config.el);
37671         this.initEvents();
37672     }
37673
37674 };
37675
37676 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37677
37678
37679     regions : null,
37680
37681     monitorWindowResize : true,
37682
37683
37684     updating : false,
37685
37686
37687     onRender : function(ct, position)
37688     {
37689         if(!this.el){
37690             this.el = Roo.get(ct);
37691             this.initEvents();
37692         }
37693         //this.fireEvent('render',this);
37694     },
37695
37696
37697     initEvents: function()
37698     {
37699
37700
37701         // ie scrollbar fix
37702         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37703             document.body.scroll = "no";
37704         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37705             this.el.position('relative');
37706         }
37707         this.id = this.el.id;
37708         this.el.addClass("roo-layout-container");
37709         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37710         if(this.el.dom != document.body ) {
37711             this.el.on('resize', this.layout,this);
37712             this.el.on('show', this.layout,this);
37713         }
37714
37715     },
37716
37717     /**
37718      * Returns true if this layout is currently being updated
37719      * @return {Boolean}
37720      */
37721     isUpdating : function(){
37722         return this.updating;
37723     },
37724
37725     /**
37726      * Suspend the LayoutManager from doing auto-layouts while
37727      * making multiple add or remove calls
37728      */
37729     beginUpdate : function(){
37730         this.updating = true;
37731     },
37732
37733     /**
37734      * Restore auto-layouts and optionally disable the manager from performing a layout
37735      * @param {Boolean} noLayout true to disable a layout update
37736      */
37737     endUpdate : function(noLayout){
37738         this.updating = false;
37739         if(!noLayout){
37740             this.layout();
37741         }
37742     },
37743
37744     layout: function(){
37745         // abstract...
37746     },
37747
37748     onRegionResized : function(region, newSize){
37749         this.fireEvent("regionresized", region, newSize);
37750         this.layout();
37751     },
37752
37753     onRegionCollapsed : function(region){
37754         this.fireEvent("regioncollapsed", region);
37755     },
37756
37757     onRegionExpanded : function(region){
37758         this.fireEvent("regionexpanded", region);
37759     },
37760
37761     /**
37762      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37763      * performs box-model adjustments.
37764      * @return {Object} The size as an object {width: (the width), height: (the height)}
37765      */
37766     getViewSize : function()
37767     {
37768         var size;
37769         if(this.el.dom != document.body){
37770             size = this.el.getSize();
37771         }else{
37772             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37773         }
37774         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37775         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37776         return size;
37777     },
37778
37779     /**
37780      * Returns the Element this layout is bound to.
37781      * @return {Roo.Element}
37782      */
37783     getEl : function(){
37784         return this.el;
37785     },
37786
37787     /**
37788      * Returns the specified region.
37789      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37790      * @return {Roo.LayoutRegion}
37791      */
37792     getRegion : function(target){
37793         return this.regions[target.toLowerCase()];
37794     },
37795
37796     onWindowResize : function(){
37797         if(this.monitorWindowResize){
37798             this.layout();
37799         }
37800     }
37801 });
37802 /*
37803  * Based on:
37804  * Ext JS Library 1.1.1
37805  * Copyright(c) 2006-2007, Ext JS, LLC.
37806  *
37807  * Originally Released Under LGPL - original licence link has changed is not relivant.
37808  *
37809  * Fork - LGPL
37810  * <script type="text/javascript">
37811  */
37812 /**
37813  * @class Roo.bootstrap.layout.Border
37814  * @extends Roo.bootstrap.layout.Manager
37815  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37816  * please see: examples/bootstrap/nested.html<br><br>
37817  
37818 <b>The container the layout is rendered into can be either the body element or any other element.
37819 If it is not the body element, the container needs to either be an absolute positioned element,
37820 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37821 the container size if it is not the body element.</b>
37822
37823 * @constructor
37824 * Create a new Border
37825 * @param {Object} config Configuration options
37826  */
37827 Roo.bootstrap.layout.Border = function(config){
37828     config = config || {};
37829     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37830     
37831     
37832     
37833     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37834         if(config[region]){
37835             config[region].region = region;
37836             this.addRegion(config[region]);
37837         }
37838     },this);
37839     
37840 };
37841
37842 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37843
37844 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37845     
37846     parent : false, // this might point to a 'nest' or a ???
37847     
37848     /**
37849      * Creates and adds a new region if it doesn't already exist.
37850      * @param {String} target The target region key (north, south, east, west or center).
37851      * @param {Object} config The regions config object
37852      * @return {BorderLayoutRegion} The new region
37853      */
37854     addRegion : function(config)
37855     {
37856         if(!this.regions[config.region]){
37857             var r = this.factory(config);
37858             this.bindRegion(r);
37859         }
37860         return this.regions[config.region];
37861     },
37862
37863     // private (kinda)
37864     bindRegion : function(r){
37865         this.regions[r.config.region] = r;
37866         
37867         r.on("visibilitychange",    this.layout, this);
37868         r.on("paneladded",          this.layout, this);
37869         r.on("panelremoved",        this.layout, this);
37870         r.on("invalidated",         this.layout, this);
37871         r.on("resized",             this.onRegionResized, this);
37872         r.on("collapsed",           this.onRegionCollapsed, this);
37873         r.on("expanded",            this.onRegionExpanded, this);
37874     },
37875
37876     /**
37877      * Performs a layout update.
37878      */
37879     layout : function()
37880     {
37881         if(this.updating) {
37882             return;
37883         }
37884         
37885         // render all the rebions if they have not been done alreayd?
37886         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37887             if(this.regions[region] && !this.regions[region].bodyEl){
37888                 this.regions[region].onRender(this.el)
37889             }
37890         },this);
37891         
37892         var size = this.getViewSize();
37893         var w = size.width;
37894         var h = size.height;
37895         var centerW = w;
37896         var centerH = h;
37897         var centerY = 0;
37898         var centerX = 0;
37899         //var x = 0, y = 0;
37900
37901         var rs = this.regions;
37902         var north = rs["north"];
37903         var south = rs["south"]; 
37904         var west = rs["west"];
37905         var east = rs["east"];
37906         var center = rs["center"];
37907         //if(this.hideOnLayout){ // not supported anymore
37908             //c.el.setStyle("display", "none");
37909         //}
37910         if(north && north.isVisible()){
37911             var b = north.getBox();
37912             var m = north.getMargins();
37913             b.width = w - (m.left+m.right);
37914             b.x = m.left;
37915             b.y = m.top;
37916             centerY = b.height + b.y + m.bottom;
37917             centerH -= centerY;
37918             north.updateBox(this.safeBox(b));
37919         }
37920         if(south && south.isVisible()){
37921             var b = south.getBox();
37922             var m = south.getMargins();
37923             b.width = w - (m.left+m.right);
37924             b.x = m.left;
37925             var totalHeight = (b.height + m.top + m.bottom);
37926             b.y = h - totalHeight + m.top;
37927             centerH -= totalHeight;
37928             south.updateBox(this.safeBox(b));
37929         }
37930         if(west && west.isVisible()){
37931             var b = west.getBox();
37932             var m = west.getMargins();
37933             b.height = centerH - (m.top+m.bottom);
37934             b.x = m.left;
37935             b.y = centerY + m.top;
37936             var totalWidth = (b.width + m.left + m.right);
37937             centerX += totalWidth;
37938             centerW -= totalWidth;
37939             west.updateBox(this.safeBox(b));
37940         }
37941         if(east && east.isVisible()){
37942             var b = east.getBox();
37943             var m = east.getMargins();
37944             b.height = centerH - (m.top+m.bottom);
37945             var totalWidth = (b.width + m.left + m.right);
37946             b.x = w - totalWidth + m.left;
37947             b.y = centerY + m.top;
37948             centerW -= totalWidth;
37949             east.updateBox(this.safeBox(b));
37950         }
37951         if(center){
37952             var m = center.getMargins();
37953             var centerBox = {
37954                 x: centerX + m.left,
37955                 y: centerY + m.top,
37956                 width: centerW - (m.left+m.right),
37957                 height: centerH - (m.top+m.bottom)
37958             };
37959             //if(this.hideOnLayout){
37960                 //center.el.setStyle("display", "block");
37961             //}
37962             center.updateBox(this.safeBox(centerBox));
37963         }
37964         this.el.repaint();
37965         this.fireEvent("layout", this);
37966     },
37967
37968     // private
37969     safeBox : function(box){
37970         box.width = Math.max(0, box.width);
37971         box.height = Math.max(0, box.height);
37972         return box;
37973     },
37974
37975     /**
37976      * Adds a ContentPanel (or subclass) to this layout.
37977      * @param {String} target The target region key (north, south, east, west or center).
37978      * @param {Roo.ContentPanel} panel The panel to add
37979      * @return {Roo.ContentPanel} The added panel
37980      */
37981     add : function(target, panel){
37982          
37983         target = target.toLowerCase();
37984         return this.regions[target].add(panel);
37985     },
37986
37987     /**
37988      * Remove a ContentPanel (or subclass) to this layout.
37989      * @param {String} target The target region key (north, south, east, west or center).
37990      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37991      * @return {Roo.ContentPanel} The removed panel
37992      */
37993     remove : function(target, panel){
37994         target = target.toLowerCase();
37995         return this.regions[target].remove(panel);
37996     },
37997
37998     /**
37999      * Searches all regions for a panel with the specified id
38000      * @param {String} panelId
38001      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38002      */
38003     findPanel : function(panelId){
38004         var rs = this.regions;
38005         for(var target in rs){
38006             if(typeof rs[target] != "function"){
38007                 var p = rs[target].getPanel(panelId);
38008                 if(p){
38009                     return p;
38010                 }
38011             }
38012         }
38013         return null;
38014     },
38015
38016     /**
38017      * Searches all regions for a panel with the specified id and activates (shows) it.
38018      * @param {String/ContentPanel} panelId The panels id or the panel itself
38019      * @return {Roo.ContentPanel} The shown panel or null
38020      */
38021     showPanel : function(panelId) {
38022       var rs = this.regions;
38023       for(var target in rs){
38024          var r = rs[target];
38025          if(typeof r != "function"){
38026             if(r.hasPanel(panelId)){
38027                return r.showPanel(panelId);
38028             }
38029          }
38030       }
38031       return null;
38032    },
38033
38034    /**
38035      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38036      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38037      */
38038    /*
38039     restoreState : function(provider){
38040         if(!provider){
38041             provider = Roo.state.Manager;
38042         }
38043         var sm = new Roo.LayoutStateManager();
38044         sm.init(this, provider);
38045     },
38046 */
38047  
38048  
38049     /**
38050      * Adds a xtype elements to the layout.
38051      * <pre><code>
38052
38053 layout.addxtype({
38054        xtype : 'ContentPanel',
38055        region: 'west',
38056        items: [ .... ]
38057    }
38058 );
38059
38060 layout.addxtype({
38061         xtype : 'NestedLayoutPanel',
38062         region: 'west',
38063         layout: {
38064            center: { },
38065            west: { }   
38066         },
38067         items : [ ... list of content panels or nested layout panels.. ]
38068    }
38069 );
38070 </code></pre>
38071      * @param {Object} cfg Xtype definition of item to add.
38072      */
38073     addxtype : function(cfg)
38074     {
38075         // basically accepts a pannel...
38076         // can accept a layout region..!?!?
38077         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38078         
38079         
38080         // theory?  children can only be panels??
38081         
38082         //if (!cfg.xtype.match(/Panel$/)) {
38083         //    return false;
38084         //}
38085         var ret = false;
38086         
38087         if (typeof(cfg.region) == 'undefined') {
38088             Roo.log("Failed to add Panel, region was not set");
38089             Roo.log(cfg);
38090             return false;
38091         }
38092         var region = cfg.region;
38093         delete cfg.region;
38094         
38095           
38096         var xitems = [];
38097         if (cfg.items) {
38098             xitems = cfg.items;
38099             delete cfg.items;
38100         }
38101         var nb = false;
38102         
38103         if ( region == 'center') {
38104             Roo.log("Center: " + cfg.title);
38105         }
38106         
38107         
38108         switch(cfg.xtype) 
38109         {
38110             case 'Content':  // ContentPanel (el, cfg)
38111             case 'Scroll':  // ContentPanel (el, cfg)
38112             case 'View': 
38113                 cfg.autoCreate = cfg.autoCreate || true;
38114                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38115                 //} else {
38116                 //    var el = this.el.createChild();
38117                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38118                 //}
38119                 
38120                 this.add(region, ret);
38121                 break;
38122             
38123             /*
38124             case 'TreePanel': // our new panel!
38125                 cfg.el = this.el.createChild();
38126                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38127                 this.add(region, ret);
38128                 break;
38129             */
38130             
38131             case 'Nest': 
38132                 // create a new Layout (which is  a Border Layout...
38133                 
38134                 var clayout = cfg.layout;
38135                 clayout.el  = this.el.createChild();
38136                 clayout.items   = clayout.items  || [];
38137                 
38138                 delete cfg.layout;
38139                 
38140                 // replace this exitems with the clayout ones..
38141                 xitems = clayout.items;
38142                  
38143                 // force background off if it's in center...
38144                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38145                     cfg.background = false;
38146                 }
38147                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38148                 
38149                 
38150                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38151                 //console.log('adding nested layout panel '  + cfg.toSource());
38152                 this.add(region, ret);
38153                 nb = {}; /// find first...
38154                 break;
38155             
38156             case 'Grid':
38157                 
38158                 // needs grid and region
38159                 
38160                 //var el = this.getRegion(region).el.createChild();
38161                 /*
38162                  *var el = this.el.createChild();
38163                 // create the grid first...
38164                 cfg.grid.container = el;
38165                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38166                 */
38167                 
38168                 if (region == 'center' && this.active ) {
38169                     cfg.background = false;
38170                 }
38171                 
38172                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38173                 
38174                 this.add(region, ret);
38175                 /*
38176                 if (cfg.background) {
38177                     // render grid on panel activation (if panel background)
38178                     ret.on('activate', function(gp) {
38179                         if (!gp.grid.rendered) {
38180                     //        gp.grid.render(el);
38181                         }
38182                     });
38183                 } else {
38184                   //  cfg.grid.render(el);
38185                 }
38186                 */
38187                 break;
38188            
38189            
38190             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38191                 // it was the old xcomponent building that caused this before.
38192                 // espeically if border is the top element in the tree.
38193                 ret = this;
38194                 break; 
38195                 
38196                     
38197                 
38198                 
38199                 
38200             default:
38201                 /*
38202                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38203                     
38204                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38205                     this.add(region, ret);
38206                 } else {
38207                 */
38208                     Roo.log(cfg);
38209                     throw "Can not add '" + cfg.xtype + "' to Border";
38210                     return null;
38211              
38212                                 
38213              
38214         }
38215         this.beginUpdate();
38216         // add children..
38217         var region = '';
38218         var abn = {};
38219         Roo.each(xitems, function(i)  {
38220             region = nb && i.region ? i.region : false;
38221             
38222             var add = ret.addxtype(i);
38223            
38224             if (region) {
38225                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38226                 if (!i.background) {
38227                     abn[region] = nb[region] ;
38228                 }
38229             }
38230             
38231         });
38232         this.endUpdate();
38233
38234         // make the last non-background panel active..
38235         //if (nb) { Roo.log(abn); }
38236         if (nb) {
38237             
38238             for(var r in abn) {
38239                 region = this.getRegion(r);
38240                 if (region) {
38241                     // tried using nb[r], but it does not work..
38242                      
38243                     region.showPanel(abn[r]);
38244                    
38245                 }
38246             }
38247         }
38248         return ret;
38249         
38250     },
38251     
38252     
38253 // private
38254     factory : function(cfg)
38255     {
38256         
38257         var validRegions = Roo.bootstrap.layout.Border.regions;
38258
38259         var target = cfg.region;
38260         cfg.mgr = this;
38261         
38262         var r = Roo.bootstrap.layout;
38263         Roo.log(target);
38264         switch(target){
38265             case "north":
38266                 return new r.North(cfg);
38267             case "south":
38268                 return new r.South(cfg);
38269             case "east":
38270                 return new r.East(cfg);
38271             case "west":
38272                 return new r.West(cfg);
38273             case "center":
38274                 return new r.Center(cfg);
38275         }
38276         throw 'Layout region "'+target+'" not supported.';
38277     }
38278     
38279     
38280 });
38281  /*
38282  * Based on:
38283  * Ext JS Library 1.1.1
38284  * Copyright(c) 2006-2007, Ext JS, LLC.
38285  *
38286  * Originally Released Under LGPL - original licence link has changed is not relivant.
38287  *
38288  * Fork - LGPL
38289  * <script type="text/javascript">
38290  */
38291  
38292 /**
38293  * @class Roo.bootstrap.layout.Basic
38294  * @extends Roo.util.Observable
38295  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38296  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38297  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38298  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38299  * @cfg {string}   region  the region that it inhabits..
38300  * @cfg {bool}   skipConfig skip config?
38301  * 
38302
38303  */
38304 Roo.bootstrap.layout.Basic = function(config){
38305     
38306     this.mgr = config.mgr;
38307     
38308     this.position = config.region;
38309     
38310     var skipConfig = config.skipConfig;
38311     
38312     this.events = {
38313         /**
38314          * @scope Roo.BasicLayoutRegion
38315          */
38316         
38317         /**
38318          * @event beforeremove
38319          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38320          * @param {Roo.LayoutRegion} this
38321          * @param {Roo.ContentPanel} panel The panel
38322          * @param {Object} e The cancel event object
38323          */
38324         "beforeremove" : true,
38325         /**
38326          * @event invalidated
38327          * Fires when the layout for this region is changed.
38328          * @param {Roo.LayoutRegion} this
38329          */
38330         "invalidated" : true,
38331         /**
38332          * @event visibilitychange
38333          * Fires when this region is shown or hidden 
38334          * @param {Roo.LayoutRegion} this
38335          * @param {Boolean} visibility true or false
38336          */
38337         "visibilitychange" : true,
38338         /**
38339          * @event paneladded
38340          * Fires when a panel is added. 
38341          * @param {Roo.LayoutRegion} this
38342          * @param {Roo.ContentPanel} panel The panel
38343          */
38344         "paneladded" : true,
38345         /**
38346          * @event panelremoved
38347          * Fires when a panel is removed. 
38348          * @param {Roo.LayoutRegion} this
38349          * @param {Roo.ContentPanel} panel The panel
38350          */
38351         "panelremoved" : true,
38352         /**
38353          * @event beforecollapse
38354          * Fires when this region before collapse.
38355          * @param {Roo.LayoutRegion} this
38356          */
38357         "beforecollapse" : true,
38358         /**
38359          * @event collapsed
38360          * Fires when this region is collapsed.
38361          * @param {Roo.LayoutRegion} this
38362          */
38363         "collapsed" : true,
38364         /**
38365          * @event expanded
38366          * Fires when this region is expanded.
38367          * @param {Roo.LayoutRegion} this
38368          */
38369         "expanded" : true,
38370         /**
38371          * @event slideshow
38372          * Fires when this region is slid into view.
38373          * @param {Roo.LayoutRegion} this
38374          */
38375         "slideshow" : true,
38376         /**
38377          * @event slidehide
38378          * Fires when this region slides out of view. 
38379          * @param {Roo.LayoutRegion} this
38380          */
38381         "slidehide" : true,
38382         /**
38383          * @event panelactivated
38384          * Fires when a panel is activated. 
38385          * @param {Roo.LayoutRegion} this
38386          * @param {Roo.ContentPanel} panel The activated panel
38387          */
38388         "panelactivated" : true,
38389         /**
38390          * @event resized
38391          * Fires when the user resizes this region. 
38392          * @param {Roo.LayoutRegion} this
38393          * @param {Number} newSize The new size (width for east/west, height for north/south)
38394          */
38395         "resized" : true
38396     };
38397     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38398     this.panels = new Roo.util.MixedCollection();
38399     this.panels.getKey = this.getPanelId.createDelegate(this);
38400     this.box = null;
38401     this.activePanel = null;
38402     // ensure listeners are added...
38403     
38404     if (config.listeners || config.events) {
38405         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38406             listeners : config.listeners || {},
38407             events : config.events || {}
38408         });
38409     }
38410     
38411     if(skipConfig !== true){
38412         this.applyConfig(config);
38413     }
38414 };
38415
38416 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38417 {
38418     getPanelId : function(p){
38419         return p.getId();
38420     },
38421     
38422     applyConfig : function(config){
38423         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38424         this.config = config;
38425         
38426     },
38427     
38428     /**
38429      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38430      * the width, for horizontal (north, south) the height.
38431      * @param {Number} newSize The new width or height
38432      */
38433     resizeTo : function(newSize){
38434         var el = this.el ? this.el :
38435                  (this.activePanel ? this.activePanel.getEl() : null);
38436         if(el){
38437             switch(this.position){
38438                 case "east":
38439                 case "west":
38440                     el.setWidth(newSize);
38441                     this.fireEvent("resized", this, newSize);
38442                 break;
38443                 case "north":
38444                 case "south":
38445                     el.setHeight(newSize);
38446                     this.fireEvent("resized", this, newSize);
38447                 break;                
38448             }
38449         }
38450     },
38451     
38452     getBox : function(){
38453         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38454     },
38455     
38456     getMargins : function(){
38457         return this.margins;
38458     },
38459     
38460     updateBox : function(box){
38461         this.box = box;
38462         var el = this.activePanel.getEl();
38463         el.dom.style.left = box.x + "px";
38464         el.dom.style.top = box.y + "px";
38465         this.activePanel.setSize(box.width, box.height);
38466     },
38467     
38468     /**
38469      * Returns the container element for this region.
38470      * @return {Roo.Element}
38471      */
38472     getEl : function(){
38473         return this.activePanel;
38474     },
38475     
38476     /**
38477      * Returns true if this region is currently visible.
38478      * @return {Boolean}
38479      */
38480     isVisible : function(){
38481         return this.activePanel ? true : false;
38482     },
38483     
38484     setActivePanel : function(panel){
38485         panel = this.getPanel(panel);
38486         if(this.activePanel && this.activePanel != panel){
38487             this.activePanel.setActiveState(false);
38488             this.activePanel.getEl().setLeftTop(-10000,-10000);
38489         }
38490         this.activePanel = panel;
38491         panel.setActiveState(true);
38492         if(this.box){
38493             panel.setSize(this.box.width, this.box.height);
38494         }
38495         this.fireEvent("panelactivated", this, panel);
38496         this.fireEvent("invalidated");
38497     },
38498     
38499     /**
38500      * Show the specified panel.
38501      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38502      * @return {Roo.ContentPanel} The shown panel or null
38503      */
38504     showPanel : function(panel){
38505         panel = this.getPanel(panel);
38506         if(panel){
38507             this.setActivePanel(panel);
38508         }
38509         return panel;
38510     },
38511     
38512     /**
38513      * Get the active panel for this region.
38514      * @return {Roo.ContentPanel} The active panel or null
38515      */
38516     getActivePanel : function(){
38517         return this.activePanel;
38518     },
38519     
38520     /**
38521      * Add the passed ContentPanel(s)
38522      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38523      * @return {Roo.ContentPanel} The panel added (if only one was added)
38524      */
38525     add : function(panel){
38526         if(arguments.length > 1){
38527             for(var i = 0, len = arguments.length; i < len; i++) {
38528                 this.add(arguments[i]);
38529             }
38530             return null;
38531         }
38532         if(this.hasPanel(panel)){
38533             this.showPanel(panel);
38534             return panel;
38535         }
38536         var el = panel.getEl();
38537         if(el.dom.parentNode != this.mgr.el.dom){
38538             this.mgr.el.dom.appendChild(el.dom);
38539         }
38540         if(panel.setRegion){
38541             panel.setRegion(this);
38542         }
38543         this.panels.add(panel);
38544         el.setStyle("position", "absolute");
38545         if(!panel.background){
38546             this.setActivePanel(panel);
38547             if(this.config.initialSize && this.panels.getCount()==1){
38548                 this.resizeTo(this.config.initialSize);
38549             }
38550         }
38551         this.fireEvent("paneladded", this, panel);
38552         return panel;
38553     },
38554     
38555     /**
38556      * Returns true if the panel is in this region.
38557      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38558      * @return {Boolean}
38559      */
38560     hasPanel : function(panel){
38561         if(typeof panel == "object"){ // must be panel obj
38562             panel = panel.getId();
38563         }
38564         return this.getPanel(panel) ? true : false;
38565     },
38566     
38567     /**
38568      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38569      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38570      * @param {Boolean} preservePanel Overrides the config preservePanel option
38571      * @return {Roo.ContentPanel} The panel that was removed
38572      */
38573     remove : function(panel, preservePanel){
38574         panel = this.getPanel(panel);
38575         if(!panel){
38576             return null;
38577         }
38578         var e = {};
38579         this.fireEvent("beforeremove", this, panel, e);
38580         if(e.cancel === true){
38581             return null;
38582         }
38583         var panelId = panel.getId();
38584         this.panels.removeKey(panelId);
38585         return panel;
38586     },
38587     
38588     /**
38589      * Returns the panel specified or null if it's not in this region.
38590      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38591      * @return {Roo.ContentPanel}
38592      */
38593     getPanel : function(id){
38594         if(typeof id == "object"){ // must be panel obj
38595             return id;
38596         }
38597         return this.panels.get(id);
38598     },
38599     
38600     /**
38601      * Returns this regions position (north/south/east/west/center).
38602      * @return {String} 
38603      */
38604     getPosition: function(){
38605         return this.position;    
38606     }
38607 });/*
38608  * Based on:
38609  * Ext JS Library 1.1.1
38610  * Copyright(c) 2006-2007, Ext JS, LLC.
38611  *
38612  * Originally Released Under LGPL - original licence link has changed is not relivant.
38613  *
38614  * Fork - LGPL
38615  * <script type="text/javascript">
38616  */
38617  
38618 /**
38619  * @class Roo.bootstrap.layout.Region
38620  * @extends Roo.bootstrap.layout.Basic
38621  * This class represents a region in a layout manager.
38622  
38623  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38624  * @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})
38625  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38626  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38627  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38628  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38629  * @cfg {String}    title           The title for the region (overrides panel titles)
38630  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38631  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38632  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38633  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38634  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38635  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38636  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38637  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38638  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38639  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38640
38641  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38642  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38643  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38644  * @cfg {Number}    width           For East/West panels
38645  * @cfg {Number}    height          For North/South panels
38646  * @cfg {Boolean}   split           To show the splitter
38647  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38648  * 
38649  * @cfg {string}   cls             Extra CSS classes to add to region
38650  * 
38651  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38652  * @cfg {string}   region  the region that it inhabits..
38653  *
38654
38655  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38656  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38657
38658  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38659  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38660  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38661  */
38662 Roo.bootstrap.layout.Region = function(config)
38663 {
38664     this.applyConfig(config);
38665
38666     var mgr = config.mgr;
38667     var pos = config.region;
38668     config.skipConfig = true;
38669     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38670     
38671     if (mgr.el) {
38672         this.onRender(mgr.el);   
38673     }
38674      
38675     this.visible = true;
38676     this.collapsed = false;
38677     this.unrendered_panels = [];
38678 };
38679
38680 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38681
38682     position: '', // set by wrapper (eg. north/south etc..)
38683     unrendered_panels : null,  // unrendered panels.
38684     
38685     tabPosition : false,
38686     
38687     mgr: false, // points to 'Border'
38688     
38689     
38690     createBody : function(){
38691         /** This region's body element 
38692         * @type Roo.Element */
38693         this.bodyEl = this.el.createChild({
38694                 tag: "div",
38695                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38696         });
38697     },
38698
38699     onRender: function(ctr, pos)
38700     {
38701         var dh = Roo.DomHelper;
38702         /** This region's container element 
38703         * @type Roo.Element */
38704         this.el = dh.append(ctr.dom, {
38705                 tag: "div",
38706                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38707             }, true);
38708         /** This region's title element 
38709         * @type Roo.Element */
38710     
38711         this.titleEl = dh.append(this.el.dom,  {
38712                 tag: "div",
38713                 unselectable: "on",
38714                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38715                 children:[
38716                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38717                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38718                 ]
38719             }, true);
38720         
38721         this.titleEl.enableDisplayMode();
38722         /** This region's title text element 
38723         * @type HTMLElement */
38724         this.titleTextEl = this.titleEl.dom.firstChild;
38725         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38726         /*
38727         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38728         this.closeBtn.enableDisplayMode();
38729         this.closeBtn.on("click", this.closeClicked, this);
38730         this.closeBtn.hide();
38731     */
38732         this.createBody(this.config);
38733         if(this.config.hideWhenEmpty){
38734             this.hide();
38735             this.on("paneladded", this.validateVisibility, this);
38736             this.on("panelremoved", this.validateVisibility, this);
38737         }
38738         if(this.autoScroll){
38739             this.bodyEl.setStyle("overflow", "auto");
38740         }else{
38741             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38742         }
38743         //if(c.titlebar !== false){
38744             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38745                 this.titleEl.hide();
38746             }else{
38747                 this.titleEl.show();
38748                 if(this.config.title){
38749                     this.titleTextEl.innerHTML = this.config.title;
38750                 }
38751             }
38752         //}
38753         if(this.config.collapsed){
38754             this.collapse(true);
38755         }
38756         if(this.config.hidden){
38757             this.hide();
38758         }
38759         
38760         if (this.unrendered_panels && this.unrendered_panels.length) {
38761             for (var i =0;i< this.unrendered_panels.length; i++) {
38762                 this.add(this.unrendered_panels[i]);
38763             }
38764             this.unrendered_panels = null;
38765             
38766         }
38767         
38768     },
38769     
38770     applyConfig : function(c)
38771     {
38772         /*
38773          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38774             var dh = Roo.DomHelper;
38775             if(c.titlebar !== false){
38776                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38777                 this.collapseBtn.on("click", this.collapse, this);
38778                 this.collapseBtn.enableDisplayMode();
38779                 /*
38780                 if(c.showPin === true || this.showPin){
38781                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38782                     this.stickBtn.enableDisplayMode();
38783                     this.stickBtn.on("click", this.expand, this);
38784                     this.stickBtn.hide();
38785                 }
38786                 
38787             }
38788             */
38789             /** This region's collapsed element
38790             * @type Roo.Element */
38791             /*
38792              *
38793             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38794                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38795             ]}, true);
38796             
38797             if(c.floatable !== false){
38798                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38799                this.collapsedEl.on("click", this.collapseClick, this);
38800             }
38801
38802             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38803                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38804                    id: "message", unselectable: "on", style:{"float":"left"}});
38805                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38806              }
38807             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38808             this.expandBtn.on("click", this.expand, this);
38809             
38810         }
38811         
38812         if(this.collapseBtn){
38813             this.collapseBtn.setVisible(c.collapsible == true);
38814         }
38815         
38816         this.cmargins = c.cmargins || this.cmargins ||
38817                          (this.position == "west" || this.position == "east" ?
38818                              {top: 0, left: 2, right:2, bottom: 0} :
38819                              {top: 2, left: 0, right:0, bottom: 2});
38820         */
38821         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38822         
38823         
38824         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38825         
38826         this.autoScroll = c.autoScroll || false;
38827         
38828         
38829        
38830         
38831         this.duration = c.duration || .30;
38832         this.slideDuration = c.slideDuration || .45;
38833         this.config = c;
38834        
38835     },
38836     /**
38837      * Returns true if this region is currently visible.
38838      * @return {Boolean}
38839      */
38840     isVisible : function(){
38841         return this.visible;
38842     },
38843
38844     /**
38845      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38846      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38847      */
38848     //setCollapsedTitle : function(title){
38849     //    title = title || "&#160;";
38850      //   if(this.collapsedTitleTextEl){
38851       //      this.collapsedTitleTextEl.innerHTML = title;
38852        // }
38853     //},
38854
38855     getBox : function(){
38856         var b;
38857       //  if(!this.collapsed){
38858             b = this.el.getBox(false, true);
38859        // }else{
38860           //  b = this.collapsedEl.getBox(false, true);
38861         //}
38862         return b;
38863     },
38864
38865     getMargins : function(){
38866         return this.margins;
38867         //return this.collapsed ? this.cmargins : this.margins;
38868     },
38869 /*
38870     highlight : function(){
38871         this.el.addClass("x-layout-panel-dragover");
38872     },
38873
38874     unhighlight : function(){
38875         this.el.removeClass("x-layout-panel-dragover");
38876     },
38877 */
38878     updateBox : function(box)
38879     {
38880         if (!this.bodyEl) {
38881             return; // not rendered yet..
38882         }
38883         
38884         this.box = box;
38885         if(!this.collapsed){
38886             this.el.dom.style.left = box.x + "px";
38887             this.el.dom.style.top = box.y + "px";
38888             this.updateBody(box.width, box.height);
38889         }else{
38890             this.collapsedEl.dom.style.left = box.x + "px";
38891             this.collapsedEl.dom.style.top = box.y + "px";
38892             this.collapsedEl.setSize(box.width, box.height);
38893         }
38894         if(this.tabs){
38895             this.tabs.autoSizeTabs();
38896         }
38897     },
38898
38899     updateBody : function(w, h)
38900     {
38901         if(w !== null){
38902             this.el.setWidth(w);
38903             w -= this.el.getBorderWidth("rl");
38904             if(this.config.adjustments){
38905                 w += this.config.adjustments[0];
38906             }
38907         }
38908         if(h !== null && h > 0){
38909             this.el.setHeight(h);
38910             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38911             h -= this.el.getBorderWidth("tb");
38912             if(this.config.adjustments){
38913                 h += this.config.adjustments[1];
38914             }
38915             this.bodyEl.setHeight(h);
38916             if(this.tabs){
38917                 h = this.tabs.syncHeight(h);
38918             }
38919         }
38920         if(this.panelSize){
38921             w = w !== null ? w : this.panelSize.width;
38922             h = h !== null ? h : this.panelSize.height;
38923         }
38924         if(this.activePanel){
38925             var el = this.activePanel.getEl();
38926             w = w !== null ? w : el.getWidth();
38927             h = h !== null ? h : el.getHeight();
38928             this.panelSize = {width: w, height: h};
38929             this.activePanel.setSize(w, h);
38930         }
38931         if(Roo.isIE && this.tabs){
38932             this.tabs.el.repaint();
38933         }
38934     },
38935
38936     /**
38937      * Returns the container element for this region.
38938      * @return {Roo.Element}
38939      */
38940     getEl : function(){
38941         return this.el;
38942     },
38943
38944     /**
38945      * Hides this region.
38946      */
38947     hide : function(){
38948         //if(!this.collapsed){
38949             this.el.dom.style.left = "-2000px";
38950             this.el.hide();
38951         //}else{
38952          //   this.collapsedEl.dom.style.left = "-2000px";
38953          //   this.collapsedEl.hide();
38954        // }
38955         this.visible = false;
38956         this.fireEvent("visibilitychange", this, false);
38957     },
38958
38959     /**
38960      * Shows this region if it was previously hidden.
38961      */
38962     show : function(){
38963         //if(!this.collapsed){
38964             this.el.show();
38965         //}else{
38966         //    this.collapsedEl.show();
38967        // }
38968         this.visible = true;
38969         this.fireEvent("visibilitychange", this, true);
38970     },
38971 /*
38972     closeClicked : function(){
38973         if(this.activePanel){
38974             this.remove(this.activePanel);
38975         }
38976     },
38977
38978     collapseClick : function(e){
38979         if(this.isSlid){
38980            e.stopPropagation();
38981            this.slideIn();
38982         }else{
38983            e.stopPropagation();
38984            this.slideOut();
38985         }
38986     },
38987 */
38988     /**
38989      * Collapses this region.
38990      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38991      */
38992     /*
38993     collapse : function(skipAnim, skipCheck = false){
38994         if(this.collapsed) {
38995             return;
38996         }
38997         
38998         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38999             
39000             this.collapsed = true;
39001             if(this.split){
39002                 this.split.el.hide();
39003             }
39004             if(this.config.animate && skipAnim !== true){
39005                 this.fireEvent("invalidated", this);
39006                 this.animateCollapse();
39007             }else{
39008                 this.el.setLocation(-20000,-20000);
39009                 this.el.hide();
39010                 this.collapsedEl.show();
39011                 this.fireEvent("collapsed", this);
39012                 this.fireEvent("invalidated", this);
39013             }
39014         }
39015         
39016     },
39017 */
39018     animateCollapse : function(){
39019         // overridden
39020     },
39021
39022     /**
39023      * Expands this region if it was previously collapsed.
39024      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39025      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39026      */
39027     /*
39028     expand : function(e, skipAnim){
39029         if(e) {
39030             e.stopPropagation();
39031         }
39032         if(!this.collapsed || this.el.hasActiveFx()) {
39033             return;
39034         }
39035         if(this.isSlid){
39036             this.afterSlideIn();
39037             skipAnim = true;
39038         }
39039         this.collapsed = false;
39040         if(this.config.animate && skipAnim !== true){
39041             this.animateExpand();
39042         }else{
39043             this.el.show();
39044             if(this.split){
39045                 this.split.el.show();
39046             }
39047             this.collapsedEl.setLocation(-2000,-2000);
39048             this.collapsedEl.hide();
39049             this.fireEvent("invalidated", this);
39050             this.fireEvent("expanded", this);
39051         }
39052     },
39053 */
39054     animateExpand : function(){
39055         // overridden
39056     },
39057
39058     initTabs : function()
39059     {
39060         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39061         
39062         var ts = new Roo.bootstrap.panel.Tabs({
39063             el: this.bodyEl.dom,
39064             region : this,
39065             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39066             disableTooltips: this.config.disableTabTips,
39067             toolbar : this.config.toolbar
39068         });
39069         
39070         if(this.config.hideTabs){
39071             ts.stripWrap.setDisplayed(false);
39072         }
39073         this.tabs = ts;
39074         ts.resizeTabs = this.config.resizeTabs === true;
39075         ts.minTabWidth = this.config.minTabWidth || 40;
39076         ts.maxTabWidth = this.config.maxTabWidth || 250;
39077         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39078         ts.monitorResize = false;
39079         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39080         ts.bodyEl.addClass('roo-layout-tabs-body');
39081         this.panels.each(this.initPanelAsTab, this);
39082     },
39083
39084     initPanelAsTab : function(panel){
39085         var ti = this.tabs.addTab(
39086             panel.getEl().id,
39087             panel.getTitle(),
39088             null,
39089             this.config.closeOnTab && panel.isClosable(),
39090             panel.tpl
39091         );
39092         if(panel.tabTip !== undefined){
39093             ti.setTooltip(panel.tabTip);
39094         }
39095         ti.on("activate", function(){
39096               this.setActivePanel(panel);
39097         }, this);
39098         
39099         if(this.config.closeOnTab){
39100             ti.on("beforeclose", function(t, e){
39101                 e.cancel = true;
39102                 this.remove(panel);
39103             }, this);
39104         }
39105         
39106         panel.tabItem = ti;
39107         
39108         return ti;
39109     },
39110
39111     updatePanelTitle : function(panel, title)
39112     {
39113         if(this.activePanel == panel){
39114             this.updateTitle(title);
39115         }
39116         if(this.tabs){
39117             var ti = this.tabs.getTab(panel.getEl().id);
39118             ti.setText(title);
39119             if(panel.tabTip !== undefined){
39120                 ti.setTooltip(panel.tabTip);
39121             }
39122         }
39123     },
39124
39125     updateTitle : function(title){
39126         if(this.titleTextEl && !this.config.title){
39127             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39128         }
39129     },
39130
39131     setActivePanel : function(panel)
39132     {
39133         panel = this.getPanel(panel);
39134         if(this.activePanel && this.activePanel != panel){
39135             if(this.activePanel.setActiveState(false) === false){
39136                 return;
39137             }
39138         }
39139         this.activePanel = panel;
39140         panel.setActiveState(true);
39141         if(this.panelSize){
39142             panel.setSize(this.panelSize.width, this.panelSize.height);
39143         }
39144         if(this.closeBtn){
39145             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39146         }
39147         this.updateTitle(panel.getTitle());
39148         if(this.tabs){
39149             this.fireEvent("invalidated", this);
39150         }
39151         this.fireEvent("panelactivated", this, panel);
39152     },
39153
39154     /**
39155      * Shows the specified panel.
39156      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39157      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39158      */
39159     showPanel : function(panel)
39160     {
39161         panel = this.getPanel(panel);
39162         if(panel){
39163             if(this.tabs){
39164                 var tab = this.tabs.getTab(panel.getEl().id);
39165                 if(tab.isHidden()){
39166                     this.tabs.unhideTab(tab.id);
39167                 }
39168                 tab.activate();
39169             }else{
39170                 this.setActivePanel(panel);
39171             }
39172         }
39173         return panel;
39174     },
39175
39176     /**
39177      * Get the active panel for this region.
39178      * @return {Roo.ContentPanel} The active panel or null
39179      */
39180     getActivePanel : function(){
39181         return this.activePanel;
39182     },
39183
39184     validateVisibility : function(){
39185         if(this.panels.getCount() < 1){
39186             this.updateTitle("&#160;");
39187             this.closeBtn.hide();
39188             this.hide();
39189         }else{
39190             if(!this.isVisible()){
39191                 this.show();
39192             }
39193         }
39194     },
39195
39196     /**
39197      * Adds the passed ContentPanel(s) to this region.
39198      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39199      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39200      */
39201     add : function(panel)
39202     {
39203         if(arguments.length > 1){
39204             for(var i = 0, len = arguments.length; i < len; i++) {
39205                 this.add(arguments[i]);
39206             }
39207             return null;
39208         }
39209         
39210         // if we have not been rendered yet, then we can not really do much of this..
39211         if (!this.bodyEl) {
39212             this.unrendered_panels.push(panel);
39213             return panel;
39214         }
39215         
39216         
39217         
39218         
39219         if(this.hasPanel(panel)){
39220             this.showPanel(panel);
39221             return panel;
39222         }
39223         panel.setRegion(this);
39224         this.panels.add(panel);
39225        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39226             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39227             // and hide them... ???
39228             this.bodyEl.dom.appendChild(panel.getEl().dom);
39229             if(panel.background !== true){
39230                 this.setActivePanel(panel);
39231             }
39232             this.fireEvent("paneladded", this, panel);
39233             return panel;
39234         }
39235         */
39236         if(!this.tabs){
39237             this.initTabs();
39238         }else{
39239             this.initPanelAsTab(panel);
39240         }
39241         
39242         
39243         if(panel.background !== true){
39244             this.tabs.activate(panel.getEl().id);
39245         }
39246         this.fireEvent("paneladded", this, panel);
39247         return panel;
39248     },
39249
39250     /**
39251      * Hides the tab for the specified panel.
39252      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39253      */
39254     hidePanel : function(panel){
39255         if(this.tabs && (panel = this.getPanel(panel))){
39256             this.tabs.hideTab(panel.getEl().id);
39257         }
39258     },
39259
39260     /**
39261      * Unhides the tab for a previously hidden panel.
39262      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39263      */
39264     unhidePanel : function(panel){
39265         if(this.tabs && (panel = this.getPanel(panel))){
39266             this.tabs.unhideTab(panel.getEl().id);
39267         }
39268     },
39269
39270     clearPanels : function(){
39271         while(this.panels.getCount() > 0){
39272              this.remove(this.panels.first());
39273         }
39274     },
39275
39276     /**
39277      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39278      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39279      * @param {Boolean} preservePanel Overrides the config preservePanel option
39280      * @return {Roo.ContentPanel} The panel that was removed
39281      */
39282     remove : function(panel, preservePanel)
39283     {
39284         panel = this.getPanel(panel);
39285         if(!panel){
39286             return null;
39287         }
39288         var e = {};
39289         this.fireEvent("beforeremove", this, panel, e);
39290         if(e.cancel === true){
39291             return null;
39292         }
39293         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39294         var panelId = panel.getId();
39295         this.panels.removeKey(panelId);
39296         if(preservePanel){
39297             document.body.appendChild(panel.getEl().dom);
39298         }
39299         if(this.tabs){
39300             this.tabs.removeTab(panel.getEl().id);
39301         }else if (!preservePanel){
39302             this.bodyEl.dom.removeChild(panel.getEl().dom);
39303         }
39304         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39305             var p = this.panels.first();
39306             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39307             tempEl.appendChild(p.getEl().dom);
39308             this.bodyEl.update("");
39309             this.bodyEl.dom.appendChild(p.getEl().dom);
39310             tempEl = null;
39311             this.updateTitle(p.getTitle());
39312             this.tabs = null;
39313             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39314             this.setActivePanel(p);
39315         }
39316         panel.setRegion(null);
39317         if(this.activePanel == panel){
39318             this.activePanel = null;
39319         }
39320         if(this.config.autoDestroy !== false && preservePanel !== true){
39321             try{panel.destroy();}catch(e){}
39322         }
39323         this.fireEvent("panelremoved", this, panel);
39324         return panel;
39325     },
39326
39327     /**
39328      * Returns the TabPanel component used by this region
39329      * @return {Roo.TabPanel}
39330      */
39331     getTabs : function(){
39332         return this.tabs;
39333     },
39334
39335     createTool : function(parentEl, className){
39336         var btn = Roo.DomHelper.append(parentEl, {
39337             tag: "div",
39338             cls: "x-layout-tools-button",
39339             children: [ {
39340                 tag: "div",
39341                 cls: "roo-layout-tools-button-inner " + className,
39342                 html: "&#160;"
39343             }]
39344         }, true);
39345         btn.addClassOnOver("roo-layout-tools-button-over");
39346         return btn;
39347     }
39348 });/*
39349  * Based on:
39350  * Ext JS Library 1.1.1
39351  * Copyright(c) 2006-2007, Ext JS, LLC.
39352  *
39353  * Originally Released Under LGPL - original licence link has changed is not relivant.
39354  *
39355  * Fork - LGPL
39356  * <script type="text/javascript">
39357  */
39358  
39359
39360
39361 /**
39362  * @class Roo.SplitLayoutRegion
39363  * @extends Roo.LayoutRegion
39364  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39365  */
39366 Roo.bootstrap.layout.Split = function(config){
39367     this.cursor = config.cursor;
39368     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39369 };
39370
39371 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39372 {
39373     splitTip : "Drag to resize.",
39374     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39375     useSplitTips : false,
39376
39377     applyConfig : function(config){
39378         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39379     },
39380     
39381     onRender : function(ctr,pos) {
39382         
39383         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39384         if(!this.config.split){
39385             return;
39386         }
39387         if(!this.split){
39388             
39389             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39390                             tag: "div",
39391                             id: this.el.id + "-split",
39392                             cls: "roo-layout-split roo-layout-split-"+this.position,
39393                             html: "&#160;"
39394             });
39395             /** The SplitBar for this region 
39396             * @type Roo.SplitBar */
39397             // does not exist yet...
39398             Roo.log([this.position, this.orientation]);
39399             
39400             this.split = new Roo.bootstrap.SplitBar({
39401                 dragElement : splitEl,
39402                 resizingElement: this.el,
39403                 orientation : this.orientation
39404             });
39405             
39406             this.split.on("moved", this.onSplitMove, this);
39407             this.split.useShim = this.config.useShim === true;
39408             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39409             if(this.useSplitTips){
39410                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39411             }
39412             //if(config.collapsible){
39413             //    this.split.el.on("dblclick", this.collapse,  this);
39414             //}
39415         }
39416         if(typeof this.config.minSize != "undefined"){
39417             this.split.minSize = this.config.minSize;
39418         }
39419         if(typeof this.config.maxSize != "undefined"){
39420             this.split.maxSize = this.config.maxSize;
39421         }
39422         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39423             this.hideSplitter();
39424         }
39425         
39426     },
39427
39428     getHMaxSize : function(){
39429          var cmax = this.config.maxSize || 10000;
39430          var center = this.mgr.getRegion("center");
39431          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39432     },
39433
39434     getVMaxSize : function(){
39435          var cmax = this.config.maxSize || 10000;
39436          var center = this.mgr.getRegion("center");
39437          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39438     },
39439
39440     onSplitMove : function(split, newSize){
39441         this.fireEvent("resized", this, newSize);
39442     },
39443     
39444     /** 
39445      * Returns the {@link Roo.SplitBar} for this region.
39446      * @return {Roo.SplitBar}
39447      */
39448     getSplitBar : function(){
39449         return this.split;
39450     },
39451     
39452     hide : function(){
39453         this.hideSplitter();
39454         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39455     },
39456
39457     hideSplitter : function(){
39458         if(this.split){
39459             this.split.el.setLocation(-2000,-2000);
39460             this.split.el.hide();
39461         }
39462     },
39463
39464     show : function(){
39465         if(this.split){
39466             this.split.el.show();
39467         }
39468         Roo.bootstrap.layout.Split.superclass.show.call(this);
39469     },
39470     
39471     beforeSlide: function(){
39472         if(Roo.isGecko){// firefox overflow auto bug workaround
39473             this.bodyEl.clip();
39474             if(this.tabs) {
39475                 this.tabs.bodyEl.clip();
39476             }
39477             if(this.activePanel){
39478                 this.activePanel.getEl().clip();
39479                 
39480                 if(this.activePanel.beforeSlide){
39481                     this.activePanel.beforeSlide();
39482                 }
39483             }
39484         }
39485     },
39486     
39487     afterSlide : function(){
39488         if(Roo.isGecko){// firefox overflow auto bug workaround
39489             this.bodyEl.unclip();
39490             if(this.tabs) {
39491                 this.tabs.bodyEl.unclip();
39492             }
39493             if(this.activePanel){
39494                 this.activePanel.getEl().unclip();
39495                 if(this.activePanel.afterSlide){
39496                     this.activePanel.afterSlide();
39497                 }
39498             }
39499         }
39500     },
39501
39502     initAutoHide : function(){
39503         if(this.autoHide !== false){
39504             if(!this.autoHideHd){
39505                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39506                 this.autoHideHd = {
39507                     "mouseout": function(e){
39508                         if(!e.within(this.el, true)){
39509                             st.delay(500);
39510                         }
39511                     },
39512                     "mouseover" : function(e){
39513                         st.cancel();
39514                     },
39515                     scope : this
39516                 };
39517             }
39518             this.el.on(this.autoHideHd);
39519         }
39520     },
39521
39522     clearAutoHide : function(){
39523         if(this.autoHide !== false){
39524             this.el.un("mouseout", this.autoHideHd.mouseout);
39525             this.el.un("mouseover", this.autoHideHd.mouseover);
39526         }
39527     },
39528
39529     clearMonitor : function(){
39530         Roo.get(document).un("click", this.slideInIf, this);
39531     },
39532
39533     // these names are backwards but not changed for compat
39534     slideOut : function(){
39535         if(this.isSlid || this.el.hasActiveFx()){
39536             return;
39537         }
39538         this.isSlid = true;
39539         if(this.collapseBtn){
39540             this.collapseBtn.hide();
39541         }
39542         this.closeBtnState = this.closeBtn.getStyle('display');
39543         this.closeBtn.hide();
39544         if(this.stickBtn){
39545             this.stickBtn.show();
39546         }
39547         this.el.show();
39548         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39549         this.beforeSlide();
39550         this.el.setStyle("z-index", 10001);
39551         this.el.slideIn(this.getSlideAnchor(), {
39552             callback: function(){
39553                 this.afterSlide();
39554                 this.initAutoHide();
39555                 Roo.get(document).on("click", this.slideInIf, this);
39556                 this.fireEvent("slideshow", this);
39557             },
39558             scope: this,
39559             block: true
39560         });
39561     },
39562
39563     afterSlideIn : function(){
39564         this.clearAutoHide();
39565         this.isSlid = false;
39566         this.clearMonitor();
39567         this.el.setStyle("z-index", "");
39568         if(this.collapseBtn){
39569             this.collapseBtn.show();
39570         }
39571         this.closeBtn.setStyle('display', this.closeBtnState);
39572         if(this.stickBtn){
39573             this.stickBtn.hide();
39574         }
39575         this.fireEvent("slidehide", this);
39576     },
39577
39578     slideIn : function(cb){
39579         if(!this.isSlid || this.el.hasActiveFx()){
39580             Roo.callback(cb);
39581             return;
39582         }
39583         this.isSlid = false;
39584         this.beforeSlide();
39585         this.el.slideOut(this.getSlideAnchor(), {
39586             callback: function(){
39587                 this.el.setLeftTop(-10000, -10000);
39588                 this.afterSlide();
39589                 this.afterSlideIn();
39590                 Roo.callback(cb);
39591             },
39592             scope: this,
39593             block: true
39594         });
39595     },
39596     
39597     slideInIf : function(e){
39598         if(!e.within(this.el)){
39599             this.slideIn();
39600         }
39601     },
39602
39603     animateCollapse : function(){
39604         this.beforeSlide();
39605         this.el.setStyle("z-index", 20000);
39606         var anchor = this.getSlideAnchor();
39607         this.el.slideOut(anchor, {
39608             callback : function(){
39609                 this.el.setStyle("z-index", "");
39610                 this.collapsedEl.slideIn(anchor, {duration:.3});
39611                 this.afterSlide();
39612                 this.el.setLocation(-10000,-10000);
39613                 this.el.hide();
39614                 this.fireEvent("collapsed", this);
39615             },
39616             scope: this,
39617             block: true
39618         });
39619     },
39620
39621     animateExpand : function(){
39622         this.beforeSlide();
39623         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39624         this.el.setStyle("z-index", 20000);
39625         this.collapsedEl.hide({
39626             duration:.1
39627         });
39628         this.el.slideIn(this.getSlideAnchor(), {
39629             callback : function(){
39630                 this.el.setStyle("z-index", "");
39631                 this.afterSlide();
39632                 if(this.split){
39633                     this.split.el.show();
39634                 }
39635                 this.fireEvent("invalidated", this);
39636                 this.fireEvent("expanded", this);
39637             },
39638             scope: this,
39639             block: true
39640         });
39641     },
39642
39643     anchors : {
39644         "west" : "left",
39645         "east" : "right",
39646         "north" : "top",
39647         "south" : "bottom"
39648     },
39649
39650     sanchors : {
39651         "west" : "l",
39652         "east" : "r",
39653         "north" : "t",
39654         "south" : "b"
39655     },
39656
39657     canchors : {
39658         "west" : "tl-tr",
39659         "east" : "tr-tl",
39660         "north" : "tl-bl",
39661         "south" : "bl-tl"
39662     },
39663
39664     getAnchor : function(){
39665         return this.anchors[this.position];
39666     },
39667
39668     getCollapseAnchor : function(){
39669         return this.canchors[this.position];
39670     },
39671
39672     getSlideAnchor : function(){
39673         return this.sanchors[this.position];
39674     },
39675
39676     getAlignAdj : function(){
39677         var cm = this.cmargins;
39678         switch(this.position){
39679             case "west":
39680                 return [0, 0];
39681             break;
39682             case "east":
39683                 return [0, 0];
39684             break;
39685             case "north":
39686                 return [0, 0];
39687             break;
39688             case "south":
39689                 return [0, 0];
39690             break;
39691         }
39692     },
39693
39694     getExpandAdj : function(){
39695         var c = this.collapsedEl, cm = this.cmargins;
39696         switch(this.position){
39697             case "west":
39698                 return [-(cm.right+c.getWidth()+cm.left), 0];
39699             break;
39700             case "east":
39701                 return [cm.right+c.getWidth()+cm.left, 0];
39702             break;
39703             case "north":
39704                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39705             break;
39706             case "south":
39707                 return [0, cm.top+cm.bottom+c.getHeight()];
39708             break;
39709         }
39710     }
39711 });/*
39712  * Based on:
39713  * Ext JS Library 1.1.1
39714  * Copyright(c) 2006-2007, Ext JS, LLC.
39715  *
39716  * Originally Released Under LGPL - original licence link has changed is not relivant.
39717  *
39718  * Fork - LGPL
39719  * <script type="text/javascript">
39720  */
39721 /*
39722  * These classes are private internal classes
39723  */
39724 Roo.bootstrap.layout.Center = function(config){
39725     config.region = "center";
39726     Roo.bootstrap.layout.Region.call(this, config);
39727     this.visible = true;
39728     this.minWidth = config.minWidth || 20;
39729     this.minHeight = config.minHeight || 20;
39730 };
39731
39732 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39733     hide : function(){
39734         // center panel can't be hidden
39735     },
39736     
39737     show : function(){
39738         // center panel can't be hidden
39739     },
39740     
39741     getMinWidth: function(){
39742         return this.minWidth;
39743     },
39744     
39745     getMinHeight: function(){
39746         return this.minHeight;
39747     }
39748 });
39749
39750
39751
39752
39753  
39754
39755
39756
39757
39758
39759
39760 Roo.bootstrap.layout.North = function(config)
39761 {
39762     config.region = 'north';
39763     config.cursor = 'n-resize';
39764     
39765     Roo.bootstrap.layout.Split.call(this, config);
39766     
39767     
39768     if(this.split){
39769         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39770         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39771         this.split.el.addClass("roo-layout-split-v");
39772     }
39773     //var size = config.initialSize || config.height;
39774     //if(this.el && typeof size != "undefined"){
39775     //    this.el.setHeight(size);
39776     //}
39777 };
39778 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39779 {
39780     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39781      
39782      
39783     onRender : function(ctr, pos)
39784     {
39785         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39786         var size = this.config.initialSize || this.config.height;
39787         if(this.el && typeof size != "undefined"){
39788             this.el.setHeight(size);
39789         }
39790     
39791     },
39792     
39793     getBox : function(){
39794         if(this.collapsed){
39795             return this.collapsedEl.getBox();
39796         }
39797         var box = this.el.getBox();
39798         if(this.split){
39799             box.height += this.split.el.getHeight();
39800         }
39801         return box;
39802     },
39803     
39804     updateBox : function(box){
39805         if(this.split && !this.collapsed){
39806             box.height -= this.split.el.getHeight();
39807             this.split.el.setLeft(box.x);
39808             this.split.el.setTop(box.y+box.height);
39809             this.split.el.setWidth(box.width);
39810         }
39811         if(this.collapsed){
39812             this.updateBody(box.width, null);
39813         }
39814         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39815     }
39816 });
39817
39818
39819
39820
39821
39822 Roo.bootstrap.layout.South = function(config){
39823     config.region = 'south';
39824     config.cursor = 's-resize';
39825     Roo.bootstrap.layout.Split.call(this, config);
39826     if(this.split){
39827         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39828         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39829         this.split.el.addClass("roo-layout-split-v");
39830     }
39831     
39832 };
39833
39834 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39835     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39836     
39837     onRender : function(ctr, pos)
39838     {
39839         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39840         var size = this.config.initialSize || this.config.height;
39841         if(this.el && typeof size != "undefined"){
39842             this.el.setHeight(size);
39843         }
39844     
39845     },
39846     
39847     getBox : function(){
39848         if(this.collapsed){
39849             return this.collapsedEl.getBox();
39850         }
39851         var box = this.el.getBox();
39852         if(this.split){
39853             var sh = this.split.el.getHeight();
39854             box.height += sh;
39855             box.y -= sh;
39856         }
39857         return box;
39858     },
39859     
39860     updateBox : function(box){
39861         if(this.split && !this.collapsed){
39862             var sh = this.split.el.getHeight();
39863             box.height -= sh;
39864             box.y += sh;
39865             this.split.el.setLeft(box.x);
39866             this.split.el.setTop(box.y-sh);
39867             this.split.el.setWidth(box.width);
39868         }
39869         if(this.collapsed){
39870             this.updateBody(box.width, null);
39871         }
39872         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39873     }
39874 });
39875
39876 Roo.bootstrap.layout.East = function(config){
39877     config.region = "east";
39878     config.cursor = "e-resize";
39879     Roo.bootstrap.layout.Split.call(this, config);
39880     if(this.split){
39881         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39882         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39883         this.split.el.addClass("roo-layout-split-h");
39884     }
39885     
39886 };
39887 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39888     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39889     
39890     onRender : function(ctr, pos)
39891     {
39892         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39893         var size = this.config.initialSize || this.config.width;
39894         if(this.el && typeof size != "undefined"){
39895             this.el.setWidth(size);
39896         }
39897     
39898     },
39899     
39900     getBox : function(){
39901         if(this.collapsed){
39902             return this.collapsedEl.getBox();
39903         }
39904         var box = this.el.getBox();
39905         if(this.split){
39906             var sw = this.split.el.getWidth();
39907             box.width += sw;
39908             box.x -= sw;
39909         }
39910         return box;
39911     },
39912
39913     updateBox : function(box){
39914         if(this.split && !this.collapsed){
39915             var sw = this.split.el.getWidth();
39916             box.width -= sw;
39917             this.split.el.setLeft(box.x);
39918             this.split.el.setTop(box.y);
39919             this.split.el.setHeight(box.height);
39920             box.x += sw;
39921         }
39922         if(this.collapsed){
39923             this.updateBody(null, box.height);
39924         }
39925         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39926     }
39927 });
39928
39929 Roo.bootstrap.layout.West = function(config){
39930     config.region = "west";
39931     config.cursor = "w-resize";
39932     
39933     Roo.bootstrap.layout.Split.call(this, config);
39934     if(this.split){
39935         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39936         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39937         this.split.el.addClass("roo-layout-split-h");
39938     }
39939     
39940 };
39941 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39942     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39943     
39944     onRender: function(ctr, pos)
39945     {
39946         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39947         var size = this.config.initialSize || this.config.width;
39948         if(typeof size != "undefined"){
39949             this.el.setWidth(size);
39950         }
39951     },
39952     
39953     getBox : function(){
39954         if(this.collapsed){
39955             return this.collapsedEl.getBox();
39956         }
39957         var box = this.el.getBox();
39958         if (box.width == 0) {
39959             box.width = this.config.width; // kludge?
39960         }
39961         if(this.split){
39962             box.width += this.split.el.getWidth();
39963         }
39964         return box;
39965     },
39966     
39967     updateBox : function(box){
39968         if(this.split && !this.collapsed){
39969             var sw = this.split.el.getWidth();
39970             box.width -= sw;
39971             this.split.el.setLeft(box.x+box.width);
39972             this.split.el.setTop(box.y);
39973             this.split.el.setHeight(box.height);
39974         }
39975         if(this.collapsed){
39976             this.updateBody(null, box.height);
39977         }
39978         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39979     }
39980 });Roo.namespace("Roo.bootstrap.panel");/*
39981  * Based on:
39982  * Ext JS Library 1.1.1
39983  * Copyright(c) 2006-2007, Ext JS, LLC.
39984  *
39985  * Originally Released Under LGPL - original licence link has changed is not relivant.
39986  *
39987  * Fork - LGPL
39988  * <script type="text/javascript">
39989  */
39990 /**
39991  * @class Roo.ContentPanel
39992  * @extends Roo.util.Observable
39993  * A basic ContentPanel element.
39994  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39995  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39996  * @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
39997  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39998  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39999  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40000  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40001  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40002  * @cfg {String} title          The title for this panel
40003  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40004  * @cfg {String} url            Calls {@link #setUrl} with this value
40005  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40006  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40007  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40008  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40009  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40010  * @cfg {Boolean} badges render the badges
40011  * @cfg {String} cls  extra classes to use  
40012  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40013
40014  * @constructor
40015  * Create a new ContentPanel.
40016  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40017  * @param {String/Object} config A string to set only the title or a config object
40018  * @param {String} content (optional) Set the HTML content for this panel
40019  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40020  */
40021 Roo.bootstrap.panel.Content = function( config){
40022     
40023     this.tpl = config.tpl || false;
40024     
40025     var el = config.el;
40026     var content = config.content;
40027
40028     if(config.autoCreate){ // xtype is available if this is called from factory
40029         el = Roo.id();
40030     }
40031     this.el = Roo.get(el);
40032     if(!this.el && config && config.autoCreate){
40033         if(typeof config.autoCreate == "object"){
40034             if(!config.autoCreate.id){
40035                 config.autoCreate.id = config.id||el;
40036             }
40037             this.el = Roo.DomHelper.append(document.body,
40038                         config.autoCreate, true);
40039         }else{
40040             var elcfg =  {
40041                 tag: "div",
40042                 cls: (config.cls || '') +
40043                     (config.background ? ' bg-' + config.background : '') +
40044                     " roo-layout-inactive-content",
40045                 id: config.id||el
40046             };
40047             if (config.iframe) {
40048                 elcfg.cn = [
40049                     {
40050                         tag : 'iframe',
40051                         style : 'border: 0px',
40052                         src : 'about:blank'
40053                     }
40054                 ];
40055             }
40056               
40057             if (config.html) {
40058                 elcfg.html = config.html;
40059                 
40060             }
40061                         
40062             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40063             if (config.iframe) {
40064                 this.iframeEl = this.el.select('iframe',true).first();
40065             }
40066             
40067         }
40068     } 
40069     this.closable = false;
40070     this.loaded = false;
40071     this.active = false;
40072    
40073       
40074     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40075         
40076         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40077         
40078         this.wrapEl = this.el; //this.el.wrap();
40079         var ti = [];
40080         if (config.toolbar.items) {
40081             ti = config.toolbar.items ;
40082             delete config.toolbar.items ;
40083         }
40084         
40085         var nitems = [];
40086         this.toolbar.render(this.wrapEl, 'before');
40087         for(var i =0;i < ti.length;i++) {
40088           //  Roo.log(['add child', items[i]]);
40089             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40090         }
40091         this.toolbar.items = nitems;
40092         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40093         delete config.toolbar;
40094         
40095     }
40096     /*
40097     // xtype created footer. - not sure if will work as we normally have to render first..
40098     if (this.footer && !this.footer.el && this.footer.xtype) {
40099         if (!this.wrapEl) {
40100             this.wrapEl = this.el.wrap();
40101         }
40102     
40103         this.footer.container = this.wrapEl.createChild();
40104          
40105         this.footer = Roo.factory(this.footer, Roo);
40106         
40107     }
40108     */
40109     
40110      if(typeof config == "string"){
40111         this.title = config;
40112     }else{
40113         Roo.apply(this, config);
40114     }
40115     
40116     if(this.resizeEl){
40117         this.resizeEl = Roo.get(this.resizeEl, true);
40118     }else{
40119         this.resizeEl = this.el;
40120     }
40121     // handle view.xtype
40122     
40123  
40124     
40125     
40126     this.addEvents({
40127         /**
40128          * @event activate
40129          * Fires when this panel is activated. 
40130          * @param {Roo.ContentPanel} this
40131          */
40132         "activate" : true,
40133         /**
40134          * @event deactivate
40135          * Fires when this panel is activated. 
40136          * @param {Roo.ContentPanel} this
40137          */
40138         "deactivate" : true,
40139
40140         /**
40141          * @event resize
40142          * Fires when this panel is resized if fitToFrame is true.
40143          * @param {Roo.ContentPanel} this
40144          * @param {Number} width The width after any component adjustments
40145          * @param {Number} height The height after any component adjustments
40146          */
40147         "resize" : true,
40148         
40149          /**
40150          * @event render
40151          * Fires when this tab is created
40152          * @param {Roo.ContentPanel} this
40153          */
40154         "render" : true
40155         
40156         
40157         
40158     });
40159     
40160
40161     
40162     
40163     if(this.autoScroll && !this.iframe){
40164         this.resizeEl.setStyle("overflow", "auto");
40165     } else {
40166         // fix randome scrolling
40167         //this.el.on('scroll', function() {
40168         //    Roo.log('fix random scolling');
40169         //    this.scrollTo('top',0); 
40170         //});
40171     }
40172     content = content || this.content;
40173     if(content){
40174         this.setContent(content);
40175     }
40176     if(config && config.url){
40177         this.setUrl(this.url, this.params, this.loadOnce);
40178     }
40179     
40180     
40181     
40182     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40183     
40184     if (this.view && typeof(this.view.xtype) != 'undefined') {
40185         this.view.el = this.el.appendChild(document.createElement("div"));
40186         this.view = Roo.factory(this.view); 
40187         this.view.render  &&  this.view.render(false, '');  
40188     }
40189     
40190     
40191     this.fireEvent('render', this);
40192 };
40193
40194 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40195     
40196     cls : '',
40197     background : '',
40198     
40199     tabTip : '',
40200     
40201     iframe : false,
40202     iframeEl : false,
40203     
40204     setRegion : function(region){
40205         this.region = region;
40206         this.setActiveClass(region && !this.background);
40207     },
40208     
40209     
40210     setActiveClass: function(state)
40211     {
40212         if(state){
40213            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40214            this.el.setStyle('position','relative');
40215         }else{
40216            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40217            this.el.setStyle('position', 'absolute');
40218         } 
40219     },
40220     
40221     /**
40222      * Returns the toolbar for this Panel if one was configured. 
40223      * @return {Roo.Toolbar} 
40224      */
40225     getToolbar : function(){
40226         return this.toolbar;
40227     },
40228     
40229     setActiveState : function(active)
40230     {
40231         this.active = active;
40232         this.setActiveClass(active);
40233         if(!active){
40234             if(this.fireEvent("deactivate", this) === false){
40235                 return false;
40236             }
40237             return true;
40238         }
40239         this.fireEvent("activate", this);
40240         return true;
40241     },
40242     /**
40243      * Updates this panel's element (not for iframe)
40244      * @param {String} content The new content
40245      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40246     */
40247     setContent : function(content, loadScripts){
40248         if (this.iframe) {
40249             return;
40250         }
40251         
40252         this.el.update(content, loadScripts);
40253     },
40254
40255     ignoreResize : function(w, h){
40256         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40257             return true;
40258         }else{
40259             this.lastSize = {width: w, height: h};
40260             return false;
40261         }
40262     },
40263     /**
40264      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40265      * @return {Roo.UpdateManager} The UpdateManager
40266      */
40267     getUpdateManager : function(){
40268         if (this.iframe) {
40269             return false;
40270         }
40271         return this.el.getUpdateManager();
40272     },
40273      /**
40274      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40275      * Does not work with IFRAME contents
40276      * @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:
40277 <pre><code>
40278 panel.load({
40279     url: "your-url.php",
40280     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40281     callback: yourFunction,
40282     scope: yourObject, //(optional scope)
40283     discardUrl: false,
40284     nocache: false,
40285     text: "Loading...",
40286     timeout: 30,
40287     scripts: false
40288 });
40289 </code></pre>
40290      
40291      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40292      * 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.
40293      * @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}
40294      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40295      * @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.
40296      * @return {Roo.ContentPanel} this
40297      */
40298     load : function(){
40299         
40300         if (this.iframe) {
40301             return this;
40302         }
40303         
40304         var um = this.el.getUpdateManager();
40305         um.update.apply(um, arguments);
40306         return this;
40307     },
40308
40309
40310     /**
40311      * 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.
40312      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40313      * @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)
40314      * @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)
40315      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40316      */
40317     setUrl : function(url, params, loadOnce){
40318         if (this.iframe) {
40319             this.iframeEl.dom.src = url;
40320             return false;
40321         }
40322         
40323         if(this.refreshDelegate){
40324             this.removeListener("activate", this.refreshDelegate);
40325         }
40326         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40327         this.on("activate", this.refreshDelegate);
40328         return this.el.getUpdateManager();
40329     },
40330     
40331     _handleRefresh : function(url, params, loadOnce){
40332         if(!loadOnce || !this.loaded){
40333             var updater = this.el.getUpdateManager();
40334             updater.update(url, params, this._setLoaded.createDelegate(this));
40335         }
40336     },
40337     
40338     _setLoaded : function(){
40339         this.loaded = true;
40340     }, 
40341     
40342     /**
40343      * Returns this panel's id
40344      * @return {String} 
40345      */
40346     getId : function(){
40347         return this.el.id;
40348     },
40349     
40350     /** 
40351      * Returns this panel's element - used by regiosn to add.
40352      * @return {Roo.Element} 
40353      */
40354     getEl : function(){
40355         return this.wrapEl || this.el;
40356     },
40357     
40358    
40359     
40360     adjustForComponents : function(width, height)
40361     {
40362         //Roo.log('adjustForComponents ');
40363         if(this.resizeEl != this.el){
40364             width -= this.el.getFrameWidth('lr');
40365             height -= this.el.getFrameWidth('tb');
40366         }
40367         if(this.toolbar){
40368             var te = this.toolbar.getEl();
40369             te.setWidth(width);
40370             height -= te.getHeight();
40371         }
40372         if(this.footer){
40373             var te = this.footer.getEl();
40374             te.setWidth(width);
40375             height -= te.getHeight();
40376         }
40377         
40378         
40379         if(this.adjustments){
40380             width += this.adjustments[0];
40381             height += this.adjustments[1];
40382         }
40383         return {"width": width, "height": height};
40384     },
40385     
40386     setSize : function(width, height){
40387         if(this.fitToFrame && !this.ignoreResize(width, height)){
40388             if(this.fitContainer && this.resizeEl != this.el){
40389                 this.el.setSize(width, height);
40390             }
40391             var size = this.adjustForComponents(width, height);
40392             if (this.iframe) {
40393                 this.iframeEl.setSize(width,height);
40394             }
40395             
40396             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40397             this.fireEvent('resize', this, size.width, size.height);
40398             
40399             
40400         }
40401     },
40402     
40403     /**
40404      * Returns this panel's title
40405      * @return {String} 
40406      */
40407     getTitle : function(){
40408         
40409         if (typeof(this.title) != 'object') {
40410             return this.title;
40411         }
40412         
40413         var t = '';
40414         for (var k in this.title) {
40415             if (!this.title.hasOwnProperty(k)) {
40416                 continue;
40417             }
40418             
40419             if (k.indexOf('-') >= 0) {
40420                 var s = k.split('-');
40421                 for (var i = 0; i<s.length; i++) {
40422                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40423                 }
40424             } else {
40425                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40426             }
40427         }
40428         return t;
40429     },
40430     
40431     /**
40432      * Set this panel's title
40433      * @param {String} title
40434      */
40435     setTitle : function(title){
40436         this.title = title;
40437         if(this.region){
40438             this.region.updatePanelTitle(this, title);
40439         }
40440     },
40441     
40442     /**
40443      * Returns true is this panel was configured to be closable
40444      * @return {Boolean} 
40445      */
40446     isClosable : function(){
40447         return this.closable;
40448     },
40449     
40450     beforeSlide : function(){
40451         this.el.clip();
40452         this.resizeEl.clip();
40453     },
40454     
40455     afterSlide : function(){
40456         this.el.unclip();
40457         this.resizeEl.unclip();
40458     },
40459     
40460     /**
40461      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40462      *   Will fail silently if the {@link #setUrl} method has not been called.
40463      *   This does not activate the panel, just updates its content.
40464      */
40465     refresh : function(){
40466         if(this.refreshDelegate){
40467            this.loaded = false;
40468            this.refreshDelegate();
40469         }
40470     },
40471     
40472     /**
40473      * Destroys this panel
40474      */
40475     destroy : function(){
40476         this.el.removeAllListeners();
40477         var tempEl = document.createElement("span");
40478         tempEl.appendChild(this.el.dom);
40479         tempEl.innerHTML = "";
40480         this.el.remove();
40481         this.el = null;
40482     },
40483     
40484     /**
40485      * form - if the content panel contains a form - this is a reference to it.
40486      * @type {Roo.form.Form}
40487      */
40488     form : false,
40489     /**
40490      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40491      *    This contains a reference to it.
40492      * @type {Roo.View}
40493      */
40494     view : false,
40495     
40496       /**
40497      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40498      * <pre><code>
40499
40500 layout.addxtype({
40501        xtype : 'Form',
40502        items: [ .... ]
40503    }
40504 );
40505
40506 </code></pre>
40507      * @param {Object} cfg Xtype definition of item to add.
40508      */
40509     
40510     
40511     getChildContainer: function () {
40512         return this.getEl();
40513     }
40514     
40515     
40516     /*
40517         var  ret = new Roo.factory(cfg);
40518         return ret;
40519         
40520         
40521         // add form..
40522         if (cfg.xtype.match(/^Form$/)) {
40523             
40524             var el;
40525             //if (this.footer) {
40526             //    el = this.footer.container.insertSibling(false, 'before');
40527             //} else {
40528                 el = this.el.createChild();
40529             //}
40530
40531             this.form = new  Roo.form.Form(cfg);
40532             
40533             
40534             if ( this.form.allItems.length) {
40535                 this.form.render(el.dom);
40536             }
40537             return this.form;
40538         }
40539         // should only have one of theses..
40540         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40541             // views.. should not be just added - used named prop 'view''
40542             
40543             cfg.el = this.el.appendChild(document.createElement("div"));
40544             // factory?
40545             
40546             var ret = new Roo.factory(cfg);
40547              
40548              ret.render && ret.render(false, ''); // render blank..
40549             this.view = ret;
40550             return ret;
40551         }
40552         return false;
40553     }
40554     \*/
40555 });
40556  
40557 /**
40558  * @class Roo.bootstrap.panel.Grid
40559  * @extends Roo.bootstrap.panel.Content
40560  * @constructor
40561  * Create a new GridPanel.
40562  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40563  * @param {Object} config A the config object
40564   
40565  */
40566
40567
40568
40569 Roo.bootstrap.panel.Grid = function(config)
40570 {
40571     
40572       
40573     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40574         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40575
40576     config.el = this.wrapper;
40577     //this.el = this.wrapper;
40578     
40579       if (config.container) {
40580         // ctor'ed from a Border/panel.grid
40581         
40582         
40583         this.wrapper.setStyle("overflow", "hidden");
40584         this.wrapper.addClass('roo-grid-container');
40585
40586     }
40587     
40588     
40589     if(config.toolbar){
40590         var tool_el = this.wrapper.createChild();    
40591         this.toolbar = Roo.factory(config.toolbar);
40592         var ti = [];
40593         if (config.toolbar.items) {
40594             ti = config.toolbar.items ;
40595             delete config.toolbar.items ;
40596         }
40597         
40598         var nitems = [];
40599         this.toolbar.render(tool_el);
40600         for(var i =0;i < ti.length;i++) {
40601           //  Roo.log(['add child', items[i]]);
40602             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40603         }
40604         this.toolbar.items = nitems;
40605         
40606         delete config.toolbar;
40607     }
40608     
40609     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40610     config.grid.scrollBody = true;;
40611     config.grid.monitorWindowResize = false; // turn off autosizing
40612     config.grid.autoHeight = false;
40613     config.grid.autoWidth = false;
40614     
40615     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40616     
40617     if (config.background) {
40618         // render grid on panel activation (if panel background)
40619         this.on('activate', function(gp) {
40620             if (!gp.grid.rendered) {
40621                 gp.grid.render(this.wrapper);
40622                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40623             }
40624         });
40625             
40626     } else {
40627         this.grid.render(this.wrapper);
40628         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40629
40630     }
40631     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40632     // ??? needed ??? config.el = this.wrapper;
40633     
40634     
40635     
40636   
40637     // xtype created footer. - not sure if will work as we normally have to render first..
40638     if (this.footer && !this.footer.el && this.footer.xtype) {
40639         
40640         var ctr = this.grid.getView().getFooterPanel(true);
40641         this.footer.dataSource = this.grid.dataSource;
40642         this.footer = Roo.factory(this.footer, Roo);
40643         this.footer.render(ctr);
40644         
40645     }
40646     
40647     
40648     
40649     
40650      
40651 };
40652
40653 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40654     getId : function(){
40655         return this.grid.id;
40656     },
40657     
40658     /**
40659      * Returns the grid for this panel
40660      * @return {Roo.bootstrap.Table} 
40661      */
40662     getGrid : function(){
40663         return this.grid;    
40664     },
40665     
40666     setSize : function(width, height){
40667         if(!this.ignoreResize(width, height)){
40668             var grid = this.grid;
40669             var size = this.adjustForComponents(width, height);
40670             // tfoot is not a footer?
40671           
40672             
40673             var gridel = grid.getGridEl();
40674             gridel.setSize(size.width, size.height);
40675             
40676             var tbd = grid.getGridEl().select('tbody', true).first();
40677             var thd = grid.getGridEl().select('thead',true).first();
40678             var tbf= grid.getGridEl().select('tfoot', true).first();
40679
40680             if (tbf) {
40681                 size.height -= tbf.getHeight();
40682             }
40683             if (thd) {
40684                 size.height -= thd.getHeight();
40685             }
40686             
40687             tbd.setSize(size.width, size.height );
40688             // this is for the account management tab -seems to work there.
40689             var thd = grid.getGridEl().select('thead',true).first();
40690             //if (tbd) {
40691             //    tbd.setSize(size.width, size.height - thd.getHeight());
40692             //}
40693              
40694             grid.autoSize();
40695         }
40696     },
40697      
40698     
40699     
40700     beforeSlide : function(){
40701         this.grid.getView().scroller.clip();
40702     },
40703     
40704     afterSlide : function(){
40705         this.grid.getView().scroller.unclip();
40706     },
40707     
40708     destroy : function(){
40709         this.grid.destroy();
40710         delete this.grid;
40711         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40712     }
40713 });
40714
40715 /**
40716  * @class Roo.bootstrap.panel.Nest
40717  * @extends Roo.bootstrap.panel.Content
40718  * @constructor
40719  * Create a new Panel, that can contain a layout.Border.
40720  * 
40721  * 
40722  * @param {Roo.BorderLayout} layout The layout for this panel
40723  * @param {String/Object} config A string to set only the title or a config object
40724  */
40725 Roo.bootstrap.panel.Nest = function(config)
40726 {
40727     // construct with only one argument..
40728     /* FIXME - implement nicer consturctors
40729     if (layout.layout) {
40730         config = layout;
40731         layout = config.layout;
40732         delete config.layout;
40733     }
40734     if (layout.xtype && !layout.getEl) {
40735         // then layout needs constructing..
40736         layout = Roo.factory(layout, Roo);
40737     }
40738     */
40739     
40740     config.el =  config.layout.getEl();
40741     
40742     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40743     
40744     config.layout.monitorWindowResize = false; // turn off autosizing
40745     this.layout = config.layout;
40746     this.layout.getEl().addClass("roo-layout-nested-layout");
40747     this.layout.parent = this;
40748     
40749     
40750     
40751     
40752 };
40753
40754 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40755
40756     setSize : function(width, height){
40757         if(!this.ignoreResize(width, height)){
40758             var size = this.adjustForComponents(width, height);
40759             var el = this.layout.getEl();
40760             if (size.height < 1) {
40761                 el.setWidth(size.width);   
40762             } else {
40763                 el.setSize(size.width, size.height);
40764             }
40765             var touch = el.dom.offsetWidth;
40766             this.layout.layout();
40767             // ie requires a double layout on the first pass
40768             if(Roo.isIE && !this.initialized){
40769                 this.initialized = true;
40770                 this.layout.layout();
40771             }
40772         }
40773     },
40774     
40775     // activate all subpanels if not currently active..
40776     
40777     setActiveState : function(active){
40778         this.active = active;
40779         this.setActiveClass(active);
40780         
40781         if(!active){
40782             this.fireEvent("deactivate", this);
40783             return;
40784         }
40785         
40786         this.fireEvent("activate", this);
40787         // not sure if this should happen before or after..
40788         if (!this.layout) {
40789             return; // should not happen..
40790         }
40791         var reg = false;
40792         for (var r in this.layout.regions) {
40793             reg = this.layout.getRegion(r);
40794             if (reg.getActivePanel()) {
40795                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40796                 reg.setActivePanel(reg.getActivePanel());
40797                 continue;
40798             }
40799             if (!reg.panels.length) {
40800                 continue;
40801             }
40802             reg.showPanel(reg.getPanel(0));
40803         }
40804         
40805         
40806         
40807         
40808     },
40809     
40810     /**
40811      * Returns the nested BorderLayout for this panel
40812      * @return {Roo.BorderLayout} 
40813      */
40814     getLayout : function(){
40815         return this.layout;
40816     },
40817     
40818      /**
40819      * Adds a xtype elements to the layout of the nested panel
40820      * <pre><code>
40821
40822 panel.addxtype({
40823        xtype : 'ContentPanel',
40824        region: 'west',
40825        items: [ .... ]
40826    }
40827 );
40828
40829 panel.addxtype({
40830         xtype : 'NestedLayoutPanel',
40831         region: 'west',
40832         layout: {
40833            center: { },
40834            west: { }   
40835         },
40836         items : [ ... list of content panels or nested layout panels.. ]
40837    }
40838 );
40839 </code></pre>
40840      * @param {Object} cfg Xtype definition of item to add.
40841      */
40842     addxtype : function(cfg) {
40843         return this.layout.addxtype(cfg);
40844     
40845     }
40846 });/*
40847  * Based on:
40848  * Ext JS Library 1.1.1
40849  * Copyright(c) 2006-2007, Ext JS, LLC.
40850  *
40851  * Originally Released Under LGPL - original licence link has changed is not relivant.
40852  *
40853  * Fork - LGPL
40854  * <script type="text/javascript">
40855  */
40856 /**
40857  * @class Roo.TabPanel
40858  * @extends Roo.util.Observable
40859  * A lightweight tab container.
40860  * <br><br>
40861  * Usage:
40862  * <pre><code>
40863 // basic tabs 1, built from existing content
40864 var tabs = new Roo.TabPanel("tabs1");
40865 tabs.addTab("script", "View Script");
40866 tabs.addTab("markup", "View Markup");
40867 tabs.activate("script");
40868
40869 // more advanced tabs, built from javascript
40870 var jtabs = new Roo.TabPanel("jtabs");
40871 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40872
40873 // set up the UpdateManager
40874 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40875 var updater = tab2.getUpdateManager();
40876 updater.setDefaultUrl("ajax1.htm");
40877 tab2.on('activate', updater.refresh, updater, true);
40878
40879 // Use setUrl for Ajax loading
40880 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40881 tab3.setUrl("ajax2.htm", null, true);
40882
40883 // Disabled tab
40884 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40885 tab4.disable();
40886
40887 jtabs.activate("jtabs-1");
40888  * </code></pre>
40889  * @constructor
40890  * Create a new TabPanel.
40891  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40892  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40893  */
40894 Roo.bootstrap.panel.Tabs = function(config){
40895     /**
40896     * The container element for this TabPanel.
40897     * @type Roo.Element
40898     */
40899     this.el = Roo.get(config.el);
40900     delete config.el;
40901     if(config){
40902         if(typeof config == "boolean"){
40903             this.tabPosition = config ? "bottom" : "top";
40904         }else{
40905             Roo.apply(this, config);
40906         }
40907     }
40908     
40909     if(this.tabPosition == "bottom"){
40910         // if tabs are at the bottom = create the body first.
40911         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40912         this.el.addClass("roo-tabs-bottom");
40913     }
40914     // next create the tabs holders
40915     
40916     if (this.tabPosition == "west"){
40917         
40918         var reg = this.region; // fake it..
40919         while (reg) {
40920             if (!reg.mgr.parent) {
40921                 break;
40922             }
40923             reg = reg.mgr.parent.region;
40924         }
40925         Roo.log("got nest?");
40926         Roo.log(reg);
40927         if (reg.mgr.getRegion('west')) {
40928             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40929             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40930             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40931             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40932             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40933         
40934             
40935         }
40936         
40937         
40938     } else {
40939      
40940         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40941         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40942         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40943         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40944     }
40945     
40946     
40947     if(Roo.isIE){
40948         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40949     }
40950     
40951     // finally - if tabs are at the top, then create the body last..
40952     if(this.tabPosition != "bottom"){
40953         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40954          * @type Roo.Element
40955          */
40956         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40957         this.el.addClass("roo-tabs-top");
40958     }
40959     this.items = [];
40960
40961     this.bodyEl.setStyle("position", "relative");
40962
40963     this.active = null;
40964     this.activateDelegate = this.activate.createDelegate(this);
40965
40966     this.addEvents({
40967         /**
40968          * @event tabchange
40969          * Fires when the active tab changes
40970          * @param {Roo.TabPanel} this
40971          * @param {Roo.TabPanelItem} activePanel The new active tab
40972          */
40973         "tabchange": true,
40974         /**
40975          * @event beforetabchange
40976          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40977          * @param {Roo.TabPanel} this
40978          * @param {Object} e Set cancel to true on this object to cancel the tab change
40979          * @param {Roo.TabPanelItem} tab The tab being changed to
40980          */
40981         "beforetabchange" : true
40982     });
40983
40984     Roo.EventManager.onWindowResize(this.onResize, this);
40985     this.cpad = this.el.getPadding("lr");
40986     this.hiddenCount = 0;
40987
40988
40989     // toolbar on the tabbar support...
40990     if (this.toolbar) {
40991         alert("no toolbar support yet");
40992         this.toolbar  = false;
40993         /*
40994         var tcfg = this.toolbar;
40995         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40996         this.toolbar = new Roo.Toolbar(tcfg);
40997         if (Roo.isSafari) {
40998             var tbl = tcfg.container.child('table', true);
40999             tbl.setAttribute('width', '100%');
41000         }
41001         */
41002         
41003     }
41004    
41005
41006
41007     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41008 };
41009
41010 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41011     /*
41012      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41013      */
41014     tabPosition : "top",
41015     /*
41016      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41017      */
41018     currentTabWidth : 0,
41019     /*
41020      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41021      */
41022     minTabWidth : 40,
41023     /*
41024      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41025      */
41026     maxTabWidth : 250,
41027     /*
41028      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41029      */
41030     preferredTabWidth : 175,
41031     /*
41032      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41033      */
41034     resizeTabs : false,
41035     /*
41036      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41037      */
41038     monitorResize : true,
41039     /*
41040      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41041      */
41042     toolbar : false,  // set by caller..
41043     
41044     region : false, /// set by caller
41045     
41046     disableTooltips : true, // not used yet...
41047
41048     /**
41049      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41050      * @param {String} id The id of the div to use <b>or create</b>
41051      * @param {String} text The text for the tab
41052      * @param {String} content (optional) Content to put in the TabPanelItem body
41053      * @param {Boolean} closable (optional) True to create a close icon on the tab
41054      * @return {Roo.TabPanelItem} The created TabPanelItem
41055      */
41056     addTab : function(id, text, content, closable, tpl)
41057     {
41058         var item = new Roo.bootstrap.panel.TabItem({
41059             panel: this,
41060             id : id,
41061             text : text,
41062             closable : closable,
41063             tpl : tpl
41064         });
41065         this.addTabItem(item);
41066         if(content){
41067             item.setContent(content);
41068         }
41069         return item;
41070     },
41071
41072     /**
41073      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41074      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41075      * @return {Roo.TabPanelItem}
41076      */
41077     getTab : function(id){
41078         return this.items[id];
41079     },
41080
41081     /**
41082      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41083      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41084      */
41085     hideTab : function(id){
41086         var t = this.items[id];
41087         if(!t.isHidden()){
41088            t.setHidden(true);
41089            this.hiddenCount++;
41090            this.autoSizeTabs();
41091         }
41092     },
41093
41094     /**
41095      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41096      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41097      */
41098     unhideTab : function(id){
41099         var t = this.items[id];
41100         if(t.isHidden()){
41101            t.setHidden(false);
41102            this.hiddenCount--;
41103            this.autoSizeTabs();
41104         }
41105     },
41106
41107     /**
41108      * Adds an existing {@link Roo.TabPanelItem}.
41109      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41110      */
41111     addTabItem : function(item)
41112     {
41113         this.items[item.id] = item;
41114         this.items.push(item);
41115         this.autoSizeTabs();
41116       //  if(this.resizeTabs){
41117     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41118   //         this.autoSizeTabs();
41119 //        }else{
41120 //            item.autoSize();
41121        // }
41122     },
41123
41124     /**
41125      * Removes a {@link Roo.TabPanelItem}.
41126      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41127      */
41128     removeTab : function(id){
41129         var items = this.items;
41130         var tab = items[id];
41131         if(!tab) { return; }
41132         var index = items.indexOf(tab);
41133         if(this.active == tab && items.length > 1){
41134             var newTab = this.getNextAvailable(index);
41135             if(newTab) {
41136                 newTab.activate();
41137             }
41138         }
41139         this.stripEl.dom.removeChild(tab.pnode.dom);
41140         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41141             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41142         }
41143         items.splice(index, 1);
41144         delete this.items[tab.id];
41145         tab.fireEvent("close", tab);
41146         tab.purgeListeners();
41147         this.autoSizeTabs();
41148     },
41149
41150     getNextAvailable : function(start){
41151         var items = this.items;
41152         var index = start;
41153         // look for a next tab that will slide over to
41154         // replace the one being removed
41155         while(index < items.length){
41156             var item = items[++index];
41157             if(item && !item.isHidden()){
41158                 return item;
41159             }
41160         }
41161         // if one isn't found select the previous tab (on the left)
41162         index = start;
41163         while(index >= 0){
41164             var item = items[--index];
41165             if(item && !item.isHidden()){
41166                 return item;
41167             }
41168         }
41169         return null;
41170     },
41171
41172     /**
41173      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41174      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41175      */
41176     disableTab : function(id){
41177         var tab = this.items[id];
41178         if(tab && this.active != tab){
41179             tab.disable();
41180         }
41181     },
41182
41183     /**
41184      * Enables a {@link Roo.TabPanelItem} that is disabled.
41185      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41186      */
41187     enableTab : function(id){
41188         var tab = this.items[id];
41189         tab.enable();
41190     },
41191
41192     /**
41193      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41194      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41195      * @return {Roo.TabPanelItem} The TabPanelItem.
41196      */
41197     activate : function(id)
41198     {
41199         //Roo.log('activite:'  + id);
41200         
41201         var tab = this.items[id];
41202         if(!tab){
41203             return null;
41204         }
41205         if(tab == this.active || tab.disabled){
41206             return tab;
41207         }
41208         var e = {};
41209         this.fireEvent("beforetabchange", this, e, tab);
41210         if(e.cancel !== true && !tab.disabled){
41211             if(this.active){
41212                 this.active.hide();
41213             }
41214             this.active = this.items[id];
41215             this.active.show();
41216             this.fireEvent("tabchange", this, this.active);
41217         }
41218         return tab;
41219     },
41220
41221     /**
41222      * Gets the active {@link Roo.TabPanelItem}.
41223      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41224      */
41225     getActiveTab : function(){
41226         return this.active;
41227     },
41228
41229     /**
41230      * Updates the tab body element to fit the height of the container element
41231      * for overflow scrolling
41232      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41233      */
41234     syncHeight : function(targetHeight){
41235         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41236         var bm = this.bodyEl.getMargins();
41237         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41238         this.bodyEl.setHeight(newHeight);
41239         return newHeight;
41240     },
41241
41242     onResize : function(){
41243         if(this.monitorResize){
41244             this.autoSizeTabs();
41245         }
41246     },
41247
41248     /**
41249      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41250      */
41251     beginUpdate : function(){
41252         this.updating = true;
41253     },
41254
41255     /**
41256      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41257      */
41258     endUpdate : function(){
41259         this.updating = false;
41260         this.autoSizeTabs();
41261     },
41262
41263     /**
41264      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41265      */
41266     autoSizeTabs : function()
41267     {
41268         var count = this.items.length;
41269         var vcount = count - this.hiddenCount;
41270         
41271         if (vcount < 2) {
41272             this.stripEl.hide();
41273         } else {
41274             this.stripEl.show();
41275         }
41276         
41277         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41278             return;
41279         }
41280         
41281         
41282         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41283         var availWidth = Math.floor(w / vcount);
41284         var b = this.stripBody;
41285         if(b.getWidth() > w){
41286             var tabs = this.items;
41287             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41288             if(availWidth < this.minTabWidth){
41289                 /*if(!this.sleft){    // incomplete scrolling code
41290                     this.createScrollButtons();
41291                 }
41292                 this.showScroll();
41293                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41294             }
41295         }else{
41296             if(this.currentTabWidth < this.preferredTabWidth){
41297                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41298             }
41299         }
41300     },
41301
41302     /**
41303      * Returns the number of tabs in this TabPanel.
41304      * @return {Number}
41305      */
41306      getCount : function(){
41307          return this.items.length;
41308      },
41309
41310     /**
41311      * Resizes all the tabs to the passed width
41312      * @param {Number} The new width
41313      */
41314     setTabWidth : function(width){
41315         this.currentTabWidth = width;
41316         for(var i = 0, len = this.items.length; i < len; i++) {
41317                 if(!this.items[i].isHidden()) {
41318                 this.items[i].setWidth(width);
41319             }
41320         }
41321     },
41322
41323     /**
41324      * Destroys this TabPanel
41325      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41326      */
41327     destroy : function(removeEl){
41328         Roo.EventManager.removeResizeListener(this.onResize, this);
41329         for(var i = 0, len = this.items.length; i < len; i++){
41330             this.items[i].purgeListeners();
41331         }
41332         if(removeEl === true){
41333             this.el.update("");
41334             this.el.remove();
41335         }
41336     },
41337     
41338     createStrip : function(container)
41339     {
41340         var strip = document.createElement("nav");
41341         strip.className = Roo.bootstrap.version == 4 ?
41342             "navbar-light bg-light" : 
41343             "navbar navbar-default"; //"x-tabs-wrap";
41344         container.appendChild(strip);
41345         return strip;
41346     },
41347     
41348     createStripList : function(strip)
41349     {
41350         // div wrapper for retard IE
41351         // returns the "tr" element.
41352         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41353         //'<div class="x-tabs-strip-wrap">'+
41354           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41355           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41356         return strip.firstChild; //.firstChild.firstChild.firstChild;
41357     },
41358     createBody : function(container)
41359     {
41360         var body = document.createElement("div");
41361         Roo.id(body, "tab-body");
41362         //Roo.fly(body).addClass("x-tabs-body");
41363         Roo.fly(body).addClass("tab-content");
41364         container.appendChild(body);
41365         return body;
41366     },
41367     createItemBody :function(bodyEl, id){
41368         var body = Roo.getDom(id);
41369         if(!body){
41370             body = document.createElement("div");
41371             body.id = id;
41372         }
41373         //Roo.fly(body).addClass("x-tabs-item-body");
41374         Roo.fly(body).addClass("tab-pane");
41375          bodyEl.insertBefore(body, bodyEl.firstChild);
41376         return body;
41377     },
41378     /** @private */
41379     createStripElements :  function(stripEl, text, closable, tpl)
41380     {
41381         var td = document.createElement("li"); // was td..
41382         td.className = 'nav-item';
41383         
41384         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41385         
41386         
41387         stripEl.appendChild(td);
41388         /*if(closable){
41389             td.className = "x-tabs-closable";
41390             if(!this.closeTpl){
41391                 this.closeTpl = new Roo.Template(
41392                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41393                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41394                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41395                 );
41396             }
41397             var el = this.closeTpl.overwrite(td, {"text": text});
41398             var close = el.getElementsByTagName("div")[0];
41399             var inner = el.getElementsByTagName("em")[0];
41400             return {"el": el, "close": close, "inner": inner};
41401         } else {
41402         */
41403         // not sure what this is..
41404 //            if(!this.tabTpl){
41405                 //this.tabTpl = new Roo.Template(
41406                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41407                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41408                 //);
41409 //                this.tabTpl = new Roo.Template(
41410 //                   '<a href="#">' +
41411 //                   '<span unselectable="on"' +
41412 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41413 //                            ' >{text}</span></a>'
41414 //                );
41415 //                
41416 //            }
41417
41418
41419             var template = tpl || this.tabTpl || false;
41420             
41421             if(!template){
41422                 template =  new Roo.Template(
41423                         Roo.bootstrap.version == 4 ? 
41424                             (
41425                                 '<a class="nav-link" href="#" unselectable="on"' +
41426                                      (this.disableTooltips ? '' : ' title="{text}"') +
41427                                      ' >{text}</a>'
41428                             ) : (
41429                                 '<a class="nav-link" href="#">' +
41430                                 '<span unselectable="on"' +
41431                                          (this.disableTooltips ? '' : ' title="{text}"') +
41432                                     ' >{text}</span></a>'
41433                             )
41434                 );
41435             }
41436             
41437             switch (typeof(template)) {
41438                 case 'object' :
41439                     break;
41440                 case 'string' :
41441                     template = new Roo.Template(template);
41442                     break;
41443                 default :
41444                     break;
41445             }
41446             
41447             var el = template.overwrite(td, {"text": text});
41448             
41449             var inner = el.getElementsByTagName("span")[0];
41450             
41451             return {"el": el, "inner": inner};
41452             
41453     }
41454         
41455     
41456 });
41457
41458 /**
41459  * @class Roo.TabPanelItem
41460  * @extends Roo.util.Observable
41461  * Represents an individual item (tab plus body) in a TabPanel.
41462  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41463  * @param {String} id The id of this TabPanelItem
41464  * @param {String} text The text for the tab of this TabPanelItem
41465  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41466  */
41467 Roo.bootstrap.panel.TabItem = function(config){
41468     /**
41469      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41470      * @type Roo.TabPanel
41471      */
41472     this.tabPanel = config.panel;
41473     /**
41474      * The id for this TabPanelItem
41475      * @type String
41476      */
41477     this.id = config.id;
41478     /** @private */
41479     this.disabled = false;
41480     /** @private */
41481     this.text = config.text;
41482     /** @private */
41483     this.loaded = false;
41484     this.closable = config.closable;
41485
41486     /**
41487      * The body element for this TabPanelItem.
41488      * @type Roo.Element
41489      */
41490     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41491     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41492     this.bodyEl.setStyle("display", "block");
41493     this.bodyEl.setStyle("zoom", "1");
41494     //this.hideAction();
41495
41496     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41497     /** @private */
41498     this.el = Roo.get(els.el);
41499     this.inner = Roo.get(els.inner, true);
41500      this.textEl = Roo.bootstrap.version == 4 ?
41501         this.el : Roo.get(this.el.dom.firstChild, true);
41502
41503     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41504     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41505
41506     
41507 //    this.el.on("mousedown", this.onTabMouseDown, this);
41508     this.el.on("click", this.onTabClick, this);
41509     /** @private */
41510     if(config.closable){
41511         var c = Roo.get(els.close, true);
41512         c.dom.title = this.closeText;
41513         c.addClassOnOver("close-over");
41514         c.on("click", this.closeClick, this);
41515      }
41516
41517     this.addEvents({
41518          /**
41519          * @event activate
41520          * Fires when this tab becomes the active tab.
41521          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41522          * @param {Roo.TabPanelItem} this
41523          */
41524         "activate": true,
41525         /**
41526          * @event beforeclose
41527          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41528          * @param {Roo.TabPanelItem} this
41529          * @param {Object} e Set cancel to true on this object to cancel the close.
41530          */
41531         "beforeclose": true,
41532         /**
41533          * @event close
41534          * Fires when this tab is closed.
41535          * @param {Roo.TabPanelItem} this
41536          */
41537          "close": true,
41538         /**
41539          * @event deactivate
41540          * Fires when this tab is no longer the active tab.
41541          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41542          * @param {Roo.TabPanelItem} this
41543          */
41544          "deactivate" : true
41545     });
41546     this.hidden = false;
41547
41548     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41549 };
41550
41551 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41552            {
41553     purgeListeners : function(){
41554        Roo.util.Observable.prototype.purgeListeners.call(this);
41555        this.el.removeAllListeners();
41556     },
41557     /**
41558      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41559      */
41560     show : function(){
41561         this.status_node.addClass("active");
41562         this.showAction();
41563         if(Roo.isOpera){
41564             this.tabPanel.stripWrap.repaint();
41565         }
41566         this.fireEvent("activate", this.tabPanel, this);
41567     },
41568
41569     /**
41570      * Returns true if this tab is the active tab.
41571      * @return {Boolean}
41572      */
41573     isActive : function(){
41574         return this.tabPanel.getActiveTab() == this;
41575     },
41576
41577     /**
41578      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41579      */
41580     hide : function(){
41581         this.status_node.removeClass("active");
41582         this.hideAction();
41583         this.fireEvent("deactivate", this.tabPanel, this);
41584     },
41585
41586     hideAction : function(){
41587         this.bodyEl.hide();
41588         this.bodyEl.setStyle("position", "absolute");
41589         this.bodyEl.setLeft("-20000px");
41590         this.bodyEl.setTop("-20000px");
41591     },
41592
41593     showAction : function(){
41594         this.bodyEl.setStyle("position", "relative");
41595         this.bodyEl.setTop("");
41596         this.bodyEl.setLeft("");
41597         this.bodyEl.show();
41598     },
41599
41600     /**
41601      * Set the tooltip for the tab.
41602      * @param {String} tooltip The tab's tooltip
41603      */
41604     setTooltip : function(text){
41605         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41606             this.textEl.dom.qtip = text;
41607             this.textEl.dom.removeAttribute('title');
41608         }else{
41609             this.textEl.dom.title = text;
41610         }
41611     },
41612
41613     onTabClick : function(e){
41614         e.preventDefault();
41615         this.tabPanel.activate(this.id);
41616     },
41617
41618     onTabMouseDown : function(e){
41619         e.preventDefault();
41620         this.tabPanel.activate(this.id);
41621     },
41622 /*
41623     getWidth : function(){
41624         return this.inner.getWidth();
41625     },
41626
41627     setWidth : function(width){
41628         var iwidth = width - this.linode.getPadding("lr");
41629         this.inner.setWidth(iwidth);
41630         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41631         this.linode.setWidth(width);
41632     },
41633 */
41634     /**
41635      * Show or hide the tab
41636      * @param {Boolean} hidden True to hide or false to show.
41637      */
41638     setHidden : function(hidden){
41639         this.hidden = hidden;
41640         this.linode.setStyle("display", hidden ? "none" : "");
41641     },
41642
41643     /**
41644      * Returns true if this tab is "hidden"
41645      * @return {Boolean}
41646      */
41647     isHidden : function(){
41648         return this.hidden;
41649     },
41650
41651     /**
41652      * Returns the text for this tab
41653      * @return {String}
41654      */
41655     getText : function(){
41656         return this.text;
41657     },
41658     /*
41659     autoSize : function(){
41660         //this.el.beginMeasure();
41661         this.textEl.setWidth(1);
41662         /*
41663          *  #2804 [new] Tabs in Roojs
41664          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41665          */
41666         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41667         //this.el.endMeasure();
41668     //},
41669
41670     /**
41671      * Sets the text for the tab (Note: this also sets the tooltip text)
41672      * @param {String} text The tab's text and tooltip
41673      */
41674     setText : function(text){
41675         this.text = text;
41676         this.textEl.update(text);
41677         this.setTooltip(text);
41678         //if(!this.tabPanel.resizeTabs){
41679         //    this.autoSize();
41680         //}
41681     },
41682     /**
41683      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41684      */
41685     activate : function(){
41686         this.tabPanel.activate(this.id);
41687     },
41688
41689     /**
41690      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41691      */
41692     disable : function(){
41693         if(this.tabPanel.active != this){
41694             this.disabled = true;
41695             this.status_node.addClass("disabled");
41696         }
41697     },
41698
41699     /**
41700      * Enables this TabPanelItem if it was previously disabled.
41701      */
41702     enable : function(){
41703         this.disabled = false;
41704         this.status_node.removeClass("disabled");
41705     },
41706
41707     /**
41708      * Sets the content for this TabPanelItem.
41709      * @param {String} content The content
41710      * @param {Boolean} loadScripts true to look for and load scripts
41711      */
41712     setContent : function(content, loadScripts){
41713         this.bodyEl.update(content, loadScripts);
41714     },
41715
41716     /**
41717      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41718      * @return {Roo.UpdateManager} The UpdateManager
41719      */
41720     getUpdateManager : function(){
41721         return this.bodyEl.getUpdateManager();
41722     },
41723
41724     /**
41725      * Set a URL to be used to load the content for this TabPanelItem.
41726      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41727      * @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)
41728      * @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)
41729      * @return {Roo.UpdateManager} The UpdateManager
41730      */
41731     setUrl : function(url, params, loadOnce){
41732         if(this.refreshDelegate){
41733             this.un('activate', this.refreshDelegate);
41734         }
41735         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41736         this.on("activate", this.refreshDelegate);
41737         return this.bodyEl.getUpdateManager();
41738     },
41739
41740     /** @private */
41741     _handleRefresh : function(url, params, loadOnce){
41742         if(!loadOnce || !this.loaded){
41743             var updater = this.bodyEl.getUpdateManager();
41744             updater.update(url, params, this._setLoaded.createDelegate(this));
41745         }
41746     },
41747
41748     /**
41749      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41750      *   Will fail silently if the setUrl method has not been called.
41751      *   This does not activate the panel, just updates its content.
41752      */
41753     refresh : function(){
41754         if(this.refreshDelegate){
41755            this.loaded = false;
41756            this.refreshDelegate();
41757         }
41758     },
41759
41760     /** @private */
41761     _setLoaded : function(){
41762         this.loaded = true;
41763     },
41764
41765     /** @private */
41766     closeClick : function(e){
41767         var o = {};
41768         e.stopEvent();
41769         this.fireEvent("beforeclose", this, o);
41770         if(o.cancel !== true){
41771             this.tabPanel.removeTab(this.id);
41772         }
41773     },
41774     /**
41775      * The text displayed in the tooltip for the close icon.
41776      * @type String
41777      */
41778     closeText : "Close this tab"
41779 });
41780 /**
41781 *    This script refer to:
41782 *    Title: International Telephone Input
41783 *    Author: Jack O'Connor
41784 *    Code version:  v12.1.12
41785 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41786 **/
41787
41788 Roo.bootstrap.PhoneInputData = function() {
41789     var d = [
41790       [
41791         "Afghanistan (‫افغانستان‬‎)",
41792         "af",
41793         "93"
41794       ],
41795       [
41796         "Albania (Shqipëri)",
41797         "al",
41798         "355"
41799       ],
41800       [
41801         "Algeria (‫الجزائر‬‎)",
41802         "dz",
41803         "213"
41804       ],
41805       [
41806         "American Samoa",
41807         "as",
41808         "1684"
41809       ],
41810       [
41811         "Andorra",
41812         "ad",
41813         "376"
41814       ],
41815       [
41816         "Angola",
41817         "ao",
41818         "244"
41819       ],
41820       [
41821         "Anguilla",
41822         "ai",
41823         "1264"
41824       ],
41825       [
41826         "Antigua and Barbuda",
41827         "ag",
41828         "1268"
41829       ],
41830       [
41831         "Argentina",
41832         "ar",
41833         "54"
41834       ],
41835       [
41836         "Armenia (Հայաստան)",
41837         "am",
41838         "374"
41839       ],
41840       [
41841         "Aruba",
41842         "aw",
41843         "297"
41844       ],
41845       [
41846         "Australia",
41847         "au",
41848         "61",
41849         0
41850       ],
41851       [
41852         "Austria (Österreich)",
41853         "at",
41854         "43"
41855       ],
41856       [
41857         "Azerbaijan (Azərbaycan)",
41858         "az",
41859         "994"
41860       ],
41861       [
41862         "Bahamas",
41863         "bs",
41864         "1242"
41865       ],
41866       [
41867         "Bahrain (‫البحرين‬‎)",
41868         "bh",
41869         "973"
41870       ],
41871       [
41872         "Bangladesh (বাংলাদেশ)",
41873         "bd",
41874         "880"
41875       ],
41876       [
41877         "Barbados",
41878         "bb",
41879         "1246"
41880       ],
41881       [
41882         "Belarus (Беларусь)",
41883         "by",
41884         "375"
41885       ],
41886       [
41887         "Belgium (België)",
41888         "be",
41889         "32"
41890       ],
41891       [
41892         "Belize",
41893         "bz",
41894         "501"
41895       ],
41896       [
41897         "Benin (Bénin)",
41898         "bj",
41899         "229"
41900       ],
41901       [
41902         "Bermuda",
41903         "bm",
41904         "1441"
41905       ],
41906       [
41907         "Bhutan (འབྲུག)",
41908         "bt",
41909         "975"
41910       ],
41911       [
41912         "Bolivia",
41913         "bo",
41914         "591"
41915       ],
41916       [
41917         "Bosnia and Herzegovina (Босна и Херцеговина)",
41918         "ba",
41919         "387"
41920       ],
41921       [
41922         "Botswana",
41923         "bw",
41924         "267"
41925       ],
41926       [
41927         "Brazil (Brasil)",
41928         "br",
41929         "55"
41930       ],
41931       [
41932         "British Indian Ocean Territory",
41933         "io",
41934         "246"
41935       ],
41936       [
41937         "British Virgin Islands",
41938         "vg",
41939         "1284"
41940       ],
41941       [
41942         "Brunei",
41943         "bn",
41944         "673"
41945       ],
41946       [
41947         "Bulgaria (България)",
41948         "bg",
41949         "359"
41950       ],
41951       [
41952         "Burkina Faso",
41953         "bf",
41954         "226"
41955       ],
41956       [
41957         "Burundi (Uburundi)",
41958         "bi",
41959         "257"
41960       ],
41961       [
41962         "Cambodia (កម្ពុជា)",
41963         "kh",
41964         "855"
41965       ],
41966       [
41967         "Cameroon (Cameroun)",
41968         "cm",
41969         "237"
41970       ],
41971       [
41972         "Canada",
41973         "ca",
41974         "1",
41975         1,
41976         ["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"]
41977       ],
41978       [
41979         "Cape Verde (Kabu Verdi)",
41980         "cv",
41981         "238"
41982       ],
41983       [
41984         "Caribbean Netherlands",
41985         "bq",
41986         "599",
41987         1
41988       ],
41989       [
41990         "Cayman Islands",
41991         "ky",
41992         "1345"
41993       ],
41994       [
41995         "Central African Republic (République centrafricaine)",
41996         "cf",
41997         "236"
41998       ],
41999       [
42000         "Chad (Tchad)",
42001         "td",
42002         "235"
42003       ],
42004       [
42005         "Chile",
42006         "cl",
42007         "56"
42008       ],
42009       [
42010         "China (中国)",
42011         "cn",
42012         "86"
42013       ],
42014       [
42015         "Christmas Island",
42016         "cx",
42017         "61",
42018         2
42019       ],
42020       [
42021         "Cocos (Keeling) Islands",
42022         "cc",
42023         "61",
42024         1
42025       ],
42026       [
42027         "Colombia",
42028         "co",
42029         "57"
42030       ],
42031       [
42032         "Comoros (‫جزر القمر‬‎)",
42033         "km",
42034         "269"
42035       ],
42036       [
42037         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42038         "cd",
42039         "243"
42040       ],
42041       [
42042         "Congo (Republic) (Congo-Brazzaville)",
42043         "cg",
42044         "242"
42045       ],
42046       [
42047         "Cook Islands",
42048         "ck",
42049         "682"
42050       ],
42051       [
42052         "Costa Rica",
42053         "cr",
42054         "506"
42055       ],
42056       [
42057         "Côte d’Ivoire",
42058         "ci",
42059         "225"
42060       ],
42061       [
42062         "Croatia (Hrvatska)",
42063         "hr",
42064         "385"
42065       ],
42066       [
42067         "Cuba",
42068         "cu",
42069         "53"
42070       ],
42071       [
42072         "Curaçao",
42073         "cw",
42074         "599",
42075         0
42076       ],
42077       [
42078         "Cyprus (Κύπρος)",
42079         "cy",
42080         "357"
42081       ],
42082       [
42083         "Czech Republic (Česká republika)",
42084         "cz",
42085         "420"
42086       ],
42087       [
42088         "Denmark (Danmark)",
42089         "dk",
42090         "45"
42091       ],
42092       [
42093         "Djibouti",
42094         "dj",
42095         "253"
42096       ],
42097       [
42098         "Dominica",
42099         "dm",
42100         "1767"
42101       ],
42102       [
42103         "Dominican Republic (República Dominicana)",
42104         "do",
42105         "1",
42106         2,
42107         ["809", "829", "849"]
42108       ],
42109       [
42110         "Ecuador",
42111         "ec",
42112         "593"
42113       ],
42114       [
42115         "Egypt (‫مصر‬‎)",
42116         "eg",
42117         "20"
42118       ],
42119       [
42120         "El Salvador",
42121         "sv",
42122         "503"
42123       ],
42124       [
42125         "Equatorial Guinea (Guinea Ecuatorial)",
42126         "gq",
42127         "240"
42128       ],
42129       [
42130         "Eritrea",
42131         "er",
42132         "291"
42133       ],
42134       [
42135         "Estonia (Eesti)",
42136         "ee",
42137         "372"
42138       ],
42139       [
42140         "Ethiopia",
42141         "et",
42142         "251"
42143       ],
42144       [
42145         "Falkland Islands (Islas Malvinas)",
42146         "fk",
42147         "500"
42148       ],
42149       [
42150         "Faroe Islands (Føroyar)",
42151         "fo",
42152         "298"
42153       ],
42154       [
42155         "Fiji",
42156         "fj",
42157         "679"
42158       ],
42159       [
42160         "Finland (Suomi)",
42161         "fi",
42162         "358",
42163         0
42164       ],
42165       [
42166         "France",
42167         "fr",
42168         "33"
42169       ],
42170       [
42171         "French Guiana (Guyane française)",
42172         "gf",
42173         "594"
42174       ],
42175       [
42176         "French Polynesia (Polynésie française)",
42177         "pf",
42178         "689"
42179       ],
42180       [
42181         "Gabon",
42182         "ga",
42183         "241"
42184       ],
42185       [
42186         "Gambia",
42187         "gm",
42188         "220"
42189       ],
42190       [
42191         "Georgia (საქართველო)",
42192         "ge",
42193         "995"
42194       ],
42195       [
42196         "Germany (Deutschland)",
42197         "de",
42198         "49"
42199       ],
42200       [
42201         "Ghana (Gaana)",
42202         "gh",
42203         "233"
42204       ],
42205       [
42206         "Gibraltar",
42207         "gi",
42208         "350"
42209       ],
42210       [
42211         "Greece (Ελλάδα)",
42212         "gr",
42213         "30"
42214       ],
42215       [
42216         "Greenland (Kalaallit Nunaat)",
42217         "gl",
42218         "299"
42219       ],
42220       [
42221         "Grenada",
42222         "gd",
42223         "1473"
42224       ],
42225       [
42226         "Guadeloupe",
42227         "gp",
42228         "590",
42229         0
42230       ],
42231       [
42232         "Guam",
42233         "gu",
42234         "1671"
42235       ],
42236       [
42237         "Guatemala",
42238         "gt",
42239         "502"
42240       ],
42241       [
42242         "Guernsey",
42243         "gg",
42244         "44",
42245         1
42246       ],
42247       [
42248         "Guinea (Guinée)",
42249         "gn",
42250         "224"
42251       ],
42252       [
42253         "Guinea-Bissau (Guiné Bissau)",
42254         "gw",
42255         "245"
42256       ],
42257       [
42258         "Guyana",
42259         "gy",
42260         "592"
42261       ],
42262       [
42263         "Haiti",
42264         "ht",
42265         "509"
42266       ],
42267       [
42268         "Honduras",
42269         "hn",
42270         "504"
42271       ],
42272       [
42273         "Hong Kong (香港)",
42274         "hk",
42275         "852"
42276       ],
42277       [
42278         "Hungary (Magyarország)",
42279         "hu",
42280         "36"
42281       ],
42282       [
42283         "Iceland (Ísland)",
42284         "is",
42285         "354"
42286       ],
42287       [
42288         "India (भारत)",
42289         "in",
42290         "91"
42291       ],
42292       [
42293         "Indonesia",
42294         "id",
42295         "62"
42296       ],
42297       [
42298         "Iran (‫ایران‬‎)",
42299         "ir",
42300         "98"
42301       ],
42302       [
42303         "Iraq (‫العراق‬‎)",
42304         "iq",
42305         "964"
42306       ],
42307       [
42308         "Ireland",
42309         "ie",
42310         "353"
42311       ],
42312       [
42313         "Isle of Man",
42314         "im",
42315         "44",
42316         2
42317       ],
42318       [
42319         "Israel (‫ישראל‬‎)",
42320         "il",
42321         "972"
42322       ],
42323       [
42324         "Italy (Italia)",
42325         "it",
42326         "39",
42327         0
42328       ],
42329       [
42330         "Jamaica",
42331         "jm",
42332         "1876"
42333       ],
42334       [
42335         "Japan (日本)",
42336         "jp",
42337         "81"
42338       ],
42339       [
42340         "Jersey",
42341         "je",
42342         "44",
42343         3
42344       ],
42345       [
42346         "Jordan (‫الأردن‬‎)",
42347         "jo",
42348         "962"
42349       ],
42350       [
42351         "Kazakhstan (Казахстан)",
42352         "kz",
42353         "7",
42354         1
42355       ],
42356       [
42357         "Kenya",
42358         "ke",
42359         "254"
42360       ],
42361       [
42362         "Kiribati",
42363         "ki",
42364         "686"
42365       ],
42366       [
42367         "Kosovo",
42368         "xk",
42369         "383"
42370       ],
42371       [
42372         "Kuwait (‫الكويت‬‎)",
42373         "kw",
42374         "965"
42375       ],
42376       [
42377         "Kyrgyzstan (Кыргызстан)",
42378         "kg",
42379         "996"
42380       ],
42381       [
42382         "Laos (ລາວ)",
42383         "la",
42384         "856"
42385       ],
42386       [
42387         "Latvia (Latvija)",
42388         "lv",
42389         "371"
42390       ],
42391       [
42392         "Lebanon (‫لبنان‬‎)",
42393         "lb",
42394         "961"
42395       ],
42396       [
42397         "Lesotho",
42398         "ls",
42399         "266"
42400       ],
42401       [
42402         "Liberia",
42403         "lr",
42404         "231"
42405       ],
42406       [
42407         "Libya (‫ليبيا‬‎)",
42408         "ly",
42409         "218"
42410       ],
42411       [
42412         "Liechtenstein",
42413         "li",
42414         "423"
42415       ],
42416       [
42417         "Lithuania (Lietuva)",
42418         "lt",
42419         "370"
42420       ],
42421       [
42422         "Luxembourg",
42423         "lu",
42424         "352"
42425       ],
42426       [
42427         "Macau (澳門)",
42428         "mo",
42429         "853"
42430       ],
42431       [
42432         "Macedonia (FYROM) (Македонија)",
42433         "mk",
42434         "389"
42435       ],
42436       [
42437         "Madagascar (Madagasikara)",
42438         "mg",
42439         "261"
42440       ],
42441       [
42442         "Malawi",
42443         "mw",
42444         "265"
42445       ],
42446       [
42447         "Malaysia",
42448         "my",
42449         "60"
42450       ],
42451       [
42452         "Maldives",
42453         "mv",
42454         "960"
42455       ],
42456       [
42457         "Mali",
42458         "ml",
42459         "223"
42460       ],
42461       [
42462         "Malta",
42463         "mt",
42464         "356"
42465       ],
42466       [
42467         "Marshall Islands",
42468         "mh",
42469         "692"
42470       ],
42471       [
42472         "Martinique",
42473         "mq",
42474         "596"
42475       ],
42476       [
42477         "Mauritania (‫موريتانيا‬‎)",
42478         "mr",
42479         "222"
42480       ],
42481       [
42482         "Mauritius (Moris)",
42483         "mu",
42484         "230"
42485       ],
42486       [
42487         "Mayotte",
42488         "yt",
42489         "262",
42490         1
42491       ],
42492       [
42493         "Mexico (México)",
42494         "mx",
42495         "52"
42496       ],
42497       [
42498         "Micronesia",
42499         "fm",
42500         "691"
42501       ],
42502       [
42503         "Moldova (Republica Moldova)",
42504         "md",
42505         "373"
42506       ],
42507       [
42508         "Monaco",
42509         "mc",
42510         "377"
42511       ],
42512       [
42513         "Mongolia (Монгол)",
42514         "mn",
42515         "976"
42516       ],
42517       [
42518         "Montenegro (Crna Gora)",
42519         "me",
42520         "382"
42521       ],
42522       [
42523         "Montserrat",
42524         "ms",
42525         "1664"
42526       ],
42527       [
42528         "Morocco (‫المغرب‬‎)",
42529         "ma",
42530         "212",
42531         0
42532       ],
42533       [
42534         "Mozambique (Moçambique)",
42535         "mz",
42536         "258"
42537       ],
42538       [
42539         "Myanmar (Burma) (မြန်မာ)",
42540         "mm",
42541         "95"
42542       ],
42543       [
42544         "Namibia (Namibië)",
42545         "na",
42546         "264"
42547       ],
42548       [
42549         "Nauru",
42550         "nr",
42551         "674"
42552       ],
42553       [
42554         "Nepal (नेपाल)",
42555         "np",
42556         "977"
42557       ],
42558       [
42559         "Netherlands (Nederland)",
42560         "nl",
42561         "31"
42562       ],
42563       [
42564         "New Caledonia (Nouvelle-Calédonie)",
42565         "nc",
42566         "687"
42567       ],
42568       [
42569         "New Zealand",
42570         "nz",
42571         "64"
42572       ],
42573       [
42574         "Nicaragua",
42575         "ni",
42576         "505"
42577       ],
42578       [
42579         "Niger (Nijar)",
42580         "ne",
42581         "227"
42582       ],
42583       [
42584         "Nigeria",
42585         "ng",
42586         "234"
42587       ],
42588       [
42589         "Niue",
42590         "nu",
42591         "683"
42592       ],
42593       [
42594         "Norfolk Island",
42595         "nf",
42596         "672"
42597       ],
42598       [
42599         "North Korea (조선 민주주의 인민 공화국)",
42600         "kp",
42601         "850"
42602       ],
42603       [
42604         "Northern Mariana Islands",
42605         "mp",
42606         "1670"
42607       ],
42608       [
42609         "Norway (Norge)",
42610         "no",
42611         "47",
42612         0
42613       ],
42614       [
42615         "Oman (‫عُمان‬‎)",
42616         "om",
42617         "968"
42618       ],
42619       [
42620         "Pakistan (‫پاکستان‬‎)",
42621         "pk",
42622         "92"
42623       ],
42624       [
42625         "Palau",
42626         "pw",
42627         "680"
42628       ],
42629       [
42630         "Palestine (‫فلسطين‬‎)",
42631         "ps",
42632         "970"
42633       ],
42634       [
42635         "Panama (Panamá)",
42636         "pa",
42637         "507"
42638       ],
42639       [
42640         "Papua New Guinea",
42641         "pg",
42642         "675"
42643       ],
42644       [
42645         "Paraguay",
42646         "py",
42647         "595"
42648       ],
42649       [
42650         "Peru (Perú)",
42651         "pe",
42652         "51"
42653       ],
42654       [
42655         "Philippines",
42656         "ph",
42657         "63"
42658       ],
42659       [
42660         "Poland (Polska)",
42661         "pl",
42662         "48"
42663       ],
42664       [
42665         "Portugal",
42666         "pt",
42667         "351"
42668       ],
42669       [
42670         "Puerto Rico",
42671         "pr",
42672         "1",
42673         3,
42674         ["787", "939"]
42675       ],
42676       [
42677         "Qatar (‫قطر‬‎)",
42678         "qa",
42679         "974"
42680       ],
42681       [
42682         "Réunion (La Réunion)",
42683         "re",
42684         "262",
42685         0
42686       ],
42687       [
42688         "Romania (România)",
42689         "ro",
42690         "40"
42691       ],
42692       [
42693         "Russia (Россия)",
42694         "ru",
42695         "7",
42696         0
42697       ],
42698       [
42699         "Rwanda",
42700         "rw",
42701         "250"
42702       ],
42703       [
42704         "Saint Barthélemy",
42705         "bl",
42706         "590",
42707         1
42708       ],
42709       [
42710         "Saint Helena",
42711         "sh",
42712         "290"
42713       ],
42714       [
42715         "Saint Kitts and Nevis",
42716         "kn",
42717         "1869"
42718       ],
42719       [
42720         "Saint Lucia",
42721         "lc",
42722         "1758"
42723       ],
42724       [
42725         "Saint Martin (Saint-Martin (partie française))",
42726         "mf",
42727         "590",
42728         2
42729       ],
42730       [
42731         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42732         "pm",
42733         "508"
42734       ],
42735       [
42736         "Saint Vincent and the Grenadines",
42737         "vc",
42738         "1784"
42739       ],
42740       [
42741         "Samoa",
42742         "ws",
42743         "685"
42744       ],
42745       [
42746         "San Marino",
42747         "sm",
42748         "378"
42749       ],
42750       [
42751         "São Tomé and Príncipe (São Tomé e Príncipe)",
42752         "st",
42753         "239"
42754       ],
42755       [
42756         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42757         "sa",
42758         "966"
42759       ],
42760       [
42761         "Senegal (Sénégal)",
42762         "sn",
42763         "221"
42764       ],
42765       [
42766         "Serbia (Србија)",
42767         "rs",
42768         "381"
42769       ],
42770       [
42771         "Seychelles",
42772         "sc",
42773         "248"
42774       ],
42775       [
42776         "Sierra Leone",
42777         "sl",
42778         "232"
42779       ],
42780       [
42781         "Singapore",
42782         "sg",
42783         "65"
42784       ],
42785       [
42786         "Sint Maarten",
42787         "sx",
42788         "1721"
42789       ],
42790       [
42791         "Slovakia (Slovensko)",
42792         "sk",
42793         "421"
42794       ],
42795       [
42796         "Slovenia (Slovenija)",
42797         "si",
42798         "386"
42799       ],
42800       [
42801         "Solomon Islands",
42802         "sb",
42803         "677"
42804       ],
42805       [
42806         "Somalia (Soomaaliya)",
42807         "so",
42808         "252"
42809       ],
42810       [
42811         "South Africa",
42812         "za",
42813         "27"
42814       ],
42815       [
42816         "South Korea (대한민국)",
42817         "kr",
42818         "82"
42819       ],
42820       [
42821         "South Sudan (‫جنوب السودان‬‎)",
42822         "ss",
42823         "211"
42824       ],
42825       [
42826         "Spain (España)",
42827         "es",
42828         "34"
42829       ],
42830       [
42831         "Sri Lanka (ශ්‍රී ලංකාව)",
42832         "lk",
42833         "94"
42834       ],
42835       [
42836         "Sudan (‫السودان‬‎)",
42837         "sd",
42838         "249"
42839       ],
42840       [
42841         "Suriname",
42842         "sr",
42843         "597"
42844       ],
42845       [
42846         "Svalbard and Jan Mayen",
42847         "sj",
42848         "47",
42849         1
42850       ],
42851       [
42852         "Swaziland",
42853         "sz",
42854         "268"
42855       ],
42856       [
42857         "Sweden (Sverige)",
42858         "se",
42859         "46"
42860       ],
42861       [
42862         "Switzerland (Schweiz)",
42863         "ch",
42864         "41"
42865       ],
42866       [
42867         "Syria (‫سوريا‬‎)",
42868         "sy",
42869         "963"
42870       ],
42871       [
42872         "Taiwan (台灣)",
42873         "tw",
42874         "886"
42875       ],
42876       [
42877         "Tajikistan",
42878         "tj",
42879         "992"
42880       ],
42881       [
42882         "Tanzania",
42883         "tz",
42884         "255"
42885       ],
42886       [
42887         "Thailand (ไทย)",
42888         "th",
42889         "66"
42890       ],
42891       [
42892         "Timor-Leste",
42893         "tl",
42894         "670"
42895       ],
42896       [
42897         "Togo",
42898         "tg",
42899         "228"
42900       ],
42901       [
42902         "Tokelau",
42903         "tk",
42904         "690"
42905       ],
42906       [
42907         "Tonga",
42908         "to",
42909         "676"
42910       ],
42911       [
42912         "Trinidad and Tobago",
42913         "tt",
42914         "1868"
42915       ],
42916       [
42917         "Tunisia (‫تونس‬‎)",
42918         "tn",
42919         "216"
42920       ],
42921       [
42922         "Turkey (Türkiye)",
42923         "tr",
42924         "90"
42925       ],
42926       [
42927         "Turkmenistan",
42928         "tm",
42929         "993"
42930       ],
42931       [
42932         "Turks and Caicos Islands",
42933         "tc",
42934         "1649"
42935       ],
42936       [
42937         "Tuvalu",
42938         "tv",
42939         "688"
42940       ],
42941       [
42942         "U.S. Virgin Islands",
42943         "vi",
42944         "1340"
42945       ],
42946       [
42947         "Uganda",
42948         "ug",
42949         "256"
42950       ],
42951       [
42952         "Ukraine (Україна)",
42953         "ua",
42954         "380"
42955       ],
42956       [
42957         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42958         "ae",
42959         "971"
42960       ],
42961       [
42962         "United Kingdom",
42963         "gb",
42964         "44",
42965         0
42966       ],
42967       [
42968         "United States",
42969         "us",
42970         "1",
42971         0
42972       ],
42973       [
42974         "Uruguay",
42975         "uy",
42976         "598"
42977       ],
42978       [
42979         "Uzbekistan (Oʻzbekiston)",
42980         "uz",
42981         "998"
42982       ],
42983       [
42984         "Vanuatu",
42985         "vu",
42986         "678"
42987       ],
42988       [
42989         "Vatican City (Città del Vaticano)",
42990         "va",
42991         "39",
42992         1
42993       ],
42994       [
42995         "Venezuela",
42996         "ve",
42997         "58"
42998       ],
42999       [
43000         "Vietnam (Việt Nam)",
43001         "vn",
43002         "84"
43003       ],
43004       [
43005         "Wallis and Futuna (Wallis-et-Futuna)",
43006         "wf",
43007         "681"
43008       ],
43009       [
43010         "Western Sahara (‫الصحراء الغربية‬‎)",
43011         "eh",
43012         "212",
43013         1
43014       ],
43015       [
43016         "Yemen (‫اليمن‬‎)",
43017         "ye",
43018         "967"
43019       ],
43020       [
43021         "Zambia",
43022         "zm",
43023         "260"
43024       ],
43025       [
43026         "Zimbabwe",
43027         "zw",
43028         "263"
43029       ],
43030       [
43031         "Åland Islands",
43032         "ax",
43033         "358",
43034         1
43035       ]
43036   ];
43037   
43038   return d;
43039 }/**
43040 *    This script refer to:
43041 *    Title: International Telephone Input
43042 *    Author: Jack O'Connor
43043 *    Code version:  v12.1.12
43044 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43045 **/
43046
43047 /**
43048  * @class Roo.bootstrap.PhoneInput
43049  * @extends Roo.bootstrap.TriggerField
43050  * An input with International dial-code selection
43051  
43052  * @cfg {String} defaultDialCode default '+852'
43053  * @cfg {Array} preferedCountries default []
43054   
43055  * @constructor
43056  * Create a new PhoneInput.
43057  * @param {Object} config Configuration options
43058  */
43059
43060 Roo.bootstrap.PhoneInput = function(config) {
43061     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43062 };
43063
43064 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43065         
43066         listWidth: undefined,
43067         
43068         selectedClass: 'active',
43069         
43070         invalidClass : "has-warning",
43071         
43072         validClass: 'has-success',
43073         
43074         allowed: '0123456789',
43075         
43076         max_length: 15,
43077         
43078         /**
43079          * @cfg {String} defaultDialCode The default dial code when initializing the input
43080          */
43081         defaultDialCode: '+852',
43082         
43083         /**
43084          * @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
43085          */
43086         preferedCountries: false,
43087         
43088         getAutoCreate : function()
43089         {
43090             var data = Roo.bootstrap.PhoneInputData();
43091             var align = this.labelAlign || this.parentLabelAlign();
43092             var id = Roo.id();
43093             
43094             this.allCountries = [];
43095             this.dialCodeMapping = [];
43096             
43097             for (var i = 0; i < data.length; i++) {
43098               var c = data[i];
43099               this.allCountries[i] = {
43100                 name: c[0],
43101                 iso2: c[1],
43102                 dialCode: c[2],
43103                 priority: c[3] || 0,
43104                 areaCodes: c[4] || null
43105               };
43106               this.dialCodeMapping[c[2]] = {
43107                   name: c[0],
43108                   iso2: c[1],
43109                   priority: c[3] || 0,
43110                   areaCodes: c[4] || null
43111               };
43112             }
43113             
43114             var cfg = {
43115                 cls: 'form-group',
43116                 cn: []
43117             };
43118             
43119             var input =  {
43120                 tag: 'input',
43121                 id : id,
43122                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43123                 maxlength: this.max_length,
43124                 cls : 'form-control tel-input',
43125                 autocomplete: 'new-password'
43126             };
43127             
43128             var hiddenInput = {
43129                 tag: 'input',
43130                 type: 'hidden',
43131                 cls: 'hidden-tel-input'
43132             };
43133             
43134             if (this.name) {
43135                 hiddenInput.name = this.name;
43136             }
43137             
43138             if (this.disabled) {
43139                 input.disabled = true;
43140             }
43141             
43142             var flag_container = {
43143                 tag: 'div',
43144                 cls: 'flag-box',
43145                 cn: [
43146                     {
43147                         tag: 'div',
43148                         cls: 'flag'
43149                     },
43150                     {
43151                         tag: 'div',
43152                         cls: 'caret'
43153                     }
43154                 ]
43155             };
43156             
43157             var box = {
43158                 tag: 'div',
43159                 cls: this.hasFeedback ? 'has-feedback' : '',
43160                 cn: [
43161                     hiddenInput,
43162                     input,
43163                     {
43164                         tag: 'input',
43165                         cls: 'dial-code-holder',
43166                         disabled: true
43167                     }
43168                 ]
43169             };
43170             
43171             var container = {
43172                 cls: 'roo-select2-container input-group',
43173                 cn: [
43174                     flag_container,
43175                     box
43176                 ]
43177             };
43178             
43179             if (this.fieldLabel.length) {
43180                 var indicator = {
43181                     tag: 'i',
43182                     tooltip: 'This field is required'
43183                 };
43184                 
43185                 var label = {
43186                     tag: 'label',
43187                     'for':  id,
43188                     cls: 'control-label',
43189                     cn: []
43190                 };
43191                 
43192                 var label_text = {
43193                     tag: 'span',
43194                     html: this.fieldLabel
43195                 };
43196                 
43197                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43198                 label.cn = [
43199                     indicator,
43200                     label_text
43201                 ];
43202                 
43203                 if(this.indicatorpos == 'right') {
43204                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43205                     label.cn = [
43206                         label_text,
43207                         indicator
43208                     ];
43209                 }
43210                 
43211                 if(align == 'left') {
43212                     container = {
43213                         tag: 'div',
43214                         cn: [
43215                             container
43216                         ]
43217                     };
43218                     
43219                     if(this.labelWidth > 12){
43220                         label.style = "width: " + this.labelWidth + 'px';
43221                     }
43222                     if(this.labelWidth < 13 && this.labelmd == 0){
43223                         this.labelmd = this.labelWidth;
43224                     }
43225                     if(this.labellg > 0){
43226                         label.cls += ' col-lg-' + this.labellg;
43227                         input.cls += ' col-lg-' + (12 - this.labellg);
43228                     }
43229                     if(this.labelmd > 0){
43230                         label.cls += ' col-md-' + this.labelmd;
43231                         container.cls += ' col-md-' + (12 - this.labelmd);
43232                     }
43233                     if(this.labelsm > 0){
43234                         label.cls += ' col-sm-' + this.labelsm;
43235                         container.cls += ' col-sm-' + (12 - this.labelsm);
43236                     }
43237                     if(this.labelxs > 0){
43238                         label.cls += ' col-xs-' + this.labelxs;
43239                         container.cls += ' col-xs-' + (12 - this.labelxs);
43240                     }
43241                 }
43242             }
43243             
43244             cfg.cn = [
43245                 label,
43246                 container
43247             ];
43248             
43249             var settings = this;
43250             
43251             ['xs','sm','md','lg'].map(function(size){
43252                 if (settings[size]) {
43253                     cfg.cls += ' col-' + size + '-' + settings[size];
43254                 }
43255             });
43256             
43257             this.store = new Roo.data.Store({
43258                 proxy : new Roo.data.MemoryProxy({}),
43259                 reader : new Roo.data.JsonReader({
43260                     fields : [
43261                         {
43262                             'name' : 'name',
43263                             'type' : 'string'
43264                         },
43265                         {
43266                             'name' : 'iso2',
43267                             'type' : 'string'
43268                         },
43269                         {
43270                             'name' : 'dialCode',
43271                             'type' : 'string'
43272                         },
43273                         {
43274                             'name' : 'priority',
43275                             'type' : 'string'
43276                         },
43277                         {
43278                             'name' : 'areaCodes',
43279                             'type' : 'string'
43280                         }
43281                     ]
43282                 })
43283             });
43284             
43285             if(!this.preferedCountries) {
43286                 this.preferedCountries = [
43287                     'hk',
43288                     'gb',
43289                     'us'
43290                 ];
43291             }
43292             
43293             var p = this.preferedCountries.reverse();
43294             
43295             if(p) {
43296                 for (var i = 0; i < p.length; i++) {
43297                     for (var j = 0; j < this.allCountries.length; j++) {
43298                         if(this.allCountries[j].iso2 == p[i]) {
43299                             var t = this.allCountries[j];
43300                             this.allCountries.splice(j,1);
43301                             this.allCountries.unshift(t);
43302                         }
43303                     } 
43304                 }
43305             }
43306             
43307             this.store.proxy.data = {
43308                 success: true,
43309                 data: this.allCountries
43310             };
43311             
43312             return cfg;
43313         },
43314         
43315         initEvents : function()
43316         {
43317             this.createList();
43318             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43319             
43320             this.indicator = this.indicatorEl();
43321             this.flag = this.flagEl();
43322             this.dialCodeHolder = this.dialCodeHolderEl();
43323             
43324             this.trigger = this.el.select('div.flag-box',true).first();
43325             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43326             
43327             var _this = this;
43328             
43329             (function(){
43330                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43331                 _this.list.setWidth(lw);
43332             }).defer(100);
43333             
43334             this.list.on('mouseover', this.onViewOver, this);
43335             this.list.on('mousemove', this.onViewMove, this);
43336             this.inputEl().on("keyup", this.onKeyUp, this);
43337             this.inputEl().on("keypress", this.onKeyPress, this);
43338             
43339             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43340
43341             this.view = new Roo.View(this.list, this.tpl, {
43342                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43343             });
43344             
43345             this.view.on('click', this.onViewClick, this);
43346             this.setValue(this.defaultDialCode);
43347         },
43348         
43349         onTriggerClick : function(e)
43350         {
43351             Roo.log('trigger click');
43352             if(this.disabled){
43353                 return;
43354             }
43355             
43356             if(this.isExpanded()){
43357                 this.collapse();
43358                 this.hasFocus = false;
43359             }else {
43360                 this.store.load({});
43361                 this.hasFocus = true;
43362                 this.expand();
43363             }
43364         },
43365         
43366         isExpanded : function()
43367         {
43368             return this.list.isVisible();
43369         },
43370         
43371         collapse : function()
43372         {
43373             if(!this.isExpanded()){
43374                 return;
43375             }
43376             this.list.hide();
43377             Roo.get(document).un('mousedown', this.collapseIf, this);
43378             Roo.get(document).un('mousewheel', this.collapseIf, this);
43379             this.fireEvent('collapse', this);
43380             this.validate();
43381         },
43382         
43383         expand : function()
43384         {
43385             Roo.log('expand');
43386
43387             if(this.isExpanded() || !this.hasFocus){
43388                 return;
43389             }
43390             
43391             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43392             this.list.setWidth(lw);
43393             
43394             this.list.show();
43395             this.restrictHeight();
43396             
43397             Roo.get(document).on('mousedown', this.collapseIf, this);
43398             Roo.get(document).on('mousewheel', this.collapseIf, this);
43399             
43400             this.fireEvent('expand', this);
43401         },
43402         
43403         restrictHeight : function()
43404         {
43405             this.list.alignTo(this.inputEl(), this.listAlign);
43406             this.list.alignTo(this.inputEl(), this.listAlign);
43407         },
43408         
43409         onViewOver : function(e, t)
43410         {
43411             if(this.inKeyMode){
43412                 return;
43413             }
43414             var item = this.view.findItemFromChild(t);
43415             
43416             if(item){
43417                 var index = this.view.indexOf(item);
43418                 this.select(index, false);
43419             }
43420         },
43421
43422         // private
43423         onViewClick : function(view, doFocus, el, e)
43424         {
43425             var index = this.view.getSelectedIndexes()[0];
43426             
43427             var r = this.store.getAt(index);
43428             
43429             if(r){
43430                 this.onSelect(r, index);
43431             }
43432             if(doFocus !== false && !this.blockFocus){
43433                 this.inputEl().focus();
43434             }
43435         },
43436         
43437         onViewMove : function(e, t)
43438         {
43439             this.inKeyMode = false;
43440         },
43441         
43442         select : function(index, scrollIntoView)
43443         {
43444             this.selectedIndex = index;
43445             this.view.select(index);
43446             if(scrollIntoView !== false){
43447                 var el = this.view.getNode(index);
43448                 if(el){
43449                     this.list.scrollChildIntoView(el, false);
43450                 }
43451             }
43452         },
43453         
43454         createList : function()
43455         {
43456             this.list = Roo.get(document.body).createChild({
43457                 tag: 'ul',
43458                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43459                 style: 'display:none'
43460             });
43461             
43462             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43463         },
43464         
43465         collapseIf : function(e)
43466         {
43467             var in_combo  = e.within(this.el);
43468             var in_list =  e.within(this.list);
43469             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43470             
43471             if (in_combo || in_list || is_list) {
43472                 return;
43473             }
43474             this.collapse();
43475         },
43476         
43477         onSelect : function(record, index)
43478         {
43479             if(this.fireEvent('beforeselect', this, record, index) !== false){
43480                 
43481                 this.setFlagClass(record.data.iso2);
43482                 this.setDialCode(record.data.dialCode);
43483                 this.hasFocus = false;
43484                 this.collapse();
43485                 this.fireEvent('select', this, record, index);
43486             }
43487         },
43488         
43489         flagEl : function()
43490         {
43491             var flag = this.el.select('div.flag',true).first();
43492             if(!flag){
43493                 return false;
43494             }
43495             return flag;
43496         },
43497         
43498         dialCodeHolderEl : function()
43499         {
43500             var d = this.el.select('input.dial-code-holder',true).first();
43501             if(!d){
43502                 return false;
43503             }
43504             return d;
43505         },
43506         
43507         setDialCode : function(v)
43508         {
43509             this.dialCodeHolder.dom.value = '+'+v;
43510         },
43511         
43512         setFlagClass : function(n)
43513         {
43514             this.flag.dom.className = 'flag '+n;
43515         },
43516         
43517         getValue : function()
43518         {
43519             var v = this.inputEl().getValue();
43520             if(this.dialCodeHolder) {
43521                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43522             }
43523             return v;
43524         },
43525         
43526         setValue : function(v)
43527         {
43528             var d = this.getDialCode(v);
43529             
43530             //invalid dial code
43531             if(v.length == 0 || !d || d.length == 0) {
43532                 if(this.rendered){
43533                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43534                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43535                 }
43536                 return;
43537             }
43538             
43539             //valid dial code
43540             this.setFlagClass(this.dialCodeMapping[d].iso2);
43541             this.setDialCode(d);
43542             this.inputEl().dom.value = v.replace('+'+d,'');
43543             this.hiddenEl().dom.value = this.getValue();
43544             
43545             this.validate();
43546         },
43547         
43548         getDialCode : function(v)
43549         {
43550             v = v ||  '';
43551             
43552             if (v.length == 0) {
43553                 return this.dialCodeHolder.dom.value;
43554             }
43555             
43556             var dialCode = "";
43557             if (v.charAt(0) != "+") {
43558                 return false;
43559             }
43560             var numericChars = "";
43561             for (var i = 1; i < v.length; i++) {
43562               var c = v.charAt(i);
43563               if (!isNaN(c)) {
43564                 numericChars += c;
43565                 if (this.dialCodeMapping[numericChars]) {
43566                   dialCode = v.substr(1, i);
43567                 }
43568                 if (numericChars.length == 4) {
43569                   break;
43570                 }
43571               }
43572             }
43573             return dialCode;
43574         },
43575         
43576         reset : function()
43577         {
43578             this.setValue(this.defaultDialCode);
43579             this.validate();
43580         },
43581         
43582         hiddenEl : function()
43583         {
43584             return this.el.select('input.hidden-tel-input',true).first();
43585         },
43586         
43587         // after setting val
43588         onKeyUp : function(e){
43589             this.setValue(this.getValue());
43590         },
43591         
43592         onKeyPress : function(e){
43593             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43594                 e.stopEvent();
43595             }
43596         }
43597         
43598 });
43599 /**
43600  * @class Roo.bootstrap.MoneyField
43601  * @extends Roo.bootstrap.ComboBox
43602  * Bootstrap MoneyField class
43603  * 
43604  * @constructor
43605  * Create a new MoneyField.
43606  * @param {Object} config Configuration options
43607  */
43608
43609 Roo.bootstrap.MoneyField = function(config) {
43610     
43611     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43612     
43613 };
43614
43615 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43616     
43617     /**
43618      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43619      */
43620     allowDecimals : true,
43621     /**
43622      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43623      */
43624     decimalSeparator : ".",
43625     /**
43626      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43627      */
43628     decimalPrecision : 0,
43629     /**
43630      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43631      */
43632     allowNegative : true,
43633     /**
43634      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43635      */
43636     allowZero: true,
43637     /**
43638      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43639      */
43640     minValue : Number.NEGATIVE_INFINITY,
43641     /**
43642      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43643      */
43644     maxValue : Number.MAX_VALUE,
43645     /**
43646      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43647      */
43648     minText : "The minimum value for this field is {0}",
43649     /**
43650      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43651      */
43652     maxText : "The maximum value for this field is {0}",
43653     /**
43654      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43655      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43656      */
43657     nanText : "{0} is not a valid number",
43658     /**
43659      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43660      */
43661     castInt : true,
43662     /**
43663      * @cfg {String} defaults currency of the MoneyField
43664      * value should be in lkey
43665      */
43666     defaultCurrency : false,
43667     /**
43668      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43669      */
43670     thousandsDelimiter : false,
43671     /**
43672      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43673      */
43674     max_length: false,
43675     
43676     inputlg : 9,
43677     inputmd : 9,
43678     inputsm : 9,
43679     inputxs : 6,
43680     
43681     store : false,
43682     
43683     getAutoCreate : function()
43684     {
43685         var align = this.labelAlign || this.parentLabelAlign();
43686         
43687         var id = Roo.id();
43688
43689         var cfg = {
43690             cls: 'form-group',
43691             cn: []
43692         };
43693
43694         var input =  {
43695             tag: 'input',
43696             id : id,
43697             cls : 'form-control roo-money-amount-input',
43698             autocomplete: 'new-password'
43699         };
43700         
43701         var hiddenInput = {
43702             tag: 'input',
43703             type: 'hidden',
43704             id: Roo.id(),
43705             cls: 'hidden-number-input'
43706         };
43707         
43708         if(this.max_length) {
43709             input.maxlength = this.max_length; 
43710         }
43711         
43712         if (this.name) {
43713             hiddenInput.name = this.name;
43714         }
43715
43716         if (this.disabled) {
43717             input.disabled = true;
43718         }
43719
43720         var clg = 12 - this.inputlg;
43721         var cmd = 12 - this.inputmd;
43722         var csm = 12 - this.inputsm;
43723         var cxs = 12 - this.inputxs;
43724         
43725         var container = {
43726             tag : 'div',
43727             cls : 'row roo-money-field',
43728             cn : [
43729                 {
43730                     tag : 'div',
43731                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43732                     cn : [
43733                         {
43734                             tag : 'div',
43735                             cls: 'roo-select2-container input-group',
43736                             cn: [
43737                                 {
43738                                     tag : 'input',
43739                                     cls : 'form-control roo-money-currency-input',
43740                                     autocomplete: 'new-password',
43741                                     readOnly : 1,
43742                                     name : this.currencyName
43743                                 },
43744                                 {
43745                                     tag :'span',
43746                                     cls : 'input-group-addon',
43747                                     cn : [
43748                                         {
43749                                             tag: 'span',
43750                                             cls: 'caret'
43751                                         }
43752                                     ]
43753                                 }
43754                             ]
43755                         }
43756                     ]
43757                 },
43758                 {
43759                     tag : 'div',
43760                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43761                     cn : [
43762                         {
43763                             tag: 'div',
43764                             cls: this.hasFeedback ? 'has-feedback' : '',
43765                             cn: [
43766                                 input
43767                             ]
43768                         }
43769                     ]
43770                 }
43771             ]
43772             
43773         };
43774         
43775         if (this.fieldLabel.length) {
43776             var indicator = {
43777                 tag: 'i',
43778                 tooltip: 'This field is required'
43779             };
43780
43781             var label = {
43782                 tag: 'label',
43783                 'for':  id,
43784                 cls: 'control-label',
43785                 cn: []
43786             };
43787
43788             var label_text = {
43789                 tag: 'span',
43790                 html: this.fieldLabel
43791             };
43792
43793             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43794             label.cn = [
43795                 indicator,
43796                 label_text
43797             ];
43798
43799             if(this.indicatorpos == 'right') {
43800                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43801                 label.cn = [
43802                     label_text,
43803                     indicator
43804                 ];
43805             }
43806
43807             if(align == 'left') {
43808                 container = {
43809                     tag: 'div',
43810                     cn: [
43811                         container
43812                     ]
43813                 };
43814
43815                 if(this.labelWidth > 12){
43816                     label.style = "width: " + this.labelWidth + 'px';
43817                 }
43818                 if(this.labelWidth < 13 && this.labelmd == 0){
43819                     this.labelmd = this.labelWidth;
43820                 }
43821                 if(this.labellg > 0){
43822                     label.cls += ' col-lg-' + this.labellg;
43823                     input.cls += ' col-lg-' + (12 - this.labellg);
43824                 }
43825                 if(this.labelmd > 0){
43826                     label.cls += ' col-md-' + this.labelmd;
43827                     container.cls += ' col-md-' + (12 - this.labelmd);
43828                 }
43829                 if(this.labelsm > 0){
43830                     label.cls += ' col-sm-' + this.labelsm;
43831                     container.cls += ' col-sm-' + (12 - this.labelsm);
43832                 }
43833                 if(this.labelxs > 0){
43834                     label.cls += ' col-xs-' + this.labelxs;
43835                     container.cls += ' col-xs-' + (12 - this.labelxs);
43836                 }
43837             }
43838         }
43839
43840         cfg.cn = [
43841             label,
43842             container,
43843             hiddenInput
43844         ];
43845         
43846         var settings = this;
43847
43848         ['xs','sm','md','lg'].map(function(size){
43849             if (settings[size]) {
43850                 cfg.cls += ' col-' + size + '-' + settings[size];
43851             }
43852         });
43853         
43854         return cfg;
43855     },
43856     
43857     initEvents : function()
43858     {
43859         this.indicator = this.indicatorEl();
43860         
43861         this.initCurrencyEvent();
43862         
43863         this.initNumberEvent();
43864     },
43865     
43866     initCurrencyEvent : function()
43867     {
43868         if (!this.store) {
43869             throw "can not find store for combo";
43870         }
43871         
43872         this.store = Roo.factory(this.store, Roo.data);
43873         this.store.parent = this;
43874         
43875         this.createList();
43876         
43877         this.triggerEl = this.el.select('.input-group-addon', true).first();
43878         
43879         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43880         
43881         var _this = this;
43882         
43883         (function(){
43884             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43885             _this.list.setWidth(lw);
43886         }).defer(100);
43887         
43888         this.list.on('mouseover', this.onViewOver, this);
43889         this.list.on('mousemove', this.onViewMove, this);
43890         this.list.on('scroll', this.onViewScroll, this);
43891         
43892         if(!this.tpl){
43893             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43894         }
43895         
43896         this.view = new Roo.View(this.list, this.tpl, {
43897             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43898         });
43899         
43900         this.view.on('click', this.onViewClick, this);
43901         
43902         this.store.on('beforeload', this.onBeforeLoad, this);
43903         this.store.on('load', this.onLoad, this);
43904         this.store.on('loadexception', this.onLoadException, this);
43905         
43906         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43907             "up" : function(e){
43908                 this.inKeyMode = true;
43909                 this.selectPrev();
43910             },
43911
43912             "down" : function(e){
43913                 if(!this.isExpanded()){
43914                     this.onTriggerClick();
43915                 }else{
43916                     this.inKeyMode = true;
43917                     this.selectNext();
43918                 }
43919             },
43920
43921             "enter" : function(e){
43922                 this.collapse();
43923                 
43924                 if(this.fireEvent("specialkey", this, e)){
43925                     this.onViewClick(false);
43926                 }
43927                 
43928                 return true;
43929             },
43930
43931             "esc" : function(e){
43932                 this.collapse();
43933             },
43934
43935             "tab" : function(e){
43936                 this.collapse();
43937                 
43938                 if(this.fireEvent("specialkey", this, e)){
43939                     this.onViewClick(false);
43940                 }
43941                 
43942                 return true;
43943             },
43944
43945             scope : this,
43946
43947             doRelay : function(foo, bar, hname){
43948                 if(hname == 'down' || this.scope.isExpanded()){
43949                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43950                 }
43951                 return true;
43952             },
43953
43954             forceKeyDown: true
43955         });
43956         
43957         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43958         
43959     },
43960     
43961     initNumberEvent : function(e)
43962     {
43963         this.inputEl().on("keydown" , this.fireKey,  this);
43964         this.inputEl().on("focus", this.onFocus,  this);
43965         this.inputEl().on("blur", this.onBlur,  this);
43966         
43967         this.inputEl().relayEvent('keyup', this);
43968         
43969         if(this.indicator){
43970             this.indicator.addClass('invisible');
43971         }
43972  
43973         this.originalValue = this.getValue();
43974         
43975         if(this.validationEvent == 'keyup'){
43976             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43977             this.inputEl().on('keyup', this.filterValidation, this);
43978         }
43979         else if(this.validationEvent !== false){
43980             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43981         }
43982         
43983         if(this.selectOnFocus){
43984             this.on("focus", this.preFocus, this);
43985             
43986         }
43987         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43988             this.inputEl().on("keypress", this.filterKeys, this);
43989         } else {
43990             this.inputEl().relayEvent('keypress', this);
43991         }
43992         
43993         var allowed = "0123456789";
43994         
43995         if(this.allowDecimals){
43996             allowed += this.decimalSeparator;
43997         }
43998         
43999         if(this.allowNegative){
44000             allowed += "-";
44001         }
44002         
44003         if(this.thousandsDelimiter) {
44004             allowed += ",";
44005         }
44006         
44007         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44008         
44009         var keyPress = function(e){
44010             
44011             var k = e.getKey();
44012             
44013             var c = e.getCharCode();
44014             
44015             if(
44016                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44017                     allowed.indexOf(String.fromCharCode(c)) === -1
44018             ){
44019                 e.stopEvent();
44020                 return;
44021             }
44022             
44023             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44024                 return;
44025             }
44026             
44027             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44028                 e.stopEvent();
44029             }
44030         };
44031         
44032         this.inputEl().on("keypress", keyPress, this);
44033         
44034     },
44035     
44036     onTriggerClick : function(e)
44037     {   
44038         if(this.disabled){
44039             return;
44040         }
44041         
44042         this.page = 0;
44043         this.loadNext = false;
44044         
44045         if(this.isExpanded()){
44046             this.collapse();
44047             return;
44048         }
44049         
44050         this.hasFocus = true;
44051         
44052         if(this.triggerAction == 'all') {
44053             this.doQuery(this.allQuery, true);
44054             return;
44055         }
44056         
44057         this.doQuery(this.getRawValue());
44058     },
44059     
44060     getCurrency : function()
44061     {   
44062         var v = this.currencyEl().getValue();
44063         
44064         return v;
44065     },
44066     
44067     restrictHeight : function()
44068     {
44069         this.list.alignTo(this.currencyEl(), this.listAlign);
44070         this.list.alignTo(this.currencyEl(), this.listAlign);
44071     },
44072     
44073     onViewClick : function(view, doFocus, el, e)
44074     {
44075         var index = this.view.getSelectedIndexes()[0];
44076         
44077         var r = this.store.getAt(index);
44078         
44079         if(r){
44080             this.onSelect(r, index);
44081         }
44082     },
44083     
44084     onSelect : function(record, index){
44085         
44086         if(this.fireEvent('beforeselect', this, record, index) !== false){
44087         
44088             this.setFromCurrencyData(index > -1 ? record.data : false);
44089             
44090             this.collapse();
44091             
44092             this.fireEvent('select', this, record, index);
44093         }
44094     },
44095     
44096     setFromCurrencyData : function(o)
44097     {
44098         var currency = '';
44099         
44100         this.lastCurrency = o;
44101         
44102         if (this.currencyField) {
44103             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44104         } else {
44105             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44106         }
44107         
44108         this.lastSelectionText = currency;
44109         
44110         //setting default currency
44111         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44112             this.setCurrency(this.defaultCurrency);
44113             return;
44114         }
44115         
44116         this.setCurrency(currency);
44117     },
44118     
44119     setFromData : function(o)
44120     {
44121         var c = {};
44122         
44123         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44124         
44125         this.setFromCurrencyData(c);
44126         
44127         var value = '';
44128         
44129         if (this.name) {
44130             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44131         } else {
44132             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44133         }
44134         
44135         this.setValue(value);
44136         
44137     },
44138     
44139     setCurrency : function(v)
44140     {   
44141         this.currencyValue = v;
44142         
44143         if(this.rendered){
44144             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44145             this.validate();
44146         }
44147     },
44148     
44149     setValue : function(v)
44150     {
44151         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44152         
44153         this.value = v;
44154         
44155         if(this.rendered){
44156             
44157             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44158             
44159             this.inputEl().dom.value = (v == '') ? '' :
44160                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44161             
44162             if(!this.allowZero && v === '0') {
44163                 this.hiddenEl().dom.value = '';
44164                 this.inputEl().dom.value = '';
44165             }
44166             
44167             this.validate();
44168         }
44169     },
44170     
44171     getRawValue : function()
44172     {
44173         var v = this.inputEl().getValue();
44174         
44175         return v;
44176     },
44177     
44178     getValue : function()
44179     {
44180         return this.fixPrecision(this.parseValue(this.getRawValue()));
44181     },
44182     
44183     parseValue : function(value)
44184     {
44185         if(this.thousandsDelimiter) {
44186             value += "";
44187             r = new RegExp(",", "g");
44188             value = value.replace(r, "");
44189         }
44190         
44191         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44192         return isNaN(value) ? '' : value;
44193         
44194     },
44195     
44196     fixPrecision : function(value)
44197     {
44198         if(this.thousandsDelimiter) {
44199             value += "";
44200             r = new RegExp(",", "g");
44201             value = value.replace(r, "");
44202         }
44203         
44204         var nan = isNaN(value);
44205         
44206         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44207             return nan ? '' : value;
44208         }
44209         return parseFloat(value).toFixed(this.decimalPrecision);
44210     },
44211     
44212     decimalPrecisionFcn : function(v)
44213     {
44214         return Math.floor(v);
44215     },
44216     
44217     validateValue : function(value)
44218     {
44219         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44220             return false;
44221         }
44222         
44223         var num = this.parseValue(value);
44224         
44225         if(isNaN(num)){
44226             this.markInvalid(String.format(this.nanText, value));
44227             return false;
44228         }
44229         
44230         if(num < this.minValue){
44231             this.markInvalid(String.format(this.minText, this.minValue));
44232             return false;
44233         }
44234         
44235         if(num > this.maxValue){
44236             this.markInvalid(String.format(this.maxText, this.maxValue));
44237             return false;
44238         }
44239         
44240         return true;
44241     },
44242     
44243     validate : function()
44244     {
44245         if(this.disabled || this.allowBlank){
44246             this.markValid();
44247             return true;
44248         }
44249         
44250         var currency = this.getCurrency();
44251         
44252         if(this.validateValue(this.getRawValue()) && currency.length){
44253             this.markValid();
44254             return true;
44255         }
44256         
44257         this.markInvalid();
44258         return false;
44259     },
44260     
44261     getName: function()
44262     {
44263         return this.name;
44264     },
44265     
44266     beforeBlur : function()
44267     {
44268         if(!this.castInt){
44269             return;
44270         }
44271         
44272         var v = this.parseValue(this.getRawValue());
44273         
44274         if(v || v == 0){
44275             this.setValue(v);
44276         }
44277     },
44278     
44279     onBlur : function()
44280     {
44281         this.beforeBlur();
44282         
44283         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44284             //this.el.removeClass(this.focusClass);
44285         }
44286         
44287         this.hasFocus = false;
44288         
44289         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44290             this.validate();
44291         }
44292         
44293         var v = this.getValue();
44294         
44295         if(String(v) !== String(this.startValue)){
44296             this.fireEvent('change', this, v, this.startValue);
44297         }
44298         
44299         this.fireEvent("blur", this);
44300     },
44301     
44302     inputEl : function()
44303     {
44304         return this.el.select('.roo-money-amount-input', true).first();
44305     },
44306     
44307     currencyEl : function()
44308     {
44309         return this.el.select('.roo-money-currency-input', true).first();
44310     },
44311     
44312     hiddenEl : function()
44313     {
44314         return this.el.select('input.hidden-number-input',true).first();
44315     }
44316     
44317 });/**
44318  * @class Roo.bootstrap.BezierSignature
44319  * @extends Roo.bootstrap.Component
44320  * Bootstrap BezierSignature class
44321  * This script refer to:
44322  *    Title: Signature Pad
44323  *    Author: szimek
44324  *    Availability: https://github.com/szimek/signature_pad
44325  *
44326  * @constructor
44327  * Create a new BezierSignature
44328  * @param {Object} config The config object
44329  */
44330
44331 Roo.bootstrap.BezierSignature = function(config){
44332     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44333     this.addEvents({
44334         "resize" : true
44335     });
44336 };
44337
44338 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44339 {
44340      
44341     curve_data: [],
44342     
44343     is_empty: true,
44344     
44345     mouse_btn_down: true,
44346     
44347     /**
44348      * @cfg {int} canvas height
44349      */
44350     canvas_height: '200px',
44351     
44352     /**
44353      * @cfg {float|function} Radius of a single dot.
44354      */ 
44355     dot_size: false,
44356     
44357     /**
44358      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44359      */
44360     min_width: 0.5,
44361     
44362     /**
44363      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44364      */
44365     max_width: 2.5,
44366     
44367     /**
44368      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44369      */
44370     throttle: 16,
44371     
44372     /**
44373      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44374      */
44375     min_distance: 5,
44376     
44377     /**
44378      * @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.
44379      */
44380     bg_color: 'rgba(0, 0, 0, 0)',
44381     
44382     /**
44383      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44384      */
44385     dot_color: 'black',
44386     
44387     /**
44388      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44389      */ 
44390     velocity_filter_weight: 0.7,
44391     
44392     /**
44393      * @cfg {function} Callback when stroke begin. 
44394      */
44395     onBegin: false,
44396     
44397     /**
44398      * @cfg {function} Callback when stroke end.
44399      */
44400     onEnd: false,
44401     
44402     getAutoCreate : function()
44403     {
44404         var cls = 'roo-signature column';
44405         
44406         if(this.cls){
44407             cls += ' ' + this.cls;
44408         }
44409         
44410         var col_sizes = [
44411             'lg',
44412             'md',
44413             'sm',
44414             'xs'
44415         ];
44416         
44417         for(var i = 0; i < col_sizes.length; i++) {
44418             if(this[col_sizes[i]]) {
44419                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44420             }
44421         }
44422         
44423         var cfg = {
44424             tag: 'div',
44425             cls: cls,
44426             cn: [
44427                 {
44428                     tag: 'div',
44429                     cls: 'roo-signature-body',
44430                     cn: [
44431                         {
44432                             tag: 'canvas',
44433                             cls: 'roo-signature-body-canvas',
44434                             height: this.canvas_height,
44435                             width: this.canvas_width
44436                         }
44437                     ]
44438                 },
44439                 {
44440                     tag: 'input',
44441                     type: 'file',
44442                     style: 'display: none'
44443                 }
44444             ]
44445         };
44446         
44447         return cfg;
44448     },
44449     
44450     initEvents: function() 
44451     {
44452         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44453         
44454         var canvas = this.canvasEl();
44455         
44456         // mouse && touch event swapping...
44457         canvas.dom.style.touchAction = 'none';
44458         canvas.dom.style.msTouchAction = 'none';
44459         
44460         this.mouse_btn_down = false;
44461         canvas.on('mousedown', this._handleMouseDown, this);
44462         canvas.on('mousemove', this._handleMouseMove, this);
44463         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44464         
44465         if (window.PointerEvent) {
44466             canvas.on('pointerdown', this._handleMouseDown, this);
44467             canvas.on('pointermove', this._handleMouseMove, this);
44468             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44469         }
44470         
44471         if ('ontouchstart' in window) {
44472             canvas.on('touchstart', this._handleTouchStart, this);
44473             canvas.on('touchmove', this._handleTouchMove, this);
44474             canvas.on('touchend', this._handleTouchEnd, this);
44475         }
44476         
44477         Roo.EventManager.onWindowResize(this.resize, this, true);
44478         
44479         // file input event
44480         this.fileEl().on('change', this.uploadImage, this);
44481         
44482         this.clear();
44483         
44484         this.resize();
44485     },
44486     
44487     resize: function(){
44488         
44489         var canvas = this.canvasEl().dom;
44490         var ctx = this.canvasElCtx();
44491         var img_data = false;
44492         
44493         if(canvas.width > 0) {
44494             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44495         }
44496         // setting canvas width will clean img data
44497         canvas.width = 0;
44498         
44499         var style = window.getComputedStyle ? 
44500             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44501             
44502         var padding_left = parseInt(style.paddingLeft) || 0;
44503         var padding_right = parseInt(style.paddingRight) || 0;
44504         
44505         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44506         
44507         if(img_data) {
44508             ctx.putImageData(img_data, 0, 0);
44509         }
44510     },
44511     
44512     _handleMouseDown: function(e)
44513     {
44514         if (e.browserEvent.which === 1) {
44515             this.mouse_btn_down = true;
44516             this.strokeBegin(e);
44517         }
44518     },
44519     
44520     _handleMouseMove: function (e)
44521     {
44522         if (this.mouse_btn_down) {
44523             this.strokeMoveUpdate(e);
44524         }
44525     },
44526     
44527     _handleMouseUp: function (e)
44528     {
44529         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44530             this.mouse_btn_down = false;
44531             this.strokeEnd(e);
44532         }
44533     },
44534     
44535     _handleTouchStart: function (e) {
44536         
44537         e.preventDefault();
44538         if (e.browserEvent.targetTouches.length === 1) {
44539             // var touch = e.browserEvent.changedTouches[0];
44540             // this.strokeBegin(touch);
44541             
44542              this.strokeBegin(e); // assume e catching the correct xy...
44543         }
44544     },
44545     
44546     _handleTouchMove: function (e) {
44547         e.preventDefault();
44548         // var touch = event.targetTouches[0];
44549         // _this._strokeMoveUpdate(touch);
44550         this.strokeMoveUpdate(e);
44551     },
44552     
44553     _handleTouchEnd: function (e) {
44554         var wasCanvasTouched = e.target === this.canvasEl().dom;
44555         if (wasCanvasTouched) {
44556             e.preventDefault();
44557             // var touch = event.changedTouches[0];
44558             // _this._strokeEnd(touch);
44559             this.strokeEnd(e);
44560         }
44561     },
44562     
44563     reset: function () {
44564         this._lastPoints = [];
44565         this._lastVelocity = 0;
44566         this._lastWidth = (this.min_width + this.max_width) / 2;
44567         this.canvasElCtx().fillStyle = this.dot_color;
44568     },
44569     
44570     strokeMoveUpdate: function(e)
44571     {
44572         this.strokeUpdate(e);
44573         
44574         if (this.throttle) {
44575             this.throttleStroke(this.strokeUpdate, this.throttle);
44576         }
44577         else {
44578             this.strokeUpdate(e);
44579         }
44580     },
44581     
44582     strokeBegin: function(e)
44583     {
44584         var newPointGroup = {
44585             color: this.dot_color,
44586             points: []
44587         };
44588         
44589         if (typeof this.onBegin === 'function') {
44590             this.onBegin(e);
44591         }
44592         
44593         this.curve_data.push(newPointGroup);
44594         this.reset();
44595         this.strokeUpdate(e);
44596     },
44597     
44598     strokeUpdate: function(e)
44599     {
44600         var rect = this.canvasEl().dom.getBoundingClientRect();
44601         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44602         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44603         var lastPoints = lastPointGroup.points;
44604         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44605         var isLastPointTooClose = lastPoint
44606             ? point.distanceTo(lastPoint) <= this.min_distance
44607             : false;
44608         var color = lastPointGroup.color;
44609         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44610             var curve = this.addPoint(point);
44611             if (!lastPoint) {
44612                 this.drawDot({color: color, point: point});
44613             }
44614             else if (curve) {
44615                 this.drawCurve({color: color, curve: curve});
44616             }
44617             lastPoints.push({
44618                 time: point.time,
44619                 x: point.x,
44620                 y: point.y
44621             });
44622         }
44623     },
44624     
44625     strokeEnd: function(e)
44626     {
44627         this.strokeUpdate(e);
44628         if (typeof this.onEnd === 'function') {
44629             this.onEnd(e);
44630         }
44631     },
44632     
44633     addPoint:  function (point) {
44634         var _lastPoints = this._lastPoints;
44635         _lastPoints.push(point);
44636         if (_lastPoints.length > 2) {
44637             if (_lastPoints.length === 3) {
44638                 _lastPoints.unshift(_lastPoints[0]);
44639             }
44640             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44641             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44642             _lastPoints.shift();
44643             return curve;
44644         }
44645         return null;
44646     },
44647     
44648     calculateCurveWidths: function (startPoint, endPoint) {
44649         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44650             (1 - this.velocity_filter_weight) * this._lastVelocity;
44651
44652         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44653         var widths = {
44654             end: newWidth,
44655             start: this._lastWidth
44656         };
44657         
44658         this._lastVelocity = velocity;
44659         this._lastWidth = newWidth;
44660         return widths;
44661     },
44662     
44663     drawDot: function (_a) {
44664         var color = _a.color, point = _a.point;
44665         var ctx = this.canvasElCtx();
44666         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44667         ctx.beginPath();
44668         this.drawCurveSegment(point.x, point.y, width);
44669         ctx.closePath();
44670         ctx.fillStyle = color;
44671         ctx.fill();
44672     },
44673     
44674     drawCurve: function (_a) {
44675         var color = _a.color, curve = _a.curve;
44676         var ctx = this.canvasElCtx();
44677         var widthDelta = curve.endWidth - curve.startWidth;
44678         var drawSteps = Math.floor(curve.length()) * 2;
44679         ctx.beginPath();
44680         ctx.fillStyle = color;
44681         for (var i = 0; i < drawSteps; i += 1) {
44682         var t = i / drawSteps;
44683         var tt = t * t;
44684         var ttt = tt * t;
44685         var u = 1 - t;
44686         var uu = u * u;
44687         var uuu = uu * u;
44688         var x = uuu * curve.startPoint.x;
44689         x += 3 * uu * t * curve.control1.x;
44690         x += 3 * u * tt * curve.control2.x;
44691         x += ttt * curve.endPoint.x;
44692         var y = uuu * curve.startPoint.y;
44693         y += 3 * uu * t * curve.control1.y;
44694         y += 3 * u * tt * curve.control2.y;
44695         y += ttt * curve.endPoint.y;
44696         var width = curve.startWidth + ttt * widthDelta;
44697         this.drawCurveSegment(x, y, width);
44698         }
44699         ctx.closePath();
44700         ctx.fill();
44701     },
44702     
44703     drawCurveSegment: function (x, y, width) {
44704         var ctx = this.canvasElCtx();
44705         ctx.moveTo(x, y);
44706         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44707         this.is_empty = false;
44708     },
44709     
44710     clear: function()
44711     {
44712         var ctx = this.canvasElCtx();
44713         var canvas = this.canvasEl().dom;
44714         ctx.fillStyle = this.bg_color;
44715         ctx.clearRect(0, 0, canvas.width, canvas.height);
44716         ctx.fillRect(0, 0, canvas.width, canvas.height);
44717         this.curve_data = [];
44718         this.reset();
44719         this.is_empty = true;
44720     },
44721     
44722     fileEl: function()
44723     {
44724         return  this.el.select('input',true).first();
44725     },
44726     
44727     canvasEl: function()
44728     {
44729         return this.el.select('canvas',true).first();
44730     },
44731     
44732     canvasElCtx: function()
44733     {
44734         return this.el.select('canvas',true).first().dom.getContext('2d');
44735     },
44736     
44737     getImage: function(type)
44738     {
44739         if(this.is_empty) {
44740             return false;
44741         }
44742         
44743         // encryption ?
44744         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44745     },
44746     
44747     drawFromImage: function(img_src)
44748     {
44749         var img = new Image();
44750         
44751         img.onload = function(){
44752             this.canvasElCtx().drawImage(img, 0, 0);
44753         }.bind(this);
44754         
44755         img.src = img_src;
44756         
44757         this.is_empty = false;
44758     },
44759     
44760     selectImage: function()
44761     {
44762         this.fileEl().dom.click();
44763     },
44764     
44765     uploadImage: function(e)
44766     {
44767         var reader = new FileReader();
44768         
44769         reader.onload = function(e){
44770             var img = new Image();
44771             img.onload = function(){
44772                 this.reset();
44773                 this.canvasElCtx().drawImage(img, 0, 0);
44774             }.bind(this);
44775             img.src = e.target.result;
44776         }.bind(this);
44777         
44778         reader.readAsDataURL(e.target.files[0]);
44779     },
44780     
44781     // Bezier Point Constructor
44782     Point: (function () {
44783         function Point(x, y, time) {
44784             this.x = x;
44785             this.y = y;
44786             this.time = time || Date.now();
44787         }
44788         Point.prototype.distanceTo = function (start) {
44789             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44790         };
44791         Point.prototype.equals = function (other) {
44792             return this.x === other.x && this.y === other.y && this.time === other.time;
44793         };
44794         Point.prototype.velocityFrom = function (start) {
44795             return this.time !== start.time
44796             ? this.distanceTo(start) / (this.time - start.time)
44797             : 0;
44798         };
44799         return Point;
44800     }()),
44801     
44802     
44803     // Bezier Constructor
44804     Bezier: (function () {
44805         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44806             this.startPoint = startPoint;
44807             this.control2 = control2;
44808             this.control1 = control1;
44809             this.endPoint = endPoint;
44810             this.startWidth = startWidth;
44811             this.endWidth = endWidth;
44812         }
44813         Bezier.fromPoints = function (points, widths, scope) {
44814             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44815             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44816             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44817         };
44818         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44819             var dx1 = s1.x - s2.x;
44820             var dy1 = s1.y - s2.y;
44821             var dx2 = s2.x - s3.x;
44822             var dy2 = s2.y - s3.y;
44823             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44824             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44825             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44826             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44827             var dxm = m1.x - m2.x;
44828             var dym = m1.y - m2.y;
44829             var k = l2 / (l1 + l2);
44830             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44831             var tx = s2.x - cm.x;
44832             var ty = s2.y - cm.y;
44833             return {
44834                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44835                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44836             };
44837         };
44838         Bezier.prototype.length = function () {
44839             var steps = 10;
44840             var length = 0;
44841             var px;
44842             var py;
44843             for (var i = 0; i <= steps; i += 1) {
44844                 var t = i / steps;
44845                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44846                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44847                 if (i > 0) {
44848                     var xdiff = cx - px;
44849                     var ydiff = cy - py;
44850                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44851                 }
44852                 px = cx;
44853                 py = cy;
44854             }
44855             return length;
44856         };
44857         Bezier.prototype.point = function (t, start, c1, c2, end) {
44858             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44859             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44860             + (3.0 * c2 * (1.0 - t) * t * t)
44861             + (end * t * t * t);
44862         };
44863         return Bezier;
44864     }()),
44865     
44866     throttleStroke: function(fn, wait) {
44867       if (wait === void 0) { wait = 250; }
44868       var previous = 0;
44869       var timeout = null;
44870       var result;
44871       var storedContext;
44872       var storedArgs;
44873       var later = function () {
44874           previous = Date.now();
44875           timeout = null;
44876           result = fn.apply(storedContext, storedArgs);
44877           if (!timeout) {
44878               storedContext = null;
44879               storedArgs = [];
44880           }
44881       };
44882       return function wrapper() {
44883           var args = [];
44884           for (var _i = 0; _i < arguments.length; _i++) {
44885               args[_i] = arguments[_i];
44886           }
44887           var now = Date.now();
44888           var remaining = wait - (now - previous);
44889           storedContext = this;
44890           storedArgs = args;
44891           if (remaining <= 0 || remaining > wait) {
44892               if (timeout) {
44893                   clearTimeout(timeout);
44894                   timeout = null;
44895               }
44896               previous = now;
44897               result = fn.apply(storedContext, storedArgs);
44898               if (!timeout) {
44899                   storedContext = null;
44900                   storedArgs = [];
44901               }
44902           }
44903           else if (!timeout) {
44904               timeout = window.setTimeout(later, remaining);
44905           }
44906           return result;
44907       };
44908   }
44909   
44910 });
44911
44912  
44913
44914