initialize date if not already done when picking year
[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         
8274         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275             e.on('click', this.sort, this);
8276         }, this);
8277         
8278         this.mainBody.on("click", this.onClick, this);
8279         this.mainBody.on("dblclick", this.onDblClick, this);
8280         
8281         // why is this done????? = it breaks dialogs??
8282         //this.parent().el.setStyle('position', 'relative');
8283         
8284         
8285         if (this.footer) {
8286             this.footer.parentId = this.id;
8287             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8288             
8289             if(this.lazyLoad){
8290                 this.el.select('tfoot tr td').first().addClass('hide');
8291             }
8292         } 
8293         
8294         if(this.loadMask) {
8295             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8296         }
8297         
8298         this.store.on('load', this.onLoad, this);
8299         this.store.on('beforeload', this.onBeforeLoad, this);
8300         this.store.on('update', this.onUpdate, this);
8301         this.store.on('add', this.onAdd, this);
8302         this.store.on("clear", this.clear, this);
8303         
8304         this.el.on("contextmenu", this.onContextMenu, this);
8305         
8306         this.mainBody.on('scroll', this.onBodyScroll, this);
8307         
8308         this.cm.on("headerchange", this.onHeaderChange, this);
8309         
8310         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8311         
8312     },
8313     
8314     onContextMenu : function(e, t)
8315     {
8316         this.processEvent("contextmenu", e);
8317     },
8318     
8319     processEvent : function(name, e)
8320     {
8321         if (name != 'touchstart' ) {
8322             this.fireEvent(name, e);    
8323         }
8324         
8325         var t = e.getTarget();
8326         
8327         var cell = Roo.get(t);
8328         
8329         if(!cell){
8330             return;
8331         }
8332         
8333         if(cell.findParent('tfoot', false, true)){
8334             return;
8335         }
8336         
8337         if(cell.findParent('thead', false, true)){
8338             
8339             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340                 cell = Roo.get(t).findParent('th', false, true);
8341                 if (!cell) {
8342                     Roo.log("failed to find th in thead?");
8343                     Roo.log(e.getTarget());
8344                     return;
8345                 }
8346             }
8347             
8348             var cellIndex = cell.dom.cellIndex;
8349             
8350             var ename = name == 'touchstart' ? 'click' : name;
8351             this.fireEvent("header" + ename, this, cellIndex, e);
8352             
8353             return;
8354         }
8355         
8356         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357             cell = Roo.get(t).findParent('td', false, true);
8358             if (!cell) {
8359                 Roo.log("failed to find th in tbody?");
8360                 Roo.log(e.getTarget());
8361                 return;
8362             }
8363         }
8364         
8365         var row = cell.findParent('tr', false, true);
8366         var cellIndex = cell.dom.cellIndex;
8367         var rowIndex = row.dom.rowIndex - 1;
8368         
8369         if(row !== false){
8370             
8371             this.fireEvent("row" + name, this, rowIndex, e);
8372             
8373             if(cell !== false){
8374             
8375                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8376             }
8377         }
8378         
8379     },
8380     
8381     onMouseover : function(e, el)
8382     {
8383         var cell = Roo.get(el);
8384         
8385         if(!cell){
8386             return;
8387         }
8388         
8389         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390             cell = cell.findParent('td', false, true);
8391         }
8392         
8393         var row = cell.findParent('tr', false, true);
8394         var cellIndex = cell.dom.cellIndex;
8395         var rowIndex = row.dom.rowIndex - 1; // start from 0
8396         
8397         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8398         
8399     },
8400     
8401     onMouseout : function(e, el)
8402     {
8403         var cell = Roo.get(el);
8404         
8405         if(!cell){
8406             return;
8407         }
8408         
8409         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410             cell = cell.findParent('td', false, true);
8411         }
8412         
8413         var row = cell.findParent('tr', false, true);
8414         var cellIndex = cell.dom.cellIndex;
8415         var rowIndex = row.dom.rowIndex - 1; // start from 0
8416         
8417         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8418         
8419     },
8420     
8421     onClick : function(e, el)
8422     {
8423         var cell = Roo.get(el);
8424         
8425         if(!cell || (!this.cellSelection && !this.rowSelection)){
8426             return;
8427         }
8428         
8429         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430             cell = cell.findParent('td', false, true);
8431         }
8432         
8433         if(!cell || typeof(cell) == 'undefined'){
8434             return;
8435         }
8436         
8437         var row = cell.findParent('tr', false, true);
8438         
8439         if(!row || typeof(row) == 'undefined'){
8440             return;
8441         }
8442         
8443         var cellIndex = cell.dom.cellIndex;
8444         var rowIndex = this.getRowIndex(row);
8445         
8446         // why??? - should these not be based on SelectionModel?
8447         if(this.cellSelection){
8448             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8449         }
8450         
8451         if(this.rowSelection){
8452             this.fireEvent('rowclick', this, row, rowIndex, e);
8453         }
8454         
8455         
8456     },
8457         
8458     onDblClick : function(e,el)
8459     {
8460         var cell = Roo.get(el);
8461         
8462         if(!cell || (!this.cellSelection && !this.rowSelection)){
8463             return;
8464         }
8465         
8466         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467             cell = cell.findParent('td', false, true);
8468         }
8469         
8470         if(!cell || typeof(cell) == 'undefined'){
8471             return;
8472         }
8473         
8474         var row = cell.findParent('tr', false, true);
8475         
8476         if(!row || typeof(row) == 'undefined'){
8477             return;
8478         }
8479         
8480         var cellIndex = cell.dom.cellIndex;
8481         var rowIndex = this.getRowIndex(row);
8482         
8483         if(this.cellSelection){
8484             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8485         }
8486         
8487         if(this.rowSelection){
8488             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8489         }
8490     },
8491     
8492     sort : function(e,el)
8493     {
8494         var col = Roo.get(el);
8495         
8496         if(!col.hasClass('sortable')){
8497             return;
8498         }
8499         
8500         var sort = col.attr('sort');
8501         var dir = 'ASC';
8502         
8503         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8504             dir = 'DESC';
8505         }
8506         
8507         this.store.sortInfo = {field : sort, direction : dir};
8508         
8509         if (this.footer) {
8510             Roo.log("calling footer first");
8511             this.footer.onClick('first');
8512         } else {
8513         
8514             this.store.load({ params : { start : 0 } });
8515         }
8516     },
8517     
8518     renderHeader : function()
8519     {
8520         var header = {
8521             tag: 'thead',
8522             cn : []
8523         };
8524         
8525         var cm = this.cm;
8526         this.totalWidth = 0;
8527         
8528         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8529             
8530             var config = cm.config[i];
8531             
8532             var c = {
8533                 tag: 'th',
8534                 cls : 'x-hcol-' + i,
8535                 style : '',
8536                 
8537                 html: cm.getColumnHeader(i)
8538             };
8539             
8540             var tooltip = cm.getColumnTooltip(i);
8541             if (tooltip) {
8542                 c.tooltip = tooltip;
8543             }
8544             
8545             
8546             var hh = '';
8547             
8548             if(typeof(config.sortable) != 'undefined' && config.sortable){
8549                 c.cls = 'sortable';
8550                 c.html = '<i class="fa"></i>' + c.html;
8551             }
8552             
8553             // could use BS4 hidden-..-down 
8554             
8555             if(typeof(config.lgHeader) != 'undefined'){
8556                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8557             }
8558             
8559             if(typeof(config.mdHeader) != 'undefined'){
8560                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8561             }
8562             
8563             if(typeof(config.smHeader) != 'undefined'){
8564                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8565             }
8566             
8567             if(typeof(config.xsHeader) != 'undefined'){
8568                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8569             }
8570             
8571             if(hh.length){
8572                 c.html = hh;
8573             }
8574             
8575             if(typeof(config.tooltip) != 'undefined'){
8576                 c.tooltip = config.tooltip;
8577             }
8578             
8579             if(typeof(config.colspan) != 'undefined'){
8580                 c.colspan = config.colspan;
8581             }
8582             
8583             if(typeof(config.hidden) != 'undefined' && config.hidden){
8584                 c.style += ' display:none;';
8585             }
8586             
8587             if(typeof(config.dataIndex) != 'undefined'){
8588                 c.sort = config.dataIndex;
8589             }
8590             
8591            
8592             
8593             if(typeof(config.align) != 'undefined' && config.align.length){
8594                 c.style += ' text-align:' + config.align + ';';
8595             }
8596             
8597             if(typeof(config.width) != 'undefined'){
8598                 c.style += ' width:' + config.width + 'px;';
8599                 this.totalWidth += config.width;
8600             } else {
8601                 this.totalWidth += 100; // assume minimum of 100 per column?
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                  
8614                 if (!config[size]) { // 0 = hidden
8615                     // BS 4 '0' is treated as hide that column and below.
8616                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8617                     return;
8618                 }
8619                 
8620                 c.cls += ' col-' + size + '-' + config[size] + (
8621                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8622                 );
8623                 
8624                 
8625             });
8626             
8627             header.cn.push(c)
8628         }
8629         
8630         return header;
8631     },
8632     
8633     renderBody : function()
8634     {
8635         var body = {
8636             tag: 'tbody',
8637             cn : [
8638                 {
8639                     tag: 'tr',
8640                     cn : [
8641                         {
8642                             tag : 'td',
8643                             colspan :  this.cm.getColumnCount()
8644                         }
8645                     ]
8646                 }
8647             ]
8648         };
8649         
8650         return body;
8651     },
8652     
8653     renderFooter : function()
8654     {
8655         var footer = {
8656             tag: 'tfoot',
8657             cn : [
8658                 {
8659                     tag: 'tr',
8660                     cn : [
8661                         {
8662                             tag : 'td',
8663                             colspan :  this.cm.getColumnCount()
8664                         }
8665                     ]
8666                 }
8667             ]
8668         };
8669         
8670         return footer;
8671     },
8672     
8673     
8674     
8675     onLoad : function()
8676     {
8677 //        Roo.log('ds onload');
8678         this.clear();
8679         
8680         var _this = this;
8681         var cm = this.cm;
8682         var ds = this.store;
8683         
8684         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8685             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8686             if (_this.store.sortInfo) {
8687                     
8688                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8689                     e.select('i', true).addClass(['fa-arrow-up']);
8690                 }
8691                 
8692                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8693                     e.select('i', true).addClass(['fa-arrow-down']);
8694                 }
8695             }
8696         });
8697         
8698         var tbody =  this.mainBody;
8699               
8700         if(ds.getCount() > 0){
8701             ds.data.each(function(d,rowIndex){
8702                 var row =  this.renderRow(cm, ds, rowIndex);
8703                 
8704                 tbody.createChild(row);
8705                 
8706                 var _this = this;
8707                 
8708                 if(row.cellObjects.length){
8709                     Roo.each(row.cellObjects, function(r){
8710                         _this.renderCellObject(r);
8711                     })
8712                 }
8713                 
8714             }, this);
8715         }
8716         
8717         var tfoot = this.el.select('tfoot', true).first();
8718         
8719         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8720             
8721             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8722             
8723             var total = this.ds.getTotalCount();
8724             
8725             if(this.footer.pageSize < total){
8726                 this.mainFoot.show();
8727             }
8728         }
8729         
8730         Roo.each(this.el.select('tbody td', true).elements, function(e){
8731             e.on('mouseover', _this.onMouseover, _this);
8732         });
8733         
8734         Roo.each(this.el.select('tbody td', true).elements, function(e){
8735             e.on('mouseout', _this.onMouseout, _this);
8736         });
8737         this.fireEvent('rowsrendered', this);
8738         
8739         this.autoSize();
8740     },
8741     
8742     
8743     onUpdate : function(ds,record)
8744     {
8745         this.refreshRow(record);
8746         this.autoSize();
8747     },
8748     
8749     onRemove : function(ds, record, index, isUpdate){
8750         if(isUpdate !== true){
8751             this.fireEvent("beforerowremoved", this, index, record);
8752         }
8753         var bt = this.mainBody.dom;
8754         
8755         var rows = this.el.select('tbody > tr', true).elements;
8756         
8757         if(typeof(rows[index]) != 'undefined'){
8758             bt.removeChild(rows[index].dom);
8759         }
8760         
8761 //        if(bt.rows[index]){
8762 //            bt.removeChild(bt.rows[index]);
8763 //        }
8764         
8765         if(isUpdate !== true){
8766             //this.stripeRows(index);
8767             //this.syncRowHeights(index, index);
8768             //this.layout();
8769             this.fireEvent("rowremoved", this, index, record);
8770         }
8771     },
8772     
8773     onAdd : function(ds, records, rowIndex)
8774     {
8775         //Roo.log('on Add called');
8776         // - note this does not handle multiple adding very well..
8777         var bt = this.mainBody.dom;
8778         for (var i =0 ; i < records.length;i++) {
8779             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8780             //Roo.log(records[i]);
8781             //Roo.log(this.store.getAt(rowIndex+i));
8782             this.insertRow(this.store, rowIndex + i, false);
8783             return;
8784         }
8785         
8786     },
8787     
8788     
8789     refreshRow : function(record){
8790         var ds = this.store, index;
8791         if(typeof record == 'number'){
8792             index = record;
8793             record = ds.getAt(index);
8794         }else{
8795             index = ds.indexOf(record);
8796             if (index < 0) {
8797                 return; // should not happen - but seems to 
8798             }
8799         }
8800         this.insertRow(ds, index, true);
8801         this.autoSize();
8802         this.onRemove(ds, record, index+1, true);
8803         this.autoSize();
8804         //this.syncRowHeights(index, index);
8805         //this.layout();
8806         this.fireEvent("rowupdated", this, index, record);
8807     },
8808     
8809     insertRow : function(dm, rowIndex, isUpdate){
8810         
8811         if(!isUpdate){
8812             this.fireEvent("beforerowsinserted", this, rowIndex);
8813         }
8814             //var s = this.getScrollState();
8815         var row = this.renderRow(this.cm, this.store, rowIndex);
8816         // insert before rowIndex..
8817         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8818         
8819         var _this = this;
8820                 
8821         if(row.cellObjects.length){
8822             Roo.each(row.cellObjects, function(r){
8823                 _this.renderCellObject(r);
8824             })
8825         }
8826             
8827         if(!isUpdate){
8828             this.fireEvent("rowsinserted", this, rowIndex);
8829             //this.syncRowHeights(firstRow, lastRow);
8830             //this.stripeRows(firstRow);
8831             //this.layout();
8832         }
8833         
8834     },
8835     
8836     
8837     getRowDom : function(rowIndex)
8838     {
8839         var rows = this.el.select('tbody > tr', true).elements;
8840         
8841         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8842         
8843     },
8844     // returns the object tree for a tr..
8845   
8846     
8847     renderRow : function(cm, ds, rowIndex) 
8848     {
8849         var d = ds.getAt(rowIndex);
8850         
8851         var row = {
8852             tag : 'tr',
8853             cls : 'x-row-' + rowIndex,
8854             cn : []
8855         };
8856             
8857         var cellObjects = [];
8858         
8859         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8860             var config = cm.config[i];
8861             
8862             var renderer = cm.getRenderer(i);
8863             var value = '';
8864             var id = false;
8865             
8866             if(typeof(renderer) !== 'undefined'){
8867                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8868             }
8869             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8870             // and are rendered into the cells after the row is rendered - using the id for the element.
8871             
8872             if(typeof(value) === 'object'){
8873                 id = Roo.id();
8874                 cellObjects.push({
8875                     container : id,
8876                     cfg : value 
8877                 })
8878             }
8879             
8880             var rowcfg = {
8881                 record: d,
8882                 rowIndex : rowIndex,
8883                 colIndex : i,
8884                 rowClass : ''
8885             };
8886
8887             this.fireEvent('rowclass', this, rowcfg);
8888             
8889             var td = {
8890                 tag: 'td',
8891                 // this might end up displaying HTML?
8892                 // this is too messy... - better to only do it on columsn you know are going to be too long
8893                 //tooltip : (typeof(value) === 'object') ? '' : value,
8894                 cls : rowcfg.rowClass + ' x-col-' + i,
8895                 style: '',
8896                 html: (typeof(value) === 'object') ? '' : value
8897             };
8898             
8899             if (id) {
8900                 td.id = id;
8901             }
8902             
8903             if(typeof(config.colspan) != 'undefined'){
8904                 td.colspan = config.colspan;
8905             }
8906             
8907             if(typeof(config.hidden) != 'undefined' && config.hidden){
8908                 td.style += ' display:none;';
8909             }
8910             
8911             if(typeof(config.align) != 'undefined' && config.align.length){
8912                 td.style += ' text-align:' + config.align + ';';
8913             }
8914             if(typeof(config.valign) != 'undefined' && config.valign.length){
8915                 td.style += ' vertical-align:' + config.valign + ';';
8916             }
8917             
8918             if(typeof(config.width) != 'undefined'){
8919                 td.style += ' width:' +  config.width + 'px;';
8920             }
8921             
8922             if(typeof(config.cursor) != 'undefined'){
8923                 td.style += ' cursor:' +  config.cursor + ';';
8924             }
8925             
8926             if(typeof(config.cls) != 'undefined'){
8927                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8928             }
8929             
8930             ['xs','sm','md','lg'].map(function(size){
8931                 
8932                 if(typeof(config[size]) == 'undefined'){
8933                     return;
8934                 }
8935                 
8936                 
8937                   
8938                 if (!config[size]) { // 0 = hidden
8939                     // BS 4 '0' is treated as hide that column and below.
8940                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8941                     return;
8942                 }
8943                 
8944                 td.cls += ' col-' + size + '-' + config[size] + (
8945                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8946                 );
8947                  
8948
8949             });
8950             
8951             row.cn.push(td);
8952            
8953         }
8954         
8955         row.cellObjects = cellObjects;
8956         
8957         return row;
8958           
8959     },
8960     
8961     
8962     
8963     onBeforeLoad : function()
8964     {
8965         
8966     },
8967      /**
8968      * Remove all rows
8969      */
8970     clear : function()
8971     {
8972         this.el.select('tbody', true).first().dom.innerHTML = '';
8973     },
8974     /**
8975      * Show or hide a row.
8976      * @param {Number} rowIndex to show or hide
8977      * @param {Boolean} state hide
8978      */
8979     setRowVisibility : function(rowIndex, state)
8980     {
8981         var bt = this.mainBody.dom;
8982         
8983         var rows = this.el.select('tbody > tr', true).elements;
8984         
8985         if(typeof(rows[rowIndex]) == 'undefined'){
8986             return;
8987         }
8988         rows[rowIndex].dom.style.display = state ? '' : 'none';
8989     },
8990     
8991     
8992     getSelectionModel : function(){
8993         if(!this.selModel){
8994             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8995         }
8996         return this.selModel;
8997     },
8998     /*
8999      * Render the Roo.bootstrap object from renderder
9000      */
9001     renderCellObject : function(r)
9002     {
9003         var _this = this;
9004         
9005         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9006         
9007         var t = r.cfg.render(r.container);
9008         
9009         if(r.cfg.cn){
9010             Roo.each(r.cfg.cn, function(c){
9011                 var child = {
9012                     container: t.getChildContainer(),
9013                     cfg: c
9014                 };
9015                 _this.renderCellObject(child);
9016             })
9017         }
9018     },
9019     
9020     getRowIndex : function(row)
9021     {
9022         var rowIndex = -1;
9023         
9024         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9025             if(el != row){
9026                 return;
9027             }
9028             
9029             rowIndex = index;
9030         });
9031         
9032         return rowIndex;
9033     },
9034      /**
9035      * Returns the grid's underlying element = used by panel.Grid
9036      * @return {Element} The element
9037      */
9038     getGridEl : function(){
9039         return this.el;
9040     },
9041      /**
9042      * Forces a resize - used by panel.Grid
9043      * @return {Element} The element
9044      */
9045     autoSize : function()
9046     {
9047         //var ctr = Roo.get(this.container.dom.parentElement);
9048         var ctr = Roo.get(this.el.dom);
9049         
9050         var thd = this.getGridEl().select('thead',true).first();
9051         var tbd = this.getGridEl().select('tbody', true).first();
9052         var tfd = this.getGridEl().select('tfoot', true).first();
9053         
9054         var cw = ctr.getWidth();
9055         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9056         
9057         if (tbd) {
9058             
9059             tbd.setWidth(ctr.getWidth());
9060             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9061             // this needs fixing for various usage - currently only hydra job advers I think..
9062             //tdb.setHeight(
9063             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9064             //); 
9065             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9066             cw -= barsize;
9067         }
9068         cw = Math.max(cw, this.totalWidth);
9069         this.getGridEl().select('tbody tr',true).setWidth(cw);
9070         
9071         // resize 'expandable coloumn?
9072         
9073         return; // we doe not have a view in this design..
9074         
9075     },
9076     onBodyScroll: function()
9077     {
9078         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9079         if(this.mainHead){
9080             this.mainHead.setStyle({
9081                 'position' : 'relative',
9082                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9083             });
9084         }
9085         
9086         if(this.lazyLoad){
9087             
9088             var scrollHeight = this.mainBody.dom.scrollHeight;
9089             
9090             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9091             
9092             var height = this.mainBody.getHeight();
9093             
9094             if(scrollHeight - height == scrollTop) {
9095                 
9096                 var total = this.ds.getTotalCount();
9097                 
9098                 if(this.footer.cursor + this.footer.pageSize < total){
9099                     
9100                     this.footer.ds.load({
9101                         params : {
9102                             start : this.footer.cursor + this.footer.pageSize,
9103                             limit : this.footer.pageSize
9104                         },
9105                         add : true
9106                     });
9107                 }
9108             }
9109             
9110         }
9111     },
9112     
9113     onHeaderChange : function()
9114     {
9115         var header = this.renderHeader();
9116         var table = this.el.select('table', true).first();
9117         
9118         this.mainHead.remove();
9119         this.mainHead = table.createChild(header, this.mainBody, false);
9120         
9121         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9122             e.on('click', this.sort, this);
9123         }, this);
9124         
9125         
9126     },
9127     
9128     onHiddenChange : function(colModel, colIndex, hidden)
9129     {
9130         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9131         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9132         
9133         this.CSS.updateRule(thSelector, "display", "");
9134         this.CSS.updateRule(tdSelector, "display", "");
9135         
9136         if(hidden){
9137             this.CSS.updateRule(thSelector, "display", "none");
9138             this.CSS.updateRule(tdSelector, "display", "none");
9139         }
9140         
9141         this.onHeaderChange();
9142         this.onLoad();
9143     },
9144     
9145     setColumnWidth: function(col_index, width)
9146     {
9147         // width = "md-2 xs-2..."
9148         if(!this.colModel.config[col_index]) {
9149             return;
9150         }
9151         
9152         var w = width.split(" ");
9153         
9154         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9155         
9156         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9157         
9158         
9159         for(var j = 0; j < w.length; j++) {
9160             
9161             if(!w[j]) {
9162                 continue;
9163             }
9164             
9165             var size_cls = w[j].split("-");
9166             
9167             if(!Number.isInteger(size_cls[1] * 1)) {
9168                 continue;
9169             }
9170             
9171             if(!this.colModel.config[col_index][size_cls[0]]) {
9172                 continue;
9173             }
9174             
9175             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9176                 continue;
9177             }
9178             
9179             h_row[0].classList.replace(
9180                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9181                 "col-"+size_cls[0]+"-"+size_cls[1]
9182             );
9183             
9184             for(var i = 0; i < rows.length; i++) {
9185                 
9186                 var size_cls = w[j].split("-");
9187                 
9188                 if(!Number.isInteger(size_cls[1] * 1)) {
9189                     continue;
9190                 }
9191                 
9192                 if(!this.colModel.config[col_index][size_cls[0]]) {
9193                     continue;
9194                 }
9195                 
9196                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9197                     continue;
9198                 }
9199                 
9200                 rows[i].classList.replace(
9201                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9202                     "col-"+size_cls[0]+"-"+size_cls[1]
9203                 );
9204             }
9205             
9206             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9207         }
9208     }
9209 });
9210
9211  
9212
9213  /*
9214  * - LGPL
9215  *
9216  * table cell
9217  * 
9218  */
9219
9220 /**
9221  * @class Roo.bootstrap.TableCell
9222  * @extends Roo.bootstrap.Component
9223  * Bootstrap TableCell class
9224  * @cfg {String} html cell contain text
9225  * @cfg {String} cls cell class
9226  * @cfg {String} tag cell tag (td|th) default td
9227  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9228  * @cfg {String} align Aligns the content in a cell
9229  * @cfg {String} axis Categorizes cells
9230  * @cfg {String} bgcolor Specifies the background color of a cell
9231  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9232  * @cfg {Number} colspan Specifies the number of columns a cell should span
9233  * @cfg {String} headers Specifies one or more header cells a cell is related to
9234  * @cfg {Number} height Sets the height of a cell
9235  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9236  * @cfg {Number} rowspan Sets the number of rows a cell should span
9237  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9238  * @cfg {String} valign Vertical aligns the content in a cell
9239  * @cfg {Number} width Specifies the width of a cell
9240  * 
9241  * @constructor
9242  * Create a new TableCell
9243  * @param {Object} config The config object
9244  */
9245
9246 Roo.bootstrap.TableCell = function(config){
9247     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9248 };
9249
9250 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9251     
9252     html: false,
9253     cls: false,
9254     tag: false,
9255     abbr: false,
9256     align: false,
9257     axis: false,
9258     bgcolor: false,
9259     charoff: false,
9260     colspan: false,
9261     headers: false,
9262     height: false,
9263     nowrap: false,
9264     rowspan: false,
9265     scope: false,
9266     valign: false,
9267     width: false,
9268     
9269     
9270     getAutoCreate : function(){
9271         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9272         
9273         cfg = {
9274             tag: 'td'
9275         };
9276         
9277         if(this.tag){
9278             cfg.tag = this.tag;
9279         }
9280         
9281         if (this.html) {
9282             cfg.html=this.html
9283         }
9284         if (this.cls) {
9285             cfg.cls=this.cls
9286         }
9287         if (this.abbr) {
9288             cfg.abbr=this.abbr
9289         }
9290         if (this.align) {
9291             cfg.align=this.align
9292         }
9293         if (this.axis) {
9294             cfg.axis=this.axis
9295         }
9296         if (this.bgcolor) {
9297             cfg.bgcolor=this.bgcolor
9298         }
9299         if (this.charoff) {
9300             cfg.charoff=this.charoff
9301         }
9302         if (this.colspan) {
9303             cfg.colspan=this.colspan
9304         }
9305         if (this.headers) {
9306             cfg.headers=this.headers
9307         }
9308         if (this.height) {
9309             cfg.height=this.height
9310         }
9311         if (this.nowrap) {
9312             cfg.nowrap=this.nowrap
9313         }
9314         if (this.rowspan) {
9315             cfg.rowspan=this.rowspan
9316         }
9317         if (this.scope) {
9318             cfg.scope=this.scope
9319         }
9320         if (this.valign) {
9321             cfg.valign=this.valign
9322         }
9323         if (this.width) {
9324             cfg.width=this.width
9325         }
9326         
9327         
9328         return cfg;
9329     }
9330    
9331 });
9332
9333  
9334
9335  /*
9336  * - LGPL
9337  *
9338  * table row
9339  * 
9340  */
9341
9342 /**
9343  * @class Roo.bootstrap.TableRow
9344  * @extends Roo.bootstrap.Component
9345  * Bootstrap TableRow class
9346  * @cfg {String} cls row class
9347  * @cfg {String} align Aligns the content in a table row
9348  * @cfg {String} bgcolor Specifies a background color for a table row
9349  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9350  * @cfg {String} valign Vertical aligns the content in a table row
9351  * 
9352  * @constructor
9353  * Create a new TableRow
9354  * @param {Object} config The config object
9355  */
9356
9357 Roo.bootstrap.TableRow = function(config){
9358     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9359 };
9360
9361 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9362     
9363     cls: false,
9364     align: false,
9365     bgcolor: false,
9366     charoff: false,
9367     valign: false,
9368     
9369     getAutoCreate : function(){
9370         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9371         
9372         cfg = {
9373             tag: 'tr'
9374         };
9375             
9376         if(this.cls){
9377             cfg.cls = this.cls;
9378         }
9379         if(this.align){
9380             cfg.align = this.align;
9381         }
9382         if(this.bgcolor){
9383             cfg.bgcolor = this.bgcolor;
9384         }
9385         if(this.charoff){
9386             cfg.charoff = this.charoff;
9387         }
9388         if(this.valign){
9389             cfg.valign = this.valign;
9390         }
9391         
9392         return cfg;
9393     }
9394    
9395 });
9396
9397  
9398
9399  /*
9400  * - LGPL
9401  *
9402  * table body
9403  * 
9404  */
9405
9406 /**
9407  * @class Roo.bootstrap.TableBody
9408  * @extends Roo.bootstrap.Component
9409  * Bootstrap TableBody class
9410  * @cfg {String} cls element class
9411  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9412  * @cfg {String} align Aligns the content inside the element
9413  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9414  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9415  * 
9416  * @constructor
9417  * Create a new TableBody
9418  * @param {Object} config The config object
9419  */
9420
9421 Roo.bootstrap.TableBody = function(config){
9422     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9423 };
9424
9425 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9426     
9427     cls: false,
9428     tag: false,
9429     align: false,
9430     charoff: false,
9431     valign: false,
9432     
9433     getAutoCreate : function(){
9434         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9435         
9436         cfg = {
9437             tag: 'tbody'
9438         };
9439             
9440         if (this.cls) {
9441             cfg.cls=this.cls
9442         }
9443         if(this.tag){
9444             cfg.tag = this.tag;
9445         }
9446         
9447         if(this.align){
9448             cfg.align = this.align;
9449         }
9450         if(this.charoff){
9451             cfg.charoff = this.charoff;
9452         }
9453         if(this.valign){
9454             cfg.valign = this.valign;
9455         }
9456         
9457         return cfg;
9458     }
9459     
9460     
9461 //    initEvents : function()
9462 //    {
9463 //        
9464 //        if(!this.store){
9465 //            return;
9466 //        }
9467 //        
9468 //        this.store = Roo.factory(this.store, Roo.data);
9469 //        this.store.on('load', this.onLoad, this);
9470 //        
9471 //        this.store.load();
9472 //        
9473 //    },
9474 //    
9475 //    onLoad: function () 
9476 //    {   
9477 //        this.fireEvent('load', this);
9478 //    }
9479 //    
9480 //   
9481 });
9482
9483  
9484
9485  /*
9486  * Based on:
9487  * Ext JS Library 1.1.1
9488  * Copyright(c) 2006-2007, Ext JS, LLC.
9489  *
9490  * Originally Released Under LGPL - original licence link has changed is not relivant.
9491  *
9492  * Fork - LGPL
9493  * <script type="text/javascript">
9494  */
9495
9496 // as we use this in bootstrap.
9497 Roo.namespace('Roo.form');
9498  /**
9499  * @class Roo.form.Action
9500  * Internal Class used to handle form actions
9501  * @constructor
9502  * @param {Roo.form.BasicForm} el The form element or its id
9503  * @param {Object} config Configuration options
9504  */
9505
9506  
9507  
9508 // define the action interface
9509 Roo.form.Action = function(form, options){
9510     this.form = form;
9511     this.options = options || {};
9512 };
9513 /**
9514  * Client Validation Failed
9515  * @const 
9516  */
9517 Roo.form.Action.CLIENT_INVALID = 'client';
9518 /**
9519  * Server Validation Failed
9520  * @const 
9521  */
9522 Roo.form.Action.SERVER_INVALID = 'server';
9523  /**
9524  * Connect to Server Failed
9525  * @const 
9526  */
9527 Roo.form.Action.CONNECT_FAILURE = 'connect';
9528 /**
9529  * Reading Data from Server Failed
9530  * @const 
9531  */
9532 Roo.form.Action.LOAD_FAILURE = 'load';
9533
9534 Roo.form.Action.prototype = {
9535     type : 'default',
9536     failureType : undefined,
9537     response : undefined,
9538     result : undefined,
9539
9540     // interface method
9541     run : function(options){
9542
9543     },
9544
9545     // interface method
9546     success : function(response){
9547
9548     },
9549
9550     // interface method
9551     handleResponse : function(response){
9552
9553     },
9554
9555     // default connection failure
9556     failure : function(response){
9557         
9558         this.response = response;
9559         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9560         this.form.afterAction(this, false);
9561     },
9562
9563     processResponse : function(response){
9564         this.response = response;
9565         if(!response.responseText){
9566             return true;
9567         }
9568         this.result = this.handleResponse(response);
9569         return this.result;
9570     },
9571
9572     // utility functions used internally
9573     getUrl : function(appendParams){
9574         var url = this.options.url || this.form.url || this.form.el.dom.action;
9575         if(appendParams){
9576             var p = this.getParams();
9577             if(p){
9578                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9579             }
9580         }
9581         return url;
9582     },
9583
9584     getMethod : function(){
9585         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9586     },
9587
9588     getParams : function(){
9589         var bp = this.form.baseParams;
9590         var p = this.options.params;
9591         if(p){
9592             if(typeof p == "object"){
9593                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9594             }else if(typeof p == 'string' && bp){
9595                 p += '&' + Roo.urlEncode(bp);
9596             }
9597         }else if(bp){
9598             p = Roo.urlEncode(bp);
9599         }
9600         return p;
9601     },
9602
9603     createCallback : function(){
9604         return {
9605             success: this.success,
9606             failure: this.failure,
9607             scope: this,
9608             timeout: (this.form.timeout*1000),
9609             upload: this.form.fileUpload ? this.success : undefined
9610         };
9611     }
9612 };
9613
9614 Roo.form.Action.Submit = function(form, options){
9615     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9616 };
9617
9618 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9619     type : 'submit',
9620
9621     haveProgress : false,
9622     uploadComplete : false,
9623     
9624     // uploadProgress indicator.
9625     uploadProgress : function()
9626     {
9627         if (!this.form.progressUrl) {
9628             return;
9629         }
9630         
9631         if (!this.haveProgress) {
9632             Roo.MessageBox.progress("Uploading", "Uploading");
9633         }
9634         if (this.uploadComplete) {
9635            Roo.MessageBox.hide();
9636            return;
9637         }
9638         
9639         this.haveProgress = true;
9640    
9641         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9642         
9643         var c = new Roo.data.Connection();
9644         c.request({
9645             url : this.form.progressUrl,
9646             params: {
9647                 id : uid
9648             },
9649             method: 'GET',
9650             success : function(req){
9651                //console.log(data);
9652                 var rdata = false;
9653                 var edata;
9654                 try  {
9655                    rdata = Roo.decode(req.responseText)
9656                 } catch (e) {
9657                     Roo.log("Invalid data from server..");
9658                     Roo.log(edata);
9659                     return;
9660                 }
9661                 if (!rdata || !rdata.success) {
9662                     Roo.log(rdata);
9663                     Roo.MessageBox.alert(Roo.encode(rdata));
9664                     return;
9665                 }
9666                 var data = rdata.data;
9667                 
9668                 if (this.uploadComplete) {
9669                    Roo.MessageBox.hide();
9670                    return;
9671                 }
9672                    
9673                 if (data){
9674                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9675                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9676                     );
9677                 }
9678                 this.uploadProgress.defer(2000,this);
9679             },
9680        
9681             failure: function(data) {
9682                 Roo.log('progress url failed ');
9683                 Roo.log(data);
9684             },
9685             scope : this
9686         });
9687            
9688     },
9689     
9690     
9691     run : function()
9692     {
9693         // run get Values on the form, so it syncs any secondary forms.
9694         this.form.getValues();
9695         
9696         var o = this.options;
9697         var method = this.getMethod();
9698         var isPost = method == 'POST';
9699         if(o.clientValidation === false || this.form.isValid()){
9700             
9701             if (this.form.progressUrl) {
9702                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9703                     (new Date() * 1) + '' + Math.random());
9704                     
9705             } 
9706             
9707             
9708             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9709                 form:this.form.el.dom,
9710                 url:this.getUrl(!isPost),
9711                 method: method,
9712                 params:isPost ? this.getParams() : null,
9713                 isUpload: this.form.fileUpload,
9714                 formData : this.form.formData
9715             }));
9716             
9717             this.uploadProgress();
9718
9719         }else if (o.clientValidation !== false){ // client validation failed
9720             this.failureType = Roo.form.Action.CLIENT_INVALID;
9721             this.form.afterAction(this, false);
9722         }
9723     },
9724
9725     success : function(response)
9726     {
9727         this.uploadComplete= true;
9728         if (this.haveProgress) {
9729             Roo.MessageBox.hide();
9730         }
9731         
9732         
9733         var result = this.processResponse(response);
9734         if(result === true || result.success){
9735             this.form.afterAction(this, true);
9736             return;
9737         }
9738         if(result.errors){
9739             this.form.markInvalid(result.errors);
9740             this.failureType = Roo.form.Action.SERVER_INVALID;
9741         }
9742         this.form.afterAction(this, false);
9743     },
9744     failure : function(response)
9745     {
9746         this.uploadComplete= true;
9747         if (this.haveProgress) {
9748             Roo.MessageBox.hide();
9749         }
9750         
9751         this.response = response;
9752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9753         this.form.afterAction(this, false);
9754     },
9755     
9756     handleResponse : function(response){
9757         if(this.form.errorReader){
9758             var rs = this.form.errorReader.read(response);
9759             var errors = [];
9760             if(rs.records){
9761                 for(var i = 0, len = rs.records.length; i < len; i++) {
9762                     var r = rs.records[i];
9763                     errors[i] = r.data;
9764                 }
9765             }
9766             if(errors.length < 1){
9767                 errors = null;
9768             }
9769             return {
9770                 success : rs.success,
9771                 errors : errors
9772             };
9773         }
9774         var ret = false;
9775         try {
9776             ret = Roo.decode(response.responseText);
9777         } catch (e) {
9778             ret = {
9779                 success: false,
9780                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9781                 errors : []
9782             };
9783         }
9784         return ret;
9785         
9786     }
9787 });
9788
9789
9790 Roo.form.Action.Load = function(form, options){
9791     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9792     this.reader = this.form.reader;
9793 };
9794
9795 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9796     type : 'load',
9797
9798     run : function(){
9799         
9800         Roo.Ajax.request(Roo.apply(
9801                 this.createCallback(), {
9802                     method:this.getMethod(),
9803                     url:this.getUrl(false),
9804                     params:this.getParams()
9805         }));
9806     },
9807
9808     success : function(response){
9809         
9810         var result = this.processResponse(response);
9811         if(result === true || !result.success || !result.data){
9812             this.failureType = Roo.form.Action.LOAD_FAILURE;
9813             this.form.afterAction(this, false);
9814             return;
9815         }
9816         this.form.clearInvalid();
9817         this.form.setValues(result.data);
9818         this.form.afterAction(this, true);
9819     },
9820
9821     handleResponse : function(response){
9822         if(this.form.reader){
9823             var rs = this.form.reader.read(response);
9824             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9825             return {
9826                 success : rs.success,
9827                 data : data
9828             };
9829         }
9830         return Roo.decode(response.responseText);
9831     }
9832 });
9833
9834 Roo.form.Action.ACTION_TYPES = {
9835     'load' : Roo.form.Action.Load,
9836     'submit' : Roo.form.Action.Submit
9837 };/*
9838  * - LGPL
9839  *
9840  * form
9841  *
9842  */
9843
9844 /**
9845  * @class Roo.bootstrap.Form
9846  * @extends Roo.bootstrap.Component
9847  * Bootstrap Form class
9848  * @cfg {String} method  GET | POST (default POST)
9849  * @cfg {String} labelAlign top | left (default top)
9850  * @cfg {String} align left  | right - for navbars
9851  * @cfg {Boolean} loadMask load mask when submit (default true)
9852
9853  *
9854  * @constructor
9855  * Create a new Form
9856  * @param {Object} config The config object
9857  */
9858
9859
9860 Roo.bootstrap.Form = function(config){
9861     
9862     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9863     
9864     Roo.bootstrap.Form.popover.apply();
9865     
9866     this.addEvents({
9867         /**
9868          * @event clientvalidation
9869          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9870          * @param {Form} this
9871          * @param {Boolean} valid true if the form has passed client-side validation
9872          */
9873         clientvalidation: true,
9874         /**
9875          * @event beforeaction
9876          * Fires before any action is performed. Return false to cancel the action.
9877          * @param {Form} this
9878          * @param {Action} action The action to be performed
9879          */
9880         beforeaction: true,
9881         /**
9882          * @event actionfailed
9883          * Fires when an action fails.
9884          * @param {Form} this
9885          * @param {Action} action The action that failed
9886          */
9887         actionfailed : true,
9888         /**
9889          * @event actioncomplete
9890          * Fires when an action is completed.
9891          * @param {Form} this
9892          * @param {Action} action The action that completed
9893          */
9894         actioncomplete : true
9895     });
9896 };
9897
9898 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9899
9900      /**
9901      * @cfg {String} method
9902      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9903      */
9904     method : 'POST',
9905     /**
9906      * @cfg {String} url
9907      * The URL to use for form actions if one isn't supplied in the action options.
9908      */
9909     /**
9910      * @cfg {Boolean} fileUpload
9911      * Set to true if this form is a file upload.
9912      */
9913
9914     /**
9915      * @cfg {Object} baseParams
9916      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9917      */
9918
9919     /**
9920      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9921      */
9922     timeout: 30,
9923     /**
9924      * @cfg {Sting} align (left|right) for navbar forms
9925      */
9926     align : 'left',
9927
9928     // private
9929     activeAction : null,
9930
9931     /**
9932      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9933      * element by passing it or its id or mask the form itself by passing in true.
9934      * @type Mixed
9935      */
9936     waitMsgTarget : false,
9937
9938     loadMask : true,
9939     
9940     /**
9941      * @cfg {Boolean} errorMask (true|false) default false
9942      */
9943     errorMask : false,
9944     
9945     /**
9946      * @cfg {Number} maskOffset Default 100
9947      */
9948     maskOffset : 100,
9949     
9950     /**
9951      * @cfg {Boolean} maskBody
9952      */
9953     maskBody : false,
9954
9955     getAutoCreate : function(){
9956
9957         var cfg = {
9958             tag: 'form',
9959             method : this.method || 'POST',
9960             id : this.id || Roo.id(),
9961             cls : ''
9962         };
9963         if (this.parent().xtype.match(/^Nav/)) {
9964             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9965
9966         }
9967
9968         if (this.labelAlign == 'left' ) {
9969             cfg.cls += ' form-horizontal';
9970         }
9971
9972
9973         return cfg;
9974     },
9975     initEvents : function()
9976     {
9977         this.el.on('submit', this.onSubmit, this);
9978         // this was added as random key presses on the form where triggering form submit.
9979         this.el.on('keypress', function(e) {
9980             if (e.getCharCode() != 13) {
9981                 return true;
9982             }
9983             // we might need to allow it for textareas.. and some other items.
9984             // check e.getTarget().
9985
9986             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9987                 return true;
9988             }
9989
9990             Roo.log("keypress blocked");
9991
9992             e.preventDefault();
9993             return false;
9994         });
9995         
9996     },
9997     // private
9998     onSubmit : function(e){
9999         e.stopEvent();
10000     },
10001
10002      /**
10003      * Returns true if client-side validation on the form is successful.
10004      * @return Boolean
10005      */
10006     isValid : function(){
10007         var items = this.getItems();
10008         var valid = true;
10009         var target = false;
10010         
10011         items.each(function(f){
10012             
10013             if(f.validate()){
10014                 return;
10015             }
10016             
10017             Roo.log('invalid field: ' + f.name);
10018             
10019             valid = false;
10020
10021             if(!target && f.el.isVisible(true)){
10022                 target = f;
10023             }
10024            
10025         });
10026         
10027         if(this.errorMask && !valid){
10028             Roo.bootstrap.Form.popover.mask(this, target);
10029         }
10030         
10031         return valid;
10032     },
10033     
10034     /**
10035      * Returns true if any fields in this form have changed since their original load.
10036      * @return Boolean
10037      */
10038     isDirty : function(){
10039         var dirty = false;
10040         var items = this.getItems();
10041         items.each(function(f){
10042            if(f.isDirty()){
10043                dirty = true;
10044                return false;
10045            }
10046            return true;
10047         });
10048         return dirty;
10049     },
10050      /**
10051      * Performs a predefined action (submit or load) or custom actions you define on this form.
10052      * @param {String} actionName The name of the action type
10053      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10054      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10055      * accept other config options):
10056      * <pre>
10057 Property          Type             Description
10058 ----------------  ---------------  ----------------------------------------------------------------------------------
10059 url               String           The url for the action (defaults to the form's url)
10060 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10061 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10062 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10063                                    validate the form on the client (defaults to false)
10064      * </pre>
10065      * @return {BasicForm} this
10066      */
10067     doAction : function(action, options){
10068         if(typeof action == 'string'){
10069             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10070         }
10071         if(this.fireEvent('beforeaction', this, action) !== false){
10072             this.beforeAction(action);
10073             action.run.defer(100, action);
10074         }
10075         return this;
10076     },
10077
10078     // private
10079     beforeAction : function(action){
10080         var o = action.options;
10081         
10082         if(this.loadMask){
10083             
10084             if(this.maskBody){
10085                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10086             } else {
10087                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10088             }
10089         }
10090         // not really supported yet.. ??
10091
10092         //if(this.waitMsgTarget === true){
10093         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10094         //}else if(this.waitMsgTarget){
10095         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10096         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10097         //}else {
10098         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10099        // }
10100
10101     },
10102
10103     // private
10104     afterAction : function(action, success){
10105         this.activeAction = null;
10106         var o = action.options;
10107
10108         if(this.loadMask){
10109             
10110             if(this.maskBody){
10111                 Roo.get(document.body).unmask();
10112             } else {
10113                 this.el.unmask();
10114             }
10115         }
10116         
10117         //if(this.waitMsgTarget === true){
10118 //            this.el.unmask();
10119         //}else if(this.waitMsgTarget){
10120         //    this.waitMsgTarget.unmask();
10121         //}else{
10122         //    Roo.MessageBox.updateProgress(1);
10123         //    Roo.MessageBox.hide();
10124        // }
10125         //
10126         if(success){
10127             if(o.reset){
10128                 this.reset();
10129             }
10130             Roo.callback(o.success, o.scope, [this, action]);
10131             this.fireEvent('actioncomplete', this, action);
10132
10133         }else{
10134
10135             // failure condition..
10136             // we have a scenario where updates need confirming.
10137             // eg. if a locking scenario exists..
10138             // we look for { errors : { needs_confirm : true }} in the response.
10139             if (
10140                 (typeof(action.result) != 'undefined')  &&
10141                 (typeof(action.result.errors) != 'undefined')  &&
10142                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10143            ){
10144                 var _t = this;
10145                 Roo.log("not supported yet");
10146                  /*
10147
10148                 Roo.MessageBox.confirm(
10149                     "Change requires confirmation",
10150                     action.result.errorMsg,
10151                     function(r) {
10152                         if (r != 'yes') {
10153                             return;
10154                         }
10155                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10156                     }
10157
10158                 );
10159                 */
10160
10161
10162                 return;
10163             }
10164
10165             Roo.callback(o.failure, o.scope, [this, action]);
10166             // show an error message if no failed handler is set..
10167             if (!this.hasListener('actionfailed')) {
10168                 Roo.log("need to add dialog support");
10169                 /*
10170                 Roo.MessageBox.alert("Error",
10171                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10172                         action.result.errorMsg :
10173                         "Saving Failed, please check your entries or try again"
10174                 );
10175                 */
10176             }
10177
10178             this.fireEvent('actionfailed', this, action);
10179         }
10180
10181     },
10182     /**
10183      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10184      * @param {String} id The value to search for
10185      * @return Field
10186      */
10187     findField : function(id){
10188         var items = this.getItems();
10189         var field = items.get(id);
10190         if(!field){
10191              items.each(function(f){
10192                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10193                     field = f;
10194                     return false;
10195                 }
10196                 return true;
10197             });
10198         }
10199         return field || null;
10200     },
10201      /**
10202      * Mark fields in this form invalid in bulk.
10203      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10204      * @return {BasicForm} this
10205      */
10206     markInvalid : function(errors){
10207         if(errors instanceof Array){
10208             for(var i = 0, len = errors.length; i < len; i++){
10209                 var fieldError = errors[i];
10210                 var f = this.findField(fieldError.id);
10211                 if(f){
10212                     f.markInvalid(fieldError.msg);
10213                 }
10214             }
10215         }else{
10216             var field, id;
10217             for(id in errors){
10218                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10219                     field.markInvalid(errors[id]);
10220                 }
10221             }
10222         }
10223         //Roo.each(this.childForms || [], function (f) {
10224         //    f.markInvalid(errors);
10225         //});
10226
10227         return this;
10228     },
10229
10230     /**
10231      * Set values for fields in this form in bulk.
10232      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10233      * @return {BasicForm} this
10234      */
10235     setValues : function(values){
10236         if(values instanceof Array){ // array of objects
10237             for(var i = 0, len = values.length; i < len; i++){
10238                 var v = values[i];
10239                 var f = this.findField(v.id);
10240                 if(f){
10241                     f.setValue(v.value);
10242                     if(this.trackResetOnLoad){
10243                         f.originalValue = f.getValue();
10244                     }
10245                 }
10246             }
10247         }else{ // object hash
10248             var field, id;
10249             for(id in values){
10250                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10251
10252                     if (field.setFromData &&
10253                         field.valueField &&
10254                         field.displayField &&
10255                         // combos' with local stores can
10256                         // be queried via setValue()
10257                         // to set their value..
10258                         (field.store && !field.store.isLocal)
10259                         ) {
10260                         // it's a combo
10261                         var sd = { };
10262                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10263                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10264                         field.setFromData(sd);
10265
10266                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10267                         
10268                         field.setFromData(values);
10269                         
10270                     } else {
10271                         field.setValue(values[id]);
10272                     }
10273
10274
10275                     if(this.trackResetOnLoad){
10276                         field.originalValue = field.getValue();
10277                     }
10278                 }
10279             }
10280         }
10281
10282         //Roo.each(this.childForms || [], function (f) {
10283         //    f.setValues(values);
10284         //});
10285
10286         return this;
10287     },
10288
10289     /**
10290      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10291      * they are returned as an array.
10292      * @param {Boolean} asString
10293      * @return {Object}
10294      */
10295     getValues : function(asString){
10296         //if (this.childForms) {
10297             // copy values from the child forms
10298         //    Roo.each(this.childForms, function (f) {
10299         //        this.setValues(f.getValues());
10300         //    }, this);
10301         //}
10302
10303
10304
10305         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10306         if(asString === true){
10307             return fs;
10308         }
10309         return Roo.urlDecode(fs);
10310     },
10311
10312     /**
10313      * Returns the fields in this form as an object with key/value pairs.
10314      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10315      * @return {Object}
10316      */
10317     getFieldValues : function(with_hidden)
10318     {
10319         var items = this.getItems();
10320         var ret = {};
10321         items.each(function(f){
10322             
10323             if (!f.getName()) {
10324                 return;
10325             }
10326             
10327             var v = f.getValue();
10328             
10329             if (f.inputType =='radio') {
10330                 if (typeof(ret[f.getName()]) == 'undefined') {
10331                     ret[f.getName()] = ''; // empty..
10332                 }
10333
10334                 if (!f.el.dom.checked) {
10335                     return;
10336
10337                 }
10338                 v = f.el.dom.value;
10339
10340             }
10341             
10342             if(f.xtype == 'MoneyField'){
10343                 ret[f.currencyName] = f.getCurrency();
10344             }
10345
10346             // not sure if this supported any more..
10347             if ((typeof(v) == 'object') && f.getRawValue) {
10348                 v = f.getRawValue() ; // dates..
10349             }
10350             // combo boxes where name != hiddenName...
10351             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10352                 ret[f.name] = f.getRawValue();
10353             }
10354             ret[f.getName()] = v;
10355         });
10356
10357         return ret;
10358     },
10359
10360     /**
10361      * Clears all invalid messages in this form.
10362      * @return {BasicForm} this
10363      */
10364     clearInvalid : function(){
10365         var items = this.getItems();
10366
10367         items.each(function(f){
10368            f.clearInvalid();
10369         });
10370
10371         return this;
10372     },
10373
10374     /**
10375      * Resets this form.
10376      * @return {BasicForm} this
10377      */
10378     reset : function(){
10379         var items = this.getItems();
10380         items.each(function(f){
10381             f.reset();
10382         });
10383
10384         Roo.each(this.childForms || [], function (f) {
10385             f.reset();
10386         });
10387
10388
10389         return this;
10390     },
10391     
10392     getItems : function()
10393     {
10394         var r=new Roo.util.MixedCollection(false, function(o){
10395             return o.id || (o.id = Roo.id());
10396         });
10397         var iter = function(el) {
10398             if (el.inputEl) {
10399                 r.add(el);
10400             }
10401             if (!el.items) {
10402                 return;
10403             }
10404             Roo.each(el.items,function(e) {
10405                 iter(e);
10406             });
10407         };
10408
10409         iter(this);
10410         return r;
10411     },
10412     
10413     hideFields : function(items)
10414     {
10415         Roo.each(items, function(i){
10416             
10417             var f = this.findField(i);
10418             
10419             if(!f){
10420                 return;
10421             }
10422             
10423             f.hide();
10424             
10425         }, this);
10426     },
10427     
10428     showFields : function(items)
10429     {
10430         Roo.each(items, function(i){
10431             
10432             var f = this.findField(i);
10433             
10434             if(!f){
10435                 return;
10436             }
10437             
10438             f.show();
10439             
10440         }, this);
10441     }
10442
10443 });
10444
10445 Roo.apply(Roo.bootstrap.Form, {
10446     
10447     popover : {
10448         
10449         padding : 5,
10450         
10451         isApplied : false,
10452         
10453         isMasked : false,
10454         
10455         form : false,
10456         
10457         target : false,
10458         
10459         toolTip : false,
10460         
10461         intervalID : false,
10462         
10463         maskEl : false,
10464         
10465         apply : function()
10466         {
10467             if(this.isApplied){
10468                 return;
10469             }
10470             
10471             this.maskEl = {
10472                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10473                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10474                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10475                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10476             };
10477             
10478             this.maskEl.top.enableDisplayMode("block");
10479             this.maskEl.left.enableDisplayMode("block");
10480             this.maskEl.bottom.enableDisplayMode("block");
10481             this.maskEl.right.enableDisplayMode("block");
10482             
10483             this.toolTip = new Roo.bootstrap.Tooltip({
10484                 cls : 'roo-form-error-popover',
10485                 alignment : {
10486                     'left' : ['r-l', [-2,0], 'right'],
10487                     'right' : ['l-r', [2,0], 'left'],
10488                     'bottom' : ['tl-bl', [0,2], 'top'],
10489                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10490                 }
10491             });
10492             
10493             this.toolTip.render(Roo.get(document.body));
10494
10495             this.toolTip.el.enableDisplayMode("block");
10496             
10497             Roo.get(document.body).on('click', function(){
10498                 this.unmask();
10499             }, this);
10500             
10501             Roo.get(document.body).on('touchstart', function(){
10502                 this.unmask();
10503             }, this);
10504             
10505             this.isApplied = true
10506         },
10507         
10508         mask : function(form, target)
10509         {
10510             this.form = form;
10511             
10512             this.target = target;
10513             
10514             if(!this.form.errorMask || !target.el){
10515                 return;
10516             }
10517             
10518             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10519             
10520             Roo.log(scrollable);
10521             
10522             var ot = this.target.el.calcOffsetsTo(scrollable);
10523             
10524             var scrollTo = ot[1] - this.form.maskOffset;
10525             
10526             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10527             
10528             scrollable.scrollTo('top', scrollTo);
10529             
10530             var box = this.target.el.getBox();
10531             Roo.log(box);
10532             var zIndex = Roo.bootstrap.Modal.zIndex++;
10533
10534             
10535             this.maskEl.top.setStyle('position', 'absolute');
10536             this.maskEl.top.setStyle('z-index', zIndex);
10537             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10538             this.maskEl.top.setLeft(0);
10539             this.maskEl.top.setTop(0);
10540             this.maskEl.top.show();
10541             
10542             this.maskEl.left.setStyle('position', 'absolute');
10543             this.maskEl.left.setStyle('z-index', zIndex);
10544             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10545             this.maskEl.left.setLeft(0);
10546             this.maskEl.left.setTop(box.y - this.padding);
10547             this.maskEl.left.show();
10548
10549             this.maskEl.bottom.setStyle('position', 'absolute');
10550             this.maskEl.bottom.setStyle('z-index', zIndex);
10551             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10552             this.maskEl.bottom.setLeft(0);
10553             this.maskEl.bottom.setTop(box.bottom + this.padding);
10554             this.maskEl.bottom.show();
10555
10556             this.maskEl.right.setStyle('position', 'absolute');
10557             this.maskEl.right.setStyle('z-index', zIndex);
10558             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10559             this.maskEl.right.setLeft(box.right + this.padding);
10560             this.maskEl.right.setTop(box.y - this.padding);
10561             this.maskEl.right.show();
10562
10563             this.toolTip.bindEl = this.target.el;
10564
10565             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10566
10567             var tip = this.target.blankText;
10568
10569             if(this.target.getValue() !== '' ) {
10570                 
10571                 if (this.target.invalidText.length) {
10572                     tip = this.target.invalidText;
10573                 } else if (this.target.regexText.length){
10574                     tip = this.target.regexText;
10575                 }
10576             }
10577
10578             this.toolTip.show(tip);
10579
10580             this.intervalID = window.setInterval(function() {
10581                 Roo.bootstrap.Form.popover.unmask();
10582             }, 10000);
10583
10584             window.onwheel = function(){ return false;};
10585             
10586             (function(){ this.isMasked = true; }).defer(500, this);
10587             
10588         },
10589         
10590         unmask : function()
10591         {
10592             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10593                 return;
10594             }
10595             
10596             this.maskEl.top.setStyle('position', 'absolute');
10597             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10598             this.maskEl.top.hide();
10599
10600             this.maskEl.left.setStyle('position', 'absolute');
10601             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10602             this.maskEl.left.hide();
10603
10604             this.maskEl.bottom.setStyle('position', 'absolute');
10605             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10606             this.maskEl.bottom.hide();
10607
10608             this.maskEl.right.setStyle('position', 'absolute');
10609             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10610             this.maskEl.right.hide();
10611             
10612             this.toolTip.hide();
10613             
10614             this.toolTip.el.hide();
10615             
10616             window.onwheel = function(){ return true;};
10617             
10618             if(this.intervalID){
10619                 window.clearInterval(this.intervalID);
10620                 this.intervalID = false;
10621             }
10622             
10623             this.isMasked = false;
10624             
10625         }
10626         
10627     }
10628     
10629 });
10630
10631 /*
10632  * Based on:
10633  * Ext JS Library 1.1.1
10634  * Copyright(c) 2006-2007, Ext JS, LLC.
10635  *
10636  * Originally Released Under LGPL - original licence link has changed is not relivant.
10637  *
10638  * Fork - LGPL
10639  * <script type="text/javascript">
10640  */
10641 /**
10642  * @class Roo.form.VTypes
10643  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10644  * @singleton
10645  */
10646 Roo.form.VTypes = function(){
10647     // closure these in so they are only created once.
10648     var alpha = /^[a-zA-Z_]+$/;
10649     var alphanum = /^[a-zA-Z0-9_]+$/;
10650     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10651     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10652
10653     // All these messages and functions are configurable
10654     return {
10655         /**
10656          * The function used to validate email addresses
10657          * @param {String} value The email address
10658          */
10659         'email' : function(v){
10660             return email.test(v);
10661         },
10662         /**
10663          * The error text to display when the email validation function returns false
10664          * @type String
10665          */
10666         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10667         /**
10668          * The keystroke filter mask to be applied on email input
10669          * @type RegExp
10670          */
10671         'emailMask' : /[a-z0-9_\.\-@]/i,
10672
10673         /**
10674          * The function used to validate URLs
10675          * @param {String} value The URL
10676          */
10677         'url' : function(v){
10678             return url.test(v);
10679         },
10680         /**
10681          * The error text to display when the url validation function returns false
10682          * @type String
10683          */
10684         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10685         
10686         /**
10687          * The function used to validate alpha values
10688          * @param {String} value The value
10689          */
10690         'alpha' : function(v){
10691             return alpha.test(v);
10692         },
10693         /**
10694          * The error text to display when the alpha validation function returns false
10695          * @type String
10696          */
10697         'alphaText' : 'This field should only contain letters and _',
10698         /**
10699          * The keystroke filter mask to be applied on alpha input
10700          * @type RegExp
10701          */
10702         'alphaMask' : /[a-z_]/i,
10703
10704         /**
10705          * The function used to validate alphanumeric values
10706          * @param {String} value The value
10707          */
10708         'alphanum' : function(v){
10709             return alphanum.test(v);
10710         },
10711         /**
10712          * The error text to display when the alphanumeric validation function returns false
10713          * @type String
10714          */
10715         'alphanumText' : 'This field should only contain letters, numbers and _',
10716         /**
10717          * The keystroke filter mask to be applied on alphanumeric input
10718          * @type RegExp
10719          */
10720         'alphanumMask' : /[a-z0-9_]/i
10721     };
10722 }();/*
10723  * - LGPL
10724  *
10725  * Input
10726  * 
10727  */
10728
10729 /**
10730  * @class Roo.bootstrap.Input
10731  * @extends Roo.bootstrap.Component
10732  * Bootstrap Input class
10733  * @cfg {Boolean} disabled is it disabled
10734  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10735  * @cfg {String} name name of the input
10736  * @cfg {string} fieldLabel - the label associated
10737  * @cfg {string} placeholder - placeholder to put in text.
10738  * @cfg {string}  before - input group add on before
10739  * @cfg {string} after - input group add on after
10740  * @cfg {string} size - (lg|sm) or leave empty..
10741  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10742  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10743  * @cfg {Number} md colspan out of 12 for computer-sized screens
10744  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10745  * @cfg {string} value default value of the input
10746  * @cfg {Number} labelWidth set the width of label 
10747  * @cfg {Number} labellg set the width of label (1-12)
10748  * @cfg {Number} labelmd set the width of label (1-12)
10749  * @cfg {Number} labelsm set the width of label (1-12)
10750  * @cfg {Number} labelxs set the width of label (1-12)
10751  * @cfg {String} labelAlign (top|left)
10752  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10753  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10754  * @cfg {String} indicatorpos (left|right) default left
10755  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10756  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10757  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10758
10759  * @cfg {String} align (left|center|right) Default left
10760  * @cfg {Boolean} forceFeedback (true|false) Default false
10761  * 
10762  * @constructor
10763  * Create a new Input
10764  * @param {Object} config The config object
10765  */
10766
10767 Roo.bootstrap.Input = function(config){
10768     
10769     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10770     
10771     this.addEvents({
10772         /**
10773          * @event focus
10774          * Fires when this field receives input focus.
10775          * @param {Roo.form.Field} this
10776          */
10777         focus : true,
10778         /**
10779          * @event blur
10780          * Fires when this field loses input focus.
10781          * @param {Roo.form.Field} this
10782          */
10783         blur : true,
10784         /**
10785          * @event specialkey
10786          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10787          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10788          * @param {Roo.form.Field} this
10789          * @param {Roo.EventObject} e The event object
10790          */
10791         specialkey : true,
10792         /**
10793          * @event change
10794          * Fires just before the field blurs if the field value has changed.
10795          * @param {Roo.form.Field} this
10796          * @param {Mixed} newValue The new value
10797          * @param {Mixed} oldValue The original value
10798          */
10799         change : true,
10800         /**
10801          * @event invalid
10802          * Fires after the field has been marked as invalid.
10803          * @param {Roo.form.Field} this
10804          * @param {String} msg The validation message
10805          */
10806         invalid : true,
10807         /**
10808          * @event valid
10809          * Fires after the field has been validated with no errors.
10810          * @param {Roo.form.Field} this
10811          */
10812         valid : true,
10813          /**
10814          * @event keyup
10815          * Fires after the key up
10816          * @param {Roo.form.Field} this
10817          * @param {Roo.EventObject}  e The event Object
10818          */
10819         keyup : true,
10820         /**
10821          * @event paste
10822          * Fires after the user pastes into input
10823          * @param {Roo.form.Field} this
10824          * @param {Roo.EventObject}  e The event Object
10825          */
10826         paste : true
10827     });
10828 };
10829
10830 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10831      /**
10832      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10833       automatic validation (defaults to "keyup").
10834      */
10835     validationEvent : "keyup",
10836      /**
10837      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10838      */
10839     validateOnBlur : true,
10840     /**
10841      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10842      */
10843     validationDelay : 250,
10844      /**
10845      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10846      */
10847     focusClass : "x-form-focus",  // not needed???
10848     
10849        
10850     /**
10851      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10852      */
10853     invalidClass : "has-warning",
10854     
10855     /**
10856      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10857      */
10858     validClass : "has-success",
10859     
10860     /**
10861      * @cfg {Boolean} hasFeedback (true|false) default true
10862      */
10863     hasFeedback : true,
10864     
10865     /**
10866      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10867      */
10868     invalidFeedbackClass : "glyphicon-warning-sign",
10869     
10870     /**
10871      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10872      */
10873     validFeedbackClass : "glyphicon-ok",
10874     
10875     /**
10876      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10877      */
10878     selectOnFocus : false,
10879     
10880      /**
10881      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10882      */
10883     maskRe : null,
10884        /**
10885      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10886      */
10887     vtype : null,
10888     
10889       /**
10890      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10891      */
10892     disableKeyFilter : false,
10893     
10894        /**
10895      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10896      */
10897     disabled : false,
10898      /**
10899      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10900      */
10901     allowBlank : true,
10902     /**
10903      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10904      */
10905     blankText : "Please complete this mandatory field",
10906     
10907      /**
10908      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10909      */
10910     minLength : 0,
10911     /**
10912      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10913      */
10914     maxLength : Number.MAX_VALUE,
10915     /**
10916      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10917      */
10918     minLengthText : "The minimum length for this field is {0}",
10919     /**
10920      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10921      */
10922     maxLengthText : "The maximum length for this field is {0}",
10923   
10924     
10925     /**
10926      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10927      * If available, this function will be called only after the basic validators all return true, and will be passed the
10928      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10929      */
10930     validator : null,
10931     /**
10932      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10933      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10934      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10935      */
10936     regex : null,
10937     /**
10938      * @cfg {String} regexText -- Depricated - use Invalid Text
10939      */
10940     regexText : "",
10941     
10942     /**
10943      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10944      */
10945     invalidText : "",
10946     
10947     
10948     
10949     autocomplete: false,
10950     
10951     
10952     fieldLabel : '',
10953     inputType : 'text',
10954     
10955     name : false,
10956     placeholder: false,
10957     before : false,
10958     after : false,
10959     size : false,
10960     hasFocus : false,
10961     preventMark: false,
10962     isFormField : true,
10963     value : '',
10964     labelWidth : 2,
10965     labelAlign : false,
10966     readOnly : false,
10967     align : false,
10968     formatedValue : false,
10969     forceFeedback : false,
10970     
10971     indicatorpos : 'left',
10972     
10973     labellg : 0,
10974     labelmd : 0,
10975     labelsm : 0,
10976     labelxs : 0,
10977     
10978     capture : '',
10979     accept : '',
10980     
10981     parentLabelAlign : function()
10982     {
10983         var parent = this;
10984         while (parent.parent()) {
10985             parent = parent.parent();
10986             if (typeof(parent.labelAlign) !='undefined') {
10987                 return parent.labelAlign;
10988             }
10989         }
10990         return 'left';
10991         
10992     },
10993     
10994     getAutoCreate : function()
10995     {
10996         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10997         
10998         var id = Roo.id();
10999         
11000         var cfg = {};
11001         
11002         if(this.inputType != 'hidden'){
11003             cfg.cls = 'form-group' //input-group
11004         }
11005         
11006         var input =  {
11007             tag: 'input',
11008             id : id,
11009             type : this.inputType,
11010             value : this.value,
11011             cls : 'form-control',
11012             placeholder : this.placeholder || '',
11013             autocomplete : this.autocomplete || 'new-password'
11014         };
11015         if (this.inputType == 'file') {
11016             input.style = 'overflow:hidden'; // why not in CSS?
11017         }
11018         
11019         if(this.capture.length){
11020             input.capture = this.capture;
11021         }
11022         
11023         if(this.accept.length){
11024             input.accept = this.accept + "/*";
11025         }
11026         
11027         if(this.align){
11028             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11029         }
11030         
11031         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11032             input.maxLength = this.maxLength;
11033         }
11034         
11035         if (this.disabled) {
11036             input.disabled=true;
11037         }
11038         
11039         if (this.readOnly) {
11040             input.readonly=true;
11041         }
11042         
11043         if (this.name) {
11044             input.name = this.name;
11045         }
11046         
11047         if (this.size) {
11048             input.cls += ' input-' + this.size;
11049         }
11050         
11051         var settings=this;
11052         ['xs','sm','md','lg'].map(function(size){
11053             if (settings[size]) {
11054                 cfg.cls += ' col-' + size + '-' + settings[size];
11055             }
11056         });
11057         
11058         var inputblock = input;
11059         
11060         var feedback = {
11061             tag: 'span',
11062             cls: 'glyphicon form-control-feedback'
11063         };
11064             
11065         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11066             
11067             inputblock = {
11068                 cls : 'has-feedback',
11069                 cn :  [
11070                     input,
11071                     feedback
11072                 ] 
11073             };  
11074         }
11075         
11076         if (this.before || this.after) {
11077             
11078             inputblock = {
11079                 cls : 'input-group',
11080                 cn :  [] 
11081             };
11082             
11083             if (this.before && typeof(this.before) == 'string') {
11084                 
11085                 inputblock.cn.push({
11086                     tag :'span',
11087                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11088                     html : this.before
11089                 });
11090             }
11091             if (this.before && typeof(this.before) == 'object') {
11092                 this.before = Roo.factory(this.before);
11093                 
11094                 inputblock.cn.push({
11095                     tag :'span',
11096                     cls : 'roo-input-before input-group-prepend   input-group-' +
11097                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11098                 });
11099             }
11100             
11101             inputblock.cn.push(input);
11102             
11103             if (this.after && typeof(this.after) == 'string') {
11104                 inputblock.cn.push({
11105                     tag :'span',
11106                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11107                     html : this.after
11108                 });
11109             }
11110             if (this.after && typeof(this.after) == 'object') {
11111                 this.after = Roo.factory(this.after);
11112                 
11113                 inputblock.cn.push({
11114                     tag :'span',
11115                     cls : 'roo-input-after input-group-append  input-group-' +
11116                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11117                 });
11118             }
11119             
11120             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11121                 inputblock.cls += ' has-feedback';
11122                 inputblock.cn.push(feedback);
11123             }
11124         };
11125         var indicator = {
11126             tag : 'i',
11127             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11128             tooltip : 'This field is required'
11129         };
11130         if (this.allowBlank ) {
11131             indicator.style = this.allowBlank ? ' display:none' : '';
11132         }
11133         if (align ==='left' && this.fieldLabel.length) {
11134             
11135             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11136             
11137             cfg.cn = [
11138                 indicator,
11139                 {
11140                     tag: 'label',
11141                     'for' :  id,
11142                     cls : 'control-label col-form-label',
11143                     html : this.fieldLabel
11144
11145                 },
11146                 {
11147                     cls : "", 
11148                     cn: [
11149                         inputblock
11150                     ]
11151                 }
11152             ];
11153             
11154             var labelCfg = cfg.cn[1];
11155             var contentCfg = cfg.cn[2];
11156             
11157             if(this.indicatorpos == 'right'){
11158                 cfg.cn = [
11159                     {
11160                         tag: 'label',
11161                         'for' :  id,
11162                         cls : 'control-label col-form-label',
11163                         cn : [
11164                             {
11165                                 tag : 'span',
11166                                 html : this.fieldLabel
11167                             },
11168                             indicator
11169                         ]
11170                     },
11171                     {
11172                         cls : "",
11173                         cn: [
11174                             inputblock
11175                         ]
11176                     }
11177
11178                 ];
11179                 
11180                 labelCfg = cfg.cn[0];
11181                 contentCfg = cfg.cn[1];
11182             
11183             }
11184             
11185             if(this.labelWidth > 12){
11186                 labelCfg.style = "width: " + this.labelWidth + 'px';
11187             }
11188             
11189             if(this.labelWidth < 13 && this.labelmd == 0){
11190                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11191             }
11192             
11193             if(this.labellg > 0){
11194                 labelCfg.cls += ' col-lg-' + this.labellg;
11195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11196             }
11197             
11198             if(this.labelmd > 0){
11199                 labelCfg.cls += ' col-md-' + this.labelmd;
11200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11201             }
11202             
11203             if(this.labelsm > 0){
11204                 labelCfg.cls += ' col-sm-' + this.labelsm;
11205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11206             }
11207             
11208             if(this.labelxs > 0){
11209                 labelCfg.cls += ' col-xs-' + this.labelxs;
11210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11211             }
11212             
11213             
11214         } else if ( this.fieldLabel.length) {
11215                 
11216             
11217             
11218             cfg.cn = [
11219                 {
11220                     tag : 'i',
11221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11222                     tooltip : 'This field is required',
11223                     style : this.allowBlank ? ' display:none' : '' 
11224                 },
11225                 {
11226                     tag: 'label',
11227                    //cls : 'input-group-addon',
11228                     html : this.fieldLabel
11229
11230                 },
11231
11232                inputblock
11233
11234            ];
11235            
11236            if(this.indicatorpos == 'right'){
11237        
11238                 cfg.cn = [
11239                     {
11240                         tag: 'label',
11241                        //cls : 'input-group-addon',
11242                         html : this.fieldLabel
11243
11244                     },
11245                     {
11246                         tag : 'i',
11247                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11248                         tooltip : 'This field is required',
11249                         style : this.allowBlank ? ' display:none' : '' 
11250                     },
11251
11252                    inputblock
11253
11254                ];
11255
11256             }
11257
11258         } else {
11259             
11260             cfg.cn = [
11261
11262                     inputblock
11263
11264             ];
11265                 
11266                 
11267         };
11268         
11269         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11270            cfg.cls += ' navbar-form';
11271         }
11272         
11273         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11274             // on BS4 we do this only if not form 
11275             cfg.cls += ' navbar-form';
11276             cfg.tag = 'li';
11277         }
11278         
11279         return cfg;
11280         
11281     },
11282     /**
11283      * return the real input element.
11284      */
11285     inputEl: function ()
11286     {
11287         return this.el.select('input.form-control',true).first();
11288     },
11289     
11290     tooltipEl : function()
11291     {
11292         return this.inputEl();
11293     },
11294     
11295     indicatorEl : function()
11296     {
11297         if (Roo.bootstrap.version == 4) {
11298             return false; // not enabled in v4 yet.
11299         }
11300         
11301         var indicator = this.el.select('i.roo-required-indicator',true).first();
11302         
11303         if(!indicator){
11304             return false;
11305         }
11306         
11307         return indicator;
11308         
11309     },
11310     
11311     setDisabled : function(v)
11312     {
11313         var i  = this.inputEl().dom;
11314         if (!v) {
11315             i.removeAttribute('disabled');
11316             return;
11317             
11318         }
11319         i.setAttribute('disabled','true');
11320     },
11321     initEvents : function()
11322     {
11323           
11324         this.inputEl().on("keydown" , this.fireKey,  this);
11325         this.inputEl().on("focus", this.onFocus,  this);
11326         this.inputEl().on("blur", this.onBlur,  this);
11327         
11328         this.inputEl().relayEvent('keyup', this);
11329         this.inputEl().relayEvent('paste', this);
11330         
11331         this.indicator = this.indicatorEl();
11332         
11333         if(this.indicator){
11334             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11335         }
11336  
11337         // reference to original value for reset
11338         this.originalValue = this.getValue();
11339         //Roo.form.TextField.superclass.initEvents.call(this);
11340         if(this.validationEvent == 'keyup'){
11341             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11342             this.inputEl().on('keyup', this.filterValidation, this);
11343         }
11344         else if(this.validationEvent !== false){
11345             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11346         }
11347         
11348         if(this.selectOnFocus){
11349             this.on("focus", this.preFocus, this);
11350             
11351         }
11352         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11353             this.inputEl().on("keypress", this.filterKeys, this);
11354         } else {
11355             this.inputEl().relayEvent('keypress', this);
11356         }
11357        /* if(this.grow){
11358             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11359             this.el.on("click", this.autoSize,  this);
11360         }
11361         */
11362         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11363             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11364         }
11365         
11366         if (typeof(this.before) == 'object') {
11367             this.before.render(this.el.select('.roo-input-before',true).first());
11368         }
11369         if (typeof(this.after) == 'object') {
11370             this.after.render(this.el.select('.roo-input-after',true).first());
11371         }
11372         
11373         this.inputEl().on('change', this.onChange, this);
11374         
11375     },
11376     filterValidation : function(e){
11377         if(!e.isNavKeyPress()){
11378             this.validationTask.delay(this.validationDelay);
11379         }
11380     },
11381      /**
11382      * Validates the field value
11383      * @return {Boolean} True if the value is valid, else false
11384      */
11385     validate : function(){
11386         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11387         if(this.disabled || this.validateValue(this.getRawValue())){
11388             this.markValid();
11389             return true;
11390         }
11391         
11392         this.markInvalid();
11393         return false;
11394     },
11395     
11396     
11397     /**
11398      * Validates a value according to the field's validation rules and marks the field as invalid
11399      * if the validation fails
11400      * @param {Mixed} value The value to validate
11401      * @return {Boolean} True if the value is valid, else false
11402      */
11403     validateValue : function(value)
11404     {
11405         if(this.getVisibilityEl().hasClass('hidden')){
11406             return true;
11407         }
11408         
11409         if(value.length < 1)  { // if it's blank
11410             if(this.allowBlank){
11411                 return true;
11412             }
11413             return false;
11414         }
11415         
11416         if(value.length < this.minLength){
11417             return false;
11418         }
11419         if(value.length > this.maxLength){
11420             return false;
11421         }
11422         if(this.vtype){
11423             var vt = Roo.form.VTypes;
11424             if(!vt[this.vtype](value, this)){
11425                 return false;
11426             }
11427         }
11428         if(typeof this.validator == "function"){
11429             var msg = this.validator(value);
11430             if(msg !== true){
11431                 return false;
11432             }
11433             if (typeof(msg) == 'string') {
11434                 this.invalidText = msg;
11435             }
11436         }
11437         
11438         if(this.regex && !this.regex.test(value)){
11439             return false;
11440         }
11441         
11442         return true;
11443     },
11444     
11445      // private
11446     fireKey : function(e){
11447         //Roo.log('field ' + e.getKey());
11448         if(e.isNavKeyPress()){
11449             this.fireEvent("specialkey", this, e);
11450         }
11451     },
11452     focus : function (selectText){
11453         if(this.rendered){
11454             this.inputEl().focus();
11455             if(selectText === true){
11456                 this.inputEl().dom.select();
11457             }
11458         }
11459         return this;
11460     } ,
11461     
11462     onFocus : function(){
11463         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11464            // this.el.addClass(this.focusClass);
11465         }
11466         if(!this.hasFocus){
11467             this.hasFocus = true;
11468             this.startValue = this.getValue();
11469             this.fireEvent("focus", this);
11470         }
11471     },
11472     
11473     beforeBlur : Roo.emptyFn,
11474
11475     
11476     // private
11477     onBlur : function(){
11478         this.beforeBlur();
11479         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11480             //this.el.removeClass(this.focusClass);
11481         }
11482         this.hasFocus = false;
11483         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11484             this.validate();
11485         }
11486         var v = this.getValue();
11487         if(String(v) !== String(this.startValue)){
11488             this.fireEvent('change', this, v, this.startValue);
11489         }
11490         this.fireEvent("blur", this);
11491     },
11492     
11493     onChange : function(e)
11494     {
11495         var v = this.getValue();
11496         if(String(v) !== String(this.startValue)){
11497             this.fireEvent('change', this, v, this.startValue);
11498         }
11499         
11500     },
11501     
11502     /**
11503      * Resets the current field value to the originally loaded value and clears any validation messages
11504      */
11505     reset : function(){
11506         this.setValue(this.originalValue);
11507         this.validate();
11508     },
11509      /**
11510      * Returns the name of the field
11511      * @return {Mixed} name The name field
11512      */
11513     getName: function(){
11514         return this.name;
11515     },
11516      /**
11517      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11518      * @return {Mixed} value The field value
11519      */
11520     getValue : function(){
11521         
11522         var v = this.inputEl().getValue();
11523         
11524         return v;
11525     },
11526     /**
11527      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11528      * @return {Mixed} value The field value
11529      */
11530     getRawValue : function(){
11531         var v = this.inputEl().getValue();
11532         
11533         return v;
11534     },
11535     
11536     /**
11537      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11538      * @param {Mixed} value The value to set
11539      */
11540     setRawValue : function(v){
11541         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11542     },
11543     
11544     selectText : function(start, end){
11545         var v = this.getRawValue();
11546         if(v.length > 0){
11547             start = start === undefined ? 0 : start;
11548             end = end === undefined ? v.length : end;
11549             var d = this.inputEl().dom;
11550             if(d.setSelectionRange){
11551                 d.setSelectionRange(start, end);
11552             }else if(d.createTextRange){
11553                 var range = d.createTextRange();
11554                 range.moveStart("character", start);
11555                 range.moveEnd("character", v.length-end);
11556                 range.select();
11557             }
11558         }
11559     },
11560     
11561     /**
11562      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11563      * @param {Mixed} value The value to set
11564      */
11565     setValue : function(v){
11566         this.value = v;
11567         if(this.rendered){
11568             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11569             this.validate();
11570         }
11571     },
11572     
11573     /*
11574     processValue : function(value){
11575         if(this.stripCharsRe){
11576             var newValue = value.replace(this.stripCharsRe, '');
11577             if(newValue !== value){
11578                 this.setRawValue(newValue);
11579                 return newValue;
11580             }
11581         }
11582         return value;
11583     },
11584   */
11585     preFocus : function(){
11586         
11587         if(this.selectOnFocus){
11588             this.inputEl().dom.select();
11589         }
11590     },
11591     filterKeys : function(e){
11592         var k = e.getKey();
11593         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11594             return;
11595         }
11596         var c = e.getCharCode(), cc = String.fromCharCode(c);
11597         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11598             return;
11599         }
11600         if(!this.maskRe.test(cc)){
11601             e.stopEvent();
11602         }
11603     },
11604      /**
11605      * Clear any invalid styles/messages for this field
11606      */
11607     clearInvalid : function(){
11608         
11609         if(!this.el || this.preventMark){ // not rendered
11610             return;
11611         }
11612         
11613         
11614         this.el.removeClass([this.invalidClass, 'is-invalid']);
11615         
11616         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11617             
11618             var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620             if(feedback){
11621                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11622             }
11623             
11624         }
11625         
11626         if(this.indicator){
11627             this.indicator.removeClass('visible');
11628             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11629         }
11630         
11631         this.fireEvent('valid', this);
11632     },
11633     
11634      /**
11635      * Mark this field as valid
11636      */
11637     markValid : function()
11638     {
11639         if(!this.el  || this.preventMark){ // not rendered...
11640             return;
11641         }
11642         
11643         this.el.removeClass([this.invalidClass, this.validClass]);
11644         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11645
11646         var feedback = this.el.select('.form-control-feedback', true).first();
11647             
11648         if(feedback){
11649             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11650         }
11651         
11652         if(this.indicator){
11653             this.indicator.removeClass('visible');
11654             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11655         }
11656         
11657         if(this.disabled){
11658             return;
11659         }
11660         
11661            
11662         if(this.allowBlank && !this.getRawValue().length){
11663             return;
11664         }
11665         if (Roo.bootstrap.version == 3) {
11666             this.el.addClass(this.validClass);
11667         } else {
11668             this.inputEl().addClass('is-valid');
11669         }
11670
11671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11672             
11673             var feedback = this.el.select('.form-control-feedback', true).first();
11674             
11675             if(feedback){
11676                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11677                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11678             }
11679             
11680         }
11681         
11682         this.fireEvent('valid', this);
11683     },
11684     
11685      /**
11686      * Mark this field as invalid
11687      * @param {String} msg The validation message
11688      */
11689     markInvalid : function(msg)
11690     {
11691         if(!this.el  || this.preventMark){ // not rendered
11692             return;
11693         }
11694         
11695         this.el.removeClass([this.invalidClass, this.validClass]);
11696         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11697         
11698         var feedback = this.el.select('.form-control-feedback', true).first();
11699             
11700         if(feedback){
11701             this.el.select('.form-control-feedback', true).first().removeClass(
11702                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11703         }
11704
11705         if(this.disabled){
11706             return;
11707         }
11708         
11709         if(this.allowBlank && !this.getRawValue().length){
11710             return;
11711         }
11712         
11713         if(this.indicator){
11714             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11715             this.indicator.addClass('visible');
11716         }
11717         if (Roo.bootstrap.version == 3) {
11718             this.el.addClass(this.invalidClass);
11719         } else {
11720             this.inputEl().addClass('is-invalid');
11721         }
11722         
11723         
11724         
11725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11726             
11727             var feedback = this.el.select('.form-control-feedback', true).first();
11728             
11729             if(feedback){
11730                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11731                 
11732                 if(this.getValue().length || this.forceFeedback){
11733                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11734                 }
11735                 
11736             }
11737             
11738         }
11739         
11740         this.fireEvent('invalid', this, msg);
11741     },
11742     // private
11743     SafariOnKeyDown : function(event)
11744     {
11745         // this is a workaround for a password hang bug on chrome/ webkit.
11746         if (this.inputEl().dom.type != 'password') {
11747             return;
11748         }
11749         
11750         var isSelectAll = false;
11751         
11752         if(this.inputEl().dom.selectionEnd > 0){
11753             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11754         }
11755         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11756             event.preventDefault();
11757             this.setValue('');
11758             return;
11759         }
11760         
11761         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11762             
11763             event.preventDefault();
11764             // this is very hacky as keydown always get's upper case.
11765             //
11766             var cc = String.fromCharCode(event.getCharCode());
11767             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11768             
11769         }
11770     },
11771     adjustWidth : function(tag, w){
11772         tag = tag.toLowerCase();
11773         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11774             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11775                 if(tag == 'input'){
11776                     return w + 2;
11777                 }
11778                 if(tag == 'textarea'){
11779                     return w-2;
11780                 }
11781             }else if(Roo.isOpera){
11782                 if(tag == 'input'){
11783                     return w + 2;
11784                 }
11785                 if(tag == 'textarea'){
11786                     return w-2;
11787                 }
11788             }
11789         }
11790         return w;
11791     },
11792     
11793     setFieldLabel : function(v)
11794     {
11795         if(!this.rendered){
11796             return;
11797         }
11798         
11799         if(this.indicatorEl()){
11800             var ar = this.el.select('label > span',true);
11801             
11802             if (ar.elements.length) {
11803                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804                 this.fieldLabel = v;
11805                 return;
11806             }
11807             
11808             var br = this.el.select('label',true);
11809             
11810             if(br.elements.length) {
11811                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11812                 this.fieldLabel = v;
11813                 return;
11814             }
11815             
11816             Roo.log('Cannot Found any of label > span || label in input');
11817             return;
11818         }
11819         
11820         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11821         this.fieldLabel = v;
11822         
11823         
11824     }
11825 });
11826
11827  
11828 /*
11829  * - LGPL
11830  *
11831  * Input
11832  * 
11833  */
11834
11835 /**
11836  * @class Roo.bootstrap.TextArea
11837  * @extends Roo.bootstrap.Input
11838  * Bootstrap TextArea class
11839  * @cfg {Number} cols Specifies the visible width of a text area
11840  * @cfg {Number} rows Specifies the visible number of lines in a text area
11841  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11842  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11843  * @cfg {string} html text
11844  * 
11845  * @constructor
11846  * Create a new TextArea
11847  * @param {Object} config The config object
11848  */
11849
11850 Roo.bootstrap.TextArea = function(config){
11851     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11852    
11853 };
11854
11855 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11856      
11857     cols : false,
11858     rows : 5,
11859     readOnly : false,
11860     warp : 'soft',
11861     resize : false,
11862     value: false,
11863     html: false,
11864     
11865     getAutoCreate : function(){
11866         
11867         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11868         
11869         var id = Roo.id();
11870         
11871         var cfg = {};
11872         
11873         if(this.inputType != 'hidden'){
11874             cfg.cls = 'form-group' //input-group
11875         }
11876         
11877         var input =  {
11878             tag: 'textarea',
11879             id : id,
11880             warp : this.warp,
11881             rows : this.rows,
11882             value : this.value || '',
11883             html: this.html || '',
11884             cls : 'form-control',
11885             placeholder : this.placeholder || '' 
11886             
11887         };
11888         
11889         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11890             input.maxLength = this.maxLength;
11891         }
11892         
11893         if(this.resize){
11894             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11895         }
11896         
11897         if(this.cols){
11898             input.cols = this.cols;
11899         }
11900         
11901         if (this.readOnly) {
11902             input.readonly = true;
11903         }
11904         
11905         if (this.name) {
11906             input.name = this.name;
11907         }
11908         
11909         if (this.size) {
11910             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11911         }
11912         
11913         var settings=this;
11914         ['xs','sm','md','lg'].map(function(size){
11915             if (settings[size]) {
11916                 cfg.cls += ' col-' + size + '-' + settings[size];
11917             }
11918         });
11919         
11920         var inputblock = input;
11921         
11922         if(this.hasFeedback && !this.allowBlank){
11923             
11924             var feedback = {
11925                 tag: 'span',
11926                 cls: 'glyphicon form-control-feedback'
11927             };
11928
11929             inputblock = {
11930                 cls : 'has-feedback',
11931                 cn :  [
11932                     input,
11933                     feedback
11934                 ] 
11935             };  
11936         }
11937         
11938         
11939         if (this.before || this.after) {
11940             
11941             inputblock = {
11942                 cls : 'input-group',
11943                 cn :  [] 
11944             };
11945             if (this.before) {
11946                 inputblock.cn.push({
11947                     tag :'span',
11948                     cls : 'input-group-addon',
11949                     html : this.before
11950                 });
11951             }
11952             
11953             inputblock.cn.push(input);
11954             
11955             if(this.hasFeedback && !this.allowBlank){
11956                 inputblock.cls += ' has-feedback';
11957                 inputblock.cn.push(feedback);
11958             }
11959             
11960             if (this.after) {
11961                 inputblock.cn.push({
11962                     tag :'span',
11963                     cls : 'input-group-addon',
11964                     html : this.after
11965                 });
11966             }
11967             
11968         }
11969         
11970         if (align ==='left' && this.fieldLabel.length) {
11971             cfg.cn = [
11972                 {
11973                     tag: 'label',
11974                     'for' :  id,
11975                     cls : 'control-label',
11976                     html : this.fieldLabel
11977                 },
11978                 {
11979                     cls : "",
11980                     cn: [
11981                         inputblock
11982                     ]
11983                 }
11984
11985             ];
11986             
11987             if(this.labelWidth > 12){
11988                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11989             }
11990
11991             if(this.labelWidth < 13 && this.labelmd == 0){
11992                 this.labelmd = this.labelWidth;
11993             }
11994
11995             if(this.labellg > 0){
11996                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11997                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11998             }
11999
12000             if(this.labelmd > 0){
12001                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12002                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12003             }
12004
12005             if(this.labelsm > 0){
12006                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12007                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12008             }
12009
12010             if(this.labelxs > 0){
12011                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12012                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12013             }
12014             
12015         } else if ( this.fieldLabel.length) {
12016             cfg.cn = [
12017
12018                {
12019                    tag: 'label',
12020                    //cls : 'input-group-addon',
12021                    html : this.fieldLabel
12022
12023                },
12024
12025                inputblock
12026
12027            ];
12028
12029         } else {
12030
12031             cfg.cn = [
12032
12033                 inputblock
12034
12035             ];
12036                 
12037         }
12038         
12039         if (this.disabled) {
12040             input.disabled=true;
12041         }
12042         
12043         return cfg;
12044         
12045     },
12046     /**
12047      * return the real textarea element.
12048      */
12049     inputEl: function ()
12050     {
12051         return this.el.select('textarea.form-control',true).first();
12052     },
12053     
12054     /**
12055      * Clear any invalid styles/messages for this field
12056      */
12057     clearInvalid : function()
12058     {
12059         
12060         if(!this.el || this.preventMark){ // not rendered
12061             return;
12062         }
12063         
12064         var label = this.el.select('label', true).first();
12065         var icon = this.el.select('i.fa-star', true).first();
12066         
12067         if(label && icon){
12068             icon.remove();
12069         }
12070         this.el.removeClass( this.validClass);
12071         this.inputEl().removeClass('is-invalid');
12072          
12073         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12074             
12075             var feedback = this.el.select('.form-control-feedback', true).first();
12076             
12077             if(feedback){
12078                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12079             }
12080             
12081         }
12082         
12083         this.fireEvent('valid', this);
12084     },
12085     
12086      /**
12087      * Mark this field as valid
12088      */
12089     markValid : function()
12090     {
12091         if(!this.el  || this.preventMark){ // not rendered
12092             return;
12093         }
12094         
12095         this.el.removeClass([this.invalidClass, this.validClass]);
12096         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12097         
12098         var feedback = this.el.select('.form-control-feedback', true).first();
12099             
12100         if(feedback){
12101             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12102         }
12103
12104         if(this.disabled || this.allowBlank){
12105             return;
12106         }
12107         
12108         var label = this.el.select('label', true).first();
12109         var icon = this.el.select('i.fa-star', true).first();
12110         
12111         if(label && icon){
12112             icon.remove();
12113         }
12114         if (Roo.bootstrap.version == 3) {
12115             this.el.addClass(this.validClass);
12116         } else {
12117             this.inputEl().addClass('is-valid');
12118         }
12119         
12120         
12121         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12122             
12123             var feedback = this.el.select('.form-control-feedback', true).first();
12124             
12125             if(feedback){
12126                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12127                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12128             }
12129             
12130         }
12131         
12132         this.fireEvent('valid', this);
12133     },
12134     
12135      /**
12136      * Mark this field as invalid
12137      * @param {String} msg The validation message
12138      */
12139     markInvalid : function(msg)
12140     {
12141         if(!this.el  || this.preventMark){ // not rendered
12142             return;
12143         }
12144         
12145         this.el.removeClass([this.invalidClass, this.validClass]);
12146         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12147         
12148         var feedback = this.el.select('.form-control-feedback', true).first();
12149             
12150         if(feedback){
12151             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12152         }
12153
12154         if(this.disabled || this.allowBlank){
12155             return;
12156         }
12157         
12158         var label = this.el.select('label', true).first();
12159         var icon = this.el.select('i.fa-star', true).first();
12160         
12161         if(!this.getValue().length && label && !icon){
12162             this.el.createChild({
12163                 tag : 'i',
12164                 cls : 'text-danger fa fa-lg fa-star',
12165                 tooltip : 'This field is required',
12166                 style : 'margin-right:5px;'
12167             }, label, true);
12168         }
12169         
12170         if (Roo.bootstrap.version == 3) {
12171             this.el.addClass(this.invalidClass);
12172         } else {
12173             this.inputEl().addClass('is-invalid');
12174         }
12175         
12176         // fixme ... this may be depricated need to test..
12177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178             
12179             var feedback = this.el.select('.form-control-feedback', true).first();
12180             
12181             if(feedback){
12182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12183                 
12184                 if(this.getValue().length || this.forceFeedback){
12185                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12186                 }
12187                 
12188             }
12189             
12190         }
12191         
12192         this.fireEvent('invalid', this, msg);
12193     }
12194 });
12195
12196  
12197 /*
12198  * - LGPL
12199  *
12200  * trigger field - base class for combo..
12201  * 
12202  */
12203  
12204 /**
12205  * @class Roo.bootstrap.TriggerField
12206  * @extends Roo.bootstrap.Input
12207  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12208  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12209  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12210  * for which you can provide a custom implementation.  For example:
12211  * <pre><code>
12212 var trigger = new Roo.bootstrap.TriggerField();
12213 trigger.onTriggerClick = myTriggerFn;
12214 trigger.applyTo('my-field');
12215 </code></pre>
12216  *
12217  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12218  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12219  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12220  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12221  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12222
12223  * @constructor
12224  * Create a new TriggerField.
12225  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12226  * to the base TextField)
12227  */
12228 Roo.bootstrap.TriggerField = function(config){
12229     this.mimicing = false;
12230     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12231 };
12232
12233 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12234     /**
12235      * @cfg {String} triggerClass A CSS class to apply to the trigger
12236      */
12237      /**
12238      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12239      */
12240     hideTrigger:false,
12241
12242     /**
12243      * @cfg {Boolean} removable (true|false) special filter default false
12244      */
12245     removable : false,
12246     
12247     /** @cfg {Boolean} grow @hide */
12248     /** @cfg {Number} growMin @hide */
12249     /** @cfg {Number} growMax @hide */
12250
12251     /**
12252      * @hide 
12253      * @method
12254      */
12255     autoSize: Roo.emptyFn,
12256     // private
12257     monitorTab : true,
12258     // private
12259     deferHeight : true,
12260
12261     
12262     actionMode : 'wrap',
12263     
12264     caret : false,
12265     
12266     
12267     getAutoCreate : function(){
12268        
12269         var align = this.labelAlign || this.parentLabelAlign();
12270         
12271         var id = Roo.id();
12272         
12273         var cfg = {
12274             cls: 'form-group' //input-group
12275         };
12276         
12277         
12278         var input =  {
12279             tag: 'input',
12280             id : id,
12281             type : this.inputType,
12282             cls : 'form-control',
12283             autocomplete: 'new-password',
12284             placeholder : this.placeholder || '' 
12285             
12286         };
12287         if (this.name) {
12288             input.name = this.name;
12289         }
12290         if (this.size) {
12291             input.cls += ' input-' + this.size;
12292         }
12293         
12294         if (this.disabled) {
12295             input.disabled=true;
12296         }
12297         
12298         var inputblock = input;
12299         
12300         if(this.hasFeedback && !this.allowBlank){
12301             
12302             var feedback = {
12303                 tag: 'span',
12304                 cls: 'glyphicon form-control-feedback'
12305             };
12306             
12307             if(this.removable && !this.editable  ){
12308                 inputblock = {
12309                     cls : 'has-feedback',
12310                     cn :  [
12311                         inputblock,
12312                         {
12313                             tag: 'button',
12314                             html : 'x',
12315                             cls : 'roo-combo-removable-btn close'
12316                         },
12317                         feedback
12318                     ] 
12319                 };
12320             } else {
12321                 inputblock = {
12322                     cls : 'has-feedback',
12323                     cn :  [
12324                         inputblock,
12325                         feedback
12326                     ] 
12327                 };
12328             }
12329
12330         } else {
12331             if(this.removable && !this.editable ){
12332                 inputblock = {
12333                     cls : 'roo-removable',
12334                     cn :  [
12335                         inputblock,
12336                         {
12337                             tag: 'button',
12338                             html : 'x',
12339                             cls : 'roo-combo-removable-btn close'
12340                         }
12341                     ] 
12342                 };
12343             }
12344         }
12345         
12346         if (this.before || this.after) {
12347             
12348             inputblock = {
12349                 cls : 'input-group',
12350                 cn :  [] 
12351             };
12352             if (this.before) {
12353                 inputblock.cn.push({
12354                     tag :'span',
12355                     cls : 'input-group-addon input-group-prepend input-group-text',
12356                     html : this.before
12357                 });
12358             }
12359             
12360             inputblock.cn.push(input);
12361             
12362             if(this.hasFeedback && !this.allowBlank){
12363                 inputblock.cls += ' has-feedback';
12364                 inputblock.cn.push(feedback);
12365             }
12366             
12367             if (this.after) {
12368                 inputblock.cn.push({
12369                     tag :'span',
12370                     cls : 'input-group-addon input-group-append input-group-text',
12371                     html : this.after
12372                 });
12373             }
12374             
12375         };
12376         
12377       
12378         
12379         var ibwrap = inputblock;
12380         
12381         if(this.multiple){
12382             ibwrap = {
12383                 tag: 'ul',
12384                 cls: 'roo-select2-choices',
12385                 cn:[
12386                     {
12387                         tag: 'li',
12388                         cls: 'roo-select2-search-field',
12389                         cn: [
12390
12391                             inputblock
12392                         ]
12393                     }
12394                 ]
12395             };
12396                 
12397         }
12398         
12399         var combobox = {
12400             cls: 'roo-select2-container input-group',
12401             cn: [
12402                  {
12403                     tag: 'input',
12404                     type : 'hidden',
12405                     cls: 'form-hidden-field'
12406                 },
12407                 ibwrap
12408             ]
12409         };
12410         
12411         if(!this.multiple && this.showToggleBtn){
12412             
12413             var caret = {
12414                         tag: 'span',
12415                         cls: 'caret'
12416              };
12417             if (this.caret != false) {
12418                 caret = {
12419                      tag: 'i',
12420                      cls: 'fa fa-' + this.caret
12421                 };
12422                 
12423             }
12424             
12425             combobox.cn.push({
12426                 tag :'span',
12427                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12428                 cn : [
12429                     Roo.bootstrap.version == 3 ? caret : '',
12430                     {
12431                         tag: 'span',
12432                         cls: 'combobox-clear',
12433                         cn  : [
12434                             {
12435                                 tag : 'i',
12436                                 cls: 'icon-remove'
12437                             }
12438                         ]
12439                     }
12440                 ]
12441
12442             })
12443         }
12444         
12445         if(this.multiple){
12446             combobox.cls += ' roo-select2-container-multi';
12447         }
12448          var indicator = {
12449             tag : 'i',
12450             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12451             tooltip : 'This field is required'
12452         };
12453         if (Roo.bootstrap.version == 4) {
12454             indicator = {
12455                 tag : 'i',
12456                 style : 'display:none'
12457             };
12458         }
12459         
12460         
12461         if (align ==='left' && this.fieldLabel.length) {
12462             
12463             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12464
12465             cfg.cn = [
12466                 indicator,
12467                 {
12468                     tag: 'label',
12469                     'for' :  id,
12470                     cls : 'control-label',
12471                     html : this.fieldLabel
12472
12473                 },
12474                 {
12475                     cls : "", 
12476                     cn: [
12477                         combobox
12478                     ]
12479                 }
12480
12481             ];
12482             
12483             var labelCfg = cfg.cn[1];
12484             var contentCfg = cfg.cn[2];
12485             
12486             if(this.indicatorpos == 'right'){
12487                 cfg.cn = [
12488                     {
12489                         tag: 'label',
12490                         'for' :  id,
12491                         cls : 'control-label',
12492                         cn : [
12493                             {
12494                                 tag : 'span',
12495                                 html : this.fieldLabel
12496                             },
12497                             indicator
12498                         ]
12499                     },
12500                     {
12501                         cls : "", 
12502                         cn: [
12503                             combobox
12504                         ]
12505                     }
12506
12507                 ];
12508                 
12509                 labelCfg = cfg.cn[0];
12510                 contentCfg = cfg.cn[1];
12511             }
12512             
12513             if(this.labelWidth > 12){
12514                 labelCfg.style = "width: " + this.labelWidth + 'px';
12515             }
12516             
12517             if(this.labelWidth < 13 && this.labelmd == 0){
12518                 this.labelmd = this.labelWidth;
12519             }
12520             
12521             if(this.labellg > 0){
12522                 labelCfg.cls += ' col-lg-' + this.labellg;
12523                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12524             }
12525             
12526             if(this.labelmd > 0){
12527                 labelCfg.cls += ' col-md-' + this.labelmd;
12528                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12529             }
12530             
12531             if(this.labelsm > 0){
12532                 labelCfg.cls += ' col-sm-' + this.labelsm;
12533                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12534             }
12535             
12536             if(this.labelxs > 0){
12537                 labelCfg.cls += ' col-xs-' + this.labelxs;
12538                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12539             }
12540             
12541         } else if ( this.fieldLabel.length) {
12542 //                Roo.log(" label");
12543             cfg.cn = [
12544                 indicator,
12545                {
12546                    tag: 'label',
12547                    //cls : 'input-group-addon',
12548                    html : this.fieldLabel
12549
12550                },
12551
12552                combobox
12553
12554             ];
12555             
12556             if(this.indicatorpos == 'right'){
12557                 
12558                 cfg.cn = [
12559                     {
12560                        tag: 'label',
12561                        cn : [
12562                            {
12563                                tag : 'span',
12564                                html : this.fieldLabel
12565                            },
12566                            indicator
12567                        ]
12568
12569                     },
12570                     combobox
12571
12572                 ];
12573
12574             }
12575
12576         } else {
12577             
12578 //                Roo.log(" no label && no align");
12579                 cfg = combobox
12580                      
12581                 
12582         }
12583         
12584         var settings=this;
12585         ['xs','sm','md','lg'].map(function(size){
12586             if (settings[size]) {
12587                 cfg.cls += ' col-' + size + '-' + settings[size];
12588             }
12589         });
12590         
12591         return cfg;
12592         
12593     },
12594     
12595     
12596     
12597     // private
12598     onResize : function(w, h){
12599 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12600 //        if(typeof w == 'number'){
12601 //            var x = w - this.trigger.getWidth();
12602 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12603 //            this.trigger.setStyle('left', x+'px');
12604 //        }
12605     },
12606
12607     // private
12608     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12609
12610     // private
12611     getResizeEl : function(){
12612         return this.inputEl();
12613     },
12614
12615     // private
12616     getPositionEl : function(){
12617         return this.inputEl();
12618     },
12619
12620     // private
12621     alignErrorIcon : function(){
12622         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12623     },
12624
12625     // private
12626     initEvents : function(){
12627         
12628         this.createList();
12629         
12630         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12631         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12632         if(!this.multiple && this.showToggleBtn){
12633             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12634             if(this.hideTrigger){
12635                 this.trigger.setDisplayed(false);
12636             }
12637             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12638         }
12639         
12640         if(this.multiple){
12641             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12642         }
12643         
12644         if(this.removable && !this.editable && !this.tickable){
12645             var close = this.closeTriggerEl();
12646             
12647             if(close){
12648                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12649                 close.on('click', this.removeBtnClick, this, close);
12650             }
12651         }
12652         
12653         //this.trigger.addClassOnOver('x-form-trigger-over');
12654         //this.trigger.addClassOnClick('x-form-trigger-click');
12655         
12656         //if(!this.width){
12657         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12658         //}
12659     },
12660     
12661     closeTriggerEl : function()
12662     {
12663         var close = this.el.select('.roo-combo-removable-btn', true).first();
12664         return close ? close : false;
12665     },
12666     
12667     removeBtnClick : function(e, h, el)
12668     {
12669         e.preventDefault();
12670         
12671         if(this.fireEvent("remove", this) !== false){
12672             this.reset();
12673             this.fireEvent("afterremove", this)
12674         }
12675     },
12676     
12677     createList : function()
12678     {
12679         this.list = Roo.get(document.body).createChild({
12680             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12681             cls: 'typeahead typeahead-long dropdown-menu shadow',
12682             style: 'display:none'
12683         });
12684         
12685         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12686         
12687     },
12688
12689     // private
12690     initTrigger : function(){
12691        
12692     },
12693
12694     // private
12695     onDestroy : function(){
12696         if(this.trigger){
12697             this.trigger.removeAllListeners();
12698           //  this.trigger.remove();
12699         }
12700         //if(this.wrap){
12701         //    this.wrap.remove();
12702         //}
12703         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12704     },
12705
12706     // private
12707     onFocus : function(){
12708         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12709         /*
12710         if(!this.mimicing){
12711             this.wrap.addClass('x-trigger-wrap-focus');
12712             this.mimicing = true;
12713             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12714             if(this.monitorTab){
12715                 this.el.on("keydown", this.checkTab, this);
12716             }
12717         }
12718         */
12719     },
12720
12721     // private
12722     checkTab : function(e){
12723         if(e.getKey() == e.TAB){
12724             this.triggerBlur();
12725         }
12726     },
12727
12728     // private
12729     onBlur : function(){
12730         // do nothing
12731     },
12732
12733     // private
12734     mimicBlur : function(e, t){
12735         /*
12736         if(!this.wrap.contains(t) && this.validateBlur()){
12737             this.triggerBlur();
12738         }
12739         */
12740     },
12741
12742     // private
12743     triggerBlur : function(){
12744         this.mimicing = false;
12745         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12746         if(this.monitorTab){
12747             this.el.un("keydown", this.checkTab, this);
12748         }
12749         //this.wrap.removeClass('x-trigger-wrap-focus');
12750         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12751     },
12752
12753     // private
12754     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12755     validateBlur : function(e, t){
12756         return true;
12757     },
12758
12759     // private
12760     onDisable : function(){
12761         this.inputEl().dom.disabled = true;
12762         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12763         //if(this.wrap){
12764         //    this.wrap.addClass('x-item-disabled');
12765         //}
12766     },
12767
12768     // private
12769     onEnable : function(){
12770         this.inputEl().dom.disabled = false;
12771         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12772         //if(this.wrap){
12773         //    this.el.removeClass('x-item-disabled');
12774         //}
12775     },
12776
12777     // private
12778     onShow : function(){
12779         var ae = this.getActionEl();
12780         
12781         if(ae){
12782             ae.dom.style.display = '';
12783             ae.dom.style.visibility = 'visible';
12784         }
12785     },
12786
12787     // private
12788     
12789     onHide : function(){
12790         var ae = this.getActionEl();
12791         ae.dom.style.display = 'none';
12792     },
12793
12794     /**
12795      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12796      * by an implementing function.
12797      * @method
12798      * @param {EventObject} e
12799      */
12800     onTriggerClick : Roo.emptyFn
12801 });
12802  
12803 /*
12804 * Licence: LGPL
12805 */
12806
12807 /**
12808  * @class Roo.bootstrap.CardUploader
12809  * @extends Roo.bootstrap.Button
12810  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12811  * @cfg {Number} errorTimeout default 3000
12812  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12813  * @cfg {Array}  html The button text.
12814
12815  *
12816  * @constructor
12817  * Create a new CardUploader
12818  * @param {Object} config The config object
12819  */
12820
12821 Roo.bootstrap.CardUploader = function(config){
12822     
12823  
12824     
12825     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12826     
12827     
12828     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12829         return r.data.id
12830      });
12831     
12832      this.addEvents({
12833          // raw events
12834         /**
12835          * @event preview
12836          * When a image is clicked on - and needs to display a slideshow or similar..
12837          * @param {Roo.bootstrap.Card} this
12838          * @param {Object} The image information data 
12839          *
12840          */
12841         'preview' : true,
12842          /**
12843          * @event download
12844          * When a the download link is clicked
12845          * @param {Roo.bootstrap.Card} this
12846          * @param {Object} The image information data  contains 
12847          */
12848         'download' : true
12849         
12850     });
12851 };
12852  
12853 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12854     
12855      
12856     errorTimeout : 3000,
12857      
12858     images : false,
12859    
12860     fileCollection : false,
12861     allowBlank : true,
12862     
12863     getAutoCreate : function()
12864     {
12865         
12866         var cfg =  {
12867             cls :'form-group' ,
12868             cn : [
12869                
12870                 {
12871                     tag: 'label',
12872                    //cls : 'input-group-addon',
12873                     html : this.fieldLabel
12874
12875                 },
12876
12877                 {
12878                     tag: 'input',
12879                     type : 'hidden',
12880                     name : this.name,
12881                     value : this.value,
12882                     cls : 'd-none  form-control'
12883                 },
12884                 
12885                 {
12886                     tag: 'input',
12887                     multiple : 'multiple',
12888                     type : 'file',
12889                     cls : 'd-none  roo-card-upload-selector'
12890                 },
12891                 
12892                 {
12893                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12894                 },
12895                 {
12896                     cls : 'card-columns roo-card-uploader-container'
12897                 }
12898
12899             ]
12900         };
12901            
12902          
12903         return cfg;
12904     },
12905     
12906     getChildContainer : function() /// what children are added to.
12907     {
12908         return this.containerEl;
12909     },
12910    
12911     getButtonContainer : function() /// what children are added to.
12912     {
12913         return this.el.select(".roo-card-uploader-button-container").first();
12914     },
12915    
12916     initEvents : function()
12917     {
12918         
12919         Roo.bootstrap.Input.prototype.initEvents.call(this);
12920         
12921         var t = this;
12922         this.addxtype({
12923             xns: Roo.bootstrap,
12924
12925             xtype : 'Button',
12926             container_method : 'getButtonContainer' ,            
12927             html :  this.html, // fix changable?
12928             cls : 'w-100 ',
12929             listeners : {
12930                 'click' : function(btn, e) {
12931                     t.onClick(e);
12932                 }
12933             }
12934         });
12935         
12936         
12937         
12938         
12939         this.urlAPI = (window.createObjectURL && window) || 
12940                                 (window.URL && URL.revokeObjectURL && URL) || 
12941                                 (window.webkitURL && webkitURL);
12942                         
12943          
12944          
12945          
12946         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12947         
12948         this.selectorEl.on('change', this.onFileSelected, this);
12949         if (this.images) {
12950             var t = this;
12951             this.images.forEach(function(img) {
12952                 t.addCard(img)
12953             });
12954             this.images = false;
12955         }
12956         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12957          
12958        
12959     },
12960     
12961    
12962     onClick : function(e)
12963     {
12964         e.preventDefault();
12965          
12966         this.selectorEl.dom.click();
12967          
12968     },
12969     
12970     onFileSelected : function(e)
12971     {
12972         e.preventDefault();
12973         
12974         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12975             return;
12976         }
12977         
12978         Roo.each(this.selectorEl.dom.files, function(file){    
12979             this.addFile(file);
12980         }, this);
12981          
12982     },
12983     
12984       
12985     
12986       
12987     
12988     addFile : function(file)
12989     {
12990            
12991         if(typeof(file) === 'string'){
12992             throw "Add file by name?"; // should not happen
12993             return;
12994         }
12995         
12996         if(!file || !this.urlAPI){
12997             return;
12998         }
12999         
13000         // file;
13001         // file.type;
13002         
13003         var _this = this;
13004         
13005         
13006         var url = _this.urlAPI.createObjectURL( file);
13007            
13008         this.addCard({
13009             id : Roo.bootstrap.CardUploader.ID--,
13010             is_uploaded : false,
13011             src : url,
13012             srcfile : file,
13013             title : file.name,
13014             mimetype : file.type,
13015             preview : false,
13016             is_deleted : 0
13017         });
13018         
13019     },
13020     
13021     /**
13022      * addCard - add an Attachment to the uploader
13023      * @param data - the data about the image to upload
13024      *
13025      * {
13026           id : 123
13027           title : "Title of file",
13028           is_uploaded : false,
13029           src : "http://.....",
13030           srcfile : { the File upload object },
13031           mimetype : file.type,
13032           preview : false,
13033           is_deleted : 0
13034           .. any other data...
13035         }
13036      *
13037      * 
13038     */
13039     
13040     addCard : function (data)
13041     {
13042         // hidden input element?
13043         // if the file is not an image...
13044         //then we need to use something other that and header_image
13045         var t = this;
13046         //   remove.....
13047         var footer = [
13048             {
13049                 xns : Roo.bootstrap,
13050                 xtype : 'CardFooter',
13051                  items: [
13052                     {
13053                         xns : Roo.bootstrap,
13054                         xtype : 'Element',
13055                         cls : 'd-flex',
13056                         items : [
13057                             
13058                             {
13059                                 xns : Roo.bootstrap,
13060                                 xtype : 'Button',
13061                                 html : String.format("<small>{0}</small>", data.title),
13062                                 cls : 'col-10 text-left',
13063                                 size: 'sm',
13064                                 weight: 'link',
13065                                 fa : 'download',
13066                                 listeners : {
13067                                     click : function() {
13068                                      
13069                                         t.fireEvent( "download", t, data );
13070                                     }
13071                                 }
13072                             },
13073                           
13074                             {
13075                                 xns : Roo.bootstrap,
13076                                 xtype : 'Button',
13077                                 style: 'max-height: 28px; ',
13078                                 size : 'sm',
13079                                 weight: 'danger',
13080                                 cls : 'col-2',
13081                                 fa : 'times',
13082                                 listeners : {
13083                                     click : function() {
13084                                         t.removeCard(data.id)
13085                                     }
13086                                 }
13087                             }
13088                         ]
13089                     }
13090                     
13091                 ] 
13092             }
13093             
13094         ];
13095         
13096         var cn = this.addxtype(
13097             {
13098                  
13099                 xns : Roo.bootstrap,
13100                 xtype : 'Card',
13101                 closeable : true,
13102                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13103                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13104                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13105                 data : data,
13106                 html : false,
13107                  
13108                 items : footer,
13109                 initEvents : function() {
13110                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13111                     var card = this;
13112                     this.imgEl = this.el.select('.card-img-top').first();
13113                     if (this.imgEl) {
13114                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13115                         this.imgEl.set({ 'pointer' : 'cursor' });
13116                                   
13117                     }
13118                     this.getCardFooter().addClass('p-1');
13119                     
13120                   
13121                 }
13122                 
13123             }
13124         );
13125         // dont' really need ot update items.
13126         // this.items.push(cn);
13127         this.fileCollection.add(cn);
13128         
13129         if (!data.srcfile) {
13130             this.updateInput();
13131             return;
13132         }
13133             
13134         var _t = this;
13135         var reader = new FileReader();
13136         reader.addEventListener("load", function() {  
13137             data.srcdata =  reader.result;
13138             _t.updateInput();
13139         });
13140         reader.readAsDataURL(data.srcfile);
13141         
13142         
13143         
13144     },
13145     removeCard : function(id)
13146     {
13147         
13148         var card  = this.fileCollection.get(id);
13149         card.data.is_deleted = 1;
13150         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13151         //this.fileCollection.remove(card);
13152         //this.items = this.items.filter(function(e) { return e != card });
13153         // dont' really need ot update items.
13154         card.el.dom.parentNode.removeChild(card.el.dom);
13155         this.updateInput();
13156
13157         
13158     },
13159     reset: function()
13160     {
13161         this.fileCollection.each(function(card) {
13162             if (card.el.dom && card.el.dom.parentNode) {
13163                 card.el.dom.parentNode.removeChild(card.el.dom);
13164             }
13165         });
13166         this.fileCollection.clear();
13167         this.updateInput();
13168     },
13169     
13170     updateInput : function()
13171     {
13172          var data = [];
13173         this.fileCollection.each(function(e) {
13174             data.push(e.data);
13175             
13176         });
13177         this.inputEl().dom.value = JSON.stringify(data);
13178         
13179         
13180         
13181     }
13182     
13183     
13184 });
13185
13186
13187 Roo.bootstrap.CardUploader.ID = -1;/*
13188  * Based on:
13189  * Ext JS Library 1.1.1
13190  * Copyright(c) 2006-2007, Ext JS, LLC.
13191  *
13192  * Originally Released Under LGPL - original licence link has changed is not relivant.
13193  *
13194  * Fork - LGPL
13195  * <script type="text/javascript">
13196  */
13197
13198
13199 /**
13200  * @class Roo.data.SortTypes
13201  * @singleton
13202  * Defines the default sorting (casting?) comparison functions used when sorting data.
13203  */
13204 Roo.data.SortTypes = {
13205     /**
13206      * Default sort that does nothing
13207      * @param {Mixed} s The value being converted
13208      * @return {Mixed} The comparison value
13209      */
13210     none : function(s){
13211         return s;
13212     },
13213     
13214     /**
13215      * The regular expression used to strip tags
13216      * @type {RegExp}
13217      * @property
13218      */
13219     stripTagsRE : /<\/?[^>]+>/gi,
13220     
13221     /**
13222      * Strips all HTML tags to sort on text only
13223      * @param {Mixed} s The value being converted
13224      * @return {String} The comparison value
13225      */
13226     asText : function(s){
13227         return String(s).replace(this.stripTagsRE, "");
13228     },
13229     
13230     /**
13231      * Strips all HTML tags to sort on text only - Case insensitive
13232      * @param {Mixed} s The value being converted
13233      * @return {String} The comparison value
13234      */
13235     asUCText : function(s){
13236         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13237     },
13238     
13239     /**
13240      * Case insensitive string
13241      * @param {Mixed} s The value being converted
13242      * @return {String} The comparison value
13243      */
13244     asUCString : function(s) {
13245         return String(s).toUpperCase();
13246     },
13247     
13248     /**
13249      * Date sorting
13250      * @param {Mixed} s The value being converted
13251      * @return {Number} The comparison value
13252      */
13253     asDate : function(s) {
13254         if(!s){
13255             return 0;
13256         }
13257         if(s instanceof Date){
13258             return s.getTime();
13259         }
13260         return Date.parse(String(s));
13261     },
13262     
13263     /**
13264      * Float sorting
13265      * @param {Mixed} s The value being converted
13266      * @return {Float} The comparison value
13267      */
13268     asFloat : function(s) {
13269         var val = parseFloat(String(s).replace(/,/g, ""));
13270         if(isNaN(val)) {
13271             val = 0;
13272         }
13273         return val;
13274     },
13275     
13276     /**
13277      * Integer sorting
13278      * @param {Mixed} s The value being converted
13279      * @return {Number} The comparison value
13280      */
13281     asInt : function(s) {
13282         var val = parseInt(String(s).replace(/,/g, ""));
13283         if(isNaN(val)) {
13284             val = 0;
13285         }
13286         return val;
13287     }
13288 };/*
13289  * Based on:
13290  * Ext JS Library 1.1.1
13291  * Copyright(c) 2006-2007, Ext JS, LLC.
13292  *
13293  * Originally Released Under LGPL - original licence link has changed is not relivant.
13294  *
13295  * Fork - LGPL
13296  * <script type="text/javascript">
13297  */
13298
13299 /**
13300 * @class Roo.data.Record
13301  * Instances of this class encapsulate both record <em>definition</em> information, and record
13302  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13303  * to access Records cached in an {@link Roo.data.Store} object.<br>
13304  * <p>
13305  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13306  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13307  * objects.<br>
13308  * <p>
13309  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13310  * @constructor
13311  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13312  * {@link #create}. The parameters are the same.
13313  * @param {Array} data An associative Array of data values keyed by the field name.
13314  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13315  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13316  * not specified an integer id is generated.
13317  */
13318 Roo.data.Record = function(data, id){
13319     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13320     this.data = data;
13321 };
13322
13323 /**
13324  * Generate a constructor for a specific record layout.
13325  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13326  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13327  * Each field definition object may contain the following properties: <ul>
13328  * <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,
13329  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13330  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13331  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13332  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13333  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13334  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13335  * this may be omitted.</p></li>
13336  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13337  * <ul><li>auto (Default, implies no conversion)</li>
13338  * <li>string</li>
13339  * <li>int</li>
13340  * <li>float</li>
13341  * <li>boolean</li>
13342  * <li>date</li></ul></p></li>
13343  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13344  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13345  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13346  * by the Reader into an object that will be stored in the Record. It is passed the
13347  * following parameters:<ul>
13348  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13349  * </ul></p></li>
13350  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13351  * </ul>
13352  * <br>usage:<br><pre><code>
13353 var TopicRecord = Roo.data.Record.create(
13354     {name: 'title', mapping: 'topic_title'},
13355     {name: 'author', mapping: 'username'},
13356     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13357     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13358     {name: 'lastPoster', mapping: 'user2'},
13359     {name: 'excerpt', mapping: 'post_text'}
13360 );
13361
13362 var myNewRecord = new TopicRecord({
13363     title: 'Do my job please',
13364     author: 'noobie',
13365     totalPosts: 1,
13366     lastPost: new Date(),
13367     lastPoster: 'Animal',
13368     excerpt: 'No way dude!'
13369 });
13370 myStore.add(myNewRecord);
13371 </code></pre>
13372  * @method create
13373  * @static
13374  */
13375 Roo.data.Record.create = function(o){
13376     var f = function(){
13377         f.superclass.constructor.apply(this, arguments);
13378     };
13379     Roo.extend(f, Roo.data.Record);
13380     var p = f.prototype;
13381     p.fields = new Roo.util.MixedCollection(false, function(field){
13382         return field.name;
13383     });
13384     for(var i = 0, len = o.length; i < len; i++){
13385         p.fields.add(new Roo.data.Field(o[i]));
13386     }
13387     f.getField = function(name){
13388         return p.fields.get(name);  
13389     };
13390     return f;
13391 };
13392
13393 Roo.data.Record.AUTO_ID = 1000;
13394 Roo.data.Record.EDIT = 'edit';
13395 Roo.data.Record.REJECT = 'reject';
13396 Roo.data.Record.COMMIT = 'commit';
13397
13398 Roo.data.Record.prototype = {
13399     /**
13400      * Readonly flag - true if this record has been modified.
13401      * @type Boolean
13402      */
13403     dirty : false,
13404     editing : false,
13405     error: null,
13406     modified: null,
13407
13408     // private
13409     join : function(store){
13410         this.store = store;
13411     },
13412
13413     /**
13414      * Set the named field to the specified value.
13415      * @param {String} name The name of the field to set.
13416      * @param {Object} value The value to set the field to.
13417      */
13418     set : function(name, value){
13419         if(this.data[name] == value){
13420             return;
13421         }
13422         this.dirty = true;
13423         if(!this.modified){
13424             this.modified = {};
13425         }
13426         if(typeof this.modified[name] == 'undefined'){
13427             this.modified[name] = this.data[name];
13428         }
13429         this.data[name] = value;
13430         if(!this.editing && this.store){
13431             this.store.afterEdit(this);
13432         }       
13433     },
13434
13435     /**
13436      * Get the value of the named field.
13437      * @param {String} name The name of the field to get the value of.
13438      * @return {Object} The value of the field.
13439      */
13440     get : function(name){
13441         return this.data[name]; 
13442     },
13443
13444     // private
13445     beginEdit : function(){
13446         this.editing = true;
13447         this.modified = {}; 
13448     },
13449
13450     // private
13451     cancelEdit : function(){
13452         this.editing = false;
13453         delete this.modified;
13454     },
13455
13456     // private
13457     endEdit : function(){
13458         this.editing = false;
13459         if(this.dirty && this.store){
13460             this.store.afterEdit(this);
13461         }
13462     },
13463
13464     /**
13465      * Usually called by the {@link Roo.data.Store} which owns the Record.
13466      * Rejects all changes made to the Record since either creation, or the last commit operation.
13467      * Modified fields are reverted to their original values.
13468      * <p>
13469      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13470      * of reject operations.
13471      */
13472     reject : function(){
13473         var m = this.modified;
13474         for(var n in m){
13475             if(typeof m[n] != "function"){
13476                 this.data[n] = m[n];
13477             }
13478         }
13479         this.dirty = false;
13480         delete this.modified;
13481         this.editing = false;
13482         if(this.store){
13483             this.store.afterReject(this);
13484         }
13485     },
13486
13487     /**
13488      * Usually called by the {@link Roo.data.Store} which owns the Record.
13489      * Commits all changes made to the Record since either creation, or the last commit operation.
13490      * <p>
13491      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13492      * of commit operations.
13493      */
13494     commit : function(){
13495         this.dirty = false;
13496         delete this.modified;
13497         this.editing = false;
13498         if(this.store){
13499             this.store.afterCommit(this);
13500         }
13501     },
13502
13503     // private
13504     hasError : function(){
13505         return this.error != null;
13506     },
13507
13508     // private
13509     clearError : function(){
13510         this.error = null;
13511     },
13512
13513     /**
13514      * Creates a copy of this record.
13515      * @param {String} id (optional) A new record id if you don't want to use this record's id
13516      * @return {Record}
13517      */
13518     copy : function(newId) {
13519         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13520     }
13521 };/*
13522  * Based on:
13523  * Ext JS Library 1.1.1
13524  * Copyright(c) 2006-2007, Ext JS, LLC.
13525  *
13526  * Originally Released Under LGPL - original licence link has changed is not relivant.
13527  *
13528  * Fork - LGPL
13529  * <script type="text/javascript">
13530  */
13531
13532
13533
13534 /**
13535  * @class Roo.data.Store
13536  * @extends Roo.util.Observable
13537  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13538  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13539  * <p>
13540  * 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
13541  * has no knowledge of the format of the data returned by the Proxy.<br>
13542  * <p>
13543  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13544  * instances from the data object. These records are cached and made available through accessor functions.
13545  * @constructor
13546  * Creates a new Store.
13547  * @param {Object} config A config object containing the objects needed for the Store to access data,
13548  * and read the data into Records.
13549  */
13550 Roo.data.Store = function(config){
13551     this.data = new Roo.util.MixedCollection(false);
13552     this.data.getKey = function(o){
13553         return o.id;
13554     };
13555     this.baseParams = {};
13556     // private
13557     this.paramNames = {
13558         "start" : "start",
13559         "limit" : "limit",
13560         "sort" : "sort",
13561         "dir" : "dir",
13562         "multisort" : "_multisort"
13563     };
13564
13565     if(config && config.data){
13566         this.inlineData = config.data;
13567         delete config.data;
13568     }
13569
13570     Roo.apply(this, config);
13571     
13572     if(this.reader){ // reader passed
13573         this.reader = Roo.factory(this.reader, Roo.data);
13574         this.reader.xmodule = this.xmodule || false;
13575         if(!this.recordType){
13576             this.recordType = this.reader.recordType;
13577         }
13578         if(this.reader.onMetaChange){
13579             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13580         }
13581     }
13582
13583     if(this.recordType){
13584         this.fields = this.recordType.prototype.fields;
13585     }
13586     this.modified = [];
13587
13588     this.addEvents({
13589         /**
13590          * @event datachanged
13591          * Fires when the data cache has changed, and a widget which is using this Store
13592          * as a Record cache should refresh its view.
13593          * @param {Store} this
13594          */
13595         datachanged : true,
13596         /**
13597          * @event metachange
13598          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13599          * @param {Store} this
13600          * @param {Object} meta The JSON metadata
13601          */
13602         metachange : true,
13603         /**
13604          * @event add
13605          * Fires when Records have been added to the Store
13606          * @param {Store} this
13607          * @param {Roo.data.Record[]} records The array of Records added
13608          * @param {Number} index The index at which the record(s) were added
13609          */
13610         add : true,
13611         /**
13612          * @event remove
13613          * Fires when a Record has been removed from the Store
13614          * @param {Store} this
13615          * @param {Roo.data.Record} record The Record that was removed
13616          * @param {Number} index The index at which the record was removed
13617          */
13618         remove : true,
13619         /**
13620          * @event update
13621          * Fires when a Record has been updated
13622          * @param {Store} this
13623          * @param {Roo.data.Record} record The Record that was updated
13624          * @param {String} operation The update operation being performed.  Value may be one of:
13625          * <pre><code>
13626  Roo.data.Record.EDIT
13627  Roo.data.Record.REJECT
13628  Roo.data.Record.COMMIT
13629          * </code></pre>
13630          */
13631         update : true,
13632         /**
13633          * @event clear
13634          * Fires when the data cache has been cleared.
13635          * @param {Store} this
13636          */
13637         clear : true,
13638         /**
13639          * @event beforeload
13640          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13641          * the load action will be canceled.
13642          * @param {Store} this
13643          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13644          */
13645         beforeload : true,
13646         /**
13647          * @event beforeloadadd
13648          * Fires after a new set of Records has been loaded.
13649          * @param {Store} this
13650          * @param {Roo.data.Record[]} records The Records that were loaded
13651          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13652          */
13653         beforeloadadd : true,
13654         /**
13655          * @event load
13656          * Fires after a new set of Records has been loaded, before they are added to the store.
13657          * @param {Store} this
13658          * @param {Roo.data.Record[]} records The Records that were loaded
13659          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13660          * @params {Object} return from reader
13661          */
13662         load : true,
13663         /**
13664          * @event loadexception
13665          * Fires if an exception occurs in the Proxy during loading.
13666          * Called with the signature of the Proxy's "loadexception" event.
13667          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13668          * 
13669          * @param {Proxy} 
13670          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13671          * @param {Object} load options 
13672          * @param {Object} jsonData from your request (normally this contains the Exception)
13673          */
13674         loadexception : true
13675     });
13676     
13677     if(this.proxy){
13678         this.proxy = Roo.factory(this.proxy, Roo.data);
13679         this.proxy.xmodule = this.xmodule || false;
13680         this.relayEvents(this.proxy,  ["loadexception"]);
13681     }
13682     this.sortToggle = {};
13683     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13684
13685     Roo.data.Store.superclass.constructor.call(this);
13686
13687     if(this.inlineData){
13688         this.loadData(this.inlineData);
13689         delete this.inlineData;
13690     }
13691 };
13692
13693 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13694      /**
13695     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13696     * without a remote query - used by combo/forms at present.
13697     */
13698     
13699     /**
13700     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13701     */
13702     /**
13703     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13704     */
13705     /**
13706     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13707     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13708     */
13709     /**
13710     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13711     * on any HTTP request
13712     */
13713     /**
13714     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13715     */
13716     /**
13717     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13718     */
13719     multiSort: false,
13720     /**
13721     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13722     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13723     */
13724     remoteSort : false,
13725
13726     /**
13727     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13728      * loaded or when a record is removed. (defaults to false).
13729     */
13730     pruneModifiedRecords : false,
13731
13732     // private
13733     lastOptions : null,
13734
13735     /**
13736      * Add Records to the Store and fires the add event.
13737      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13738      */
13739     add : function(records){
13740         records = [].concat(records);
13741         for(var i = 0, len = records.length; i < len; i++){
13742             records[i].join(this);
13743         }
13744         var index = this.data.length;
13745         this.data.addAll(records);
13746         this.fireEvent("add", this, records, index);
13747     },
13748
13749     /**
13750      * Remove a Record from the Store and fires the remove event.
13751      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13752      */
13753     remove : function(record){
13754         var index = this.data.indexOf(record);
13755         this.data.removeAt(index);
13756  
13757         if(this.pruneModifiedRecords){
13758             this.modified.remove(record);
13759         }
13760         this.fireEvent("remove", this, record, index);
13761     },
13762
13763     /**
13764      * Remove all Records from the Store and fires the clear event.
13765      */
13766     removeAll : function(){
13767         this.data.clear();
13768         if(this.pruneModifiedRecords){
13769             this.modified = [];
13770         }
13771         this.fireEvent("clear", this);
13772     },
13773
13774     /**
13775      * Inserts Records to the Store at the given index and fires the add event.
13776      * @param {Number} index The start index at which to insert the passed Records.
13777      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13778      */
13779     insert : function(index, records){
13780         records = [].concat(records);
13781         for(var i = 0, len = records.length; i < len; i++){
13782             this.data.insert(index, records[i]);
13783             records[i].join(this);
13784         }
13785         this.fireEvent("add", this, records, index);
13786     },
13787
13788     /**
13789      * Get the index within the cache of the passed Record.
13790      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13791      * @return {Number} The index of the passed Record. Returns -1 if not found.
13792      */
13793     indexOf : function(record){
13794         return this.data.indexOf(record);
13795     },
13796
13797     /**
13798      * Get the index within the cache of the Record with the passed id.
13799      * @param {String} id The id of the Record to find.
13800      * @return {Number} The index of the Record. Returns -1 if not found.
13801      */
13802     indexOfId : function(id){
13803         return this.data.indexOfKey(id);
13804     },
13805
13806     /**
13807      * Get the Record with the specified id.
13808      * @param {String} id The id of the Record to find.
13809      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13810      */
13811     getById : function(id){
13812         return this.data.key(id);
13813     },
13814
13815     /**
13816      * Get the Record at the specified index.
13817      * @param {Number} index The index of the Record to find.
13818      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13819      */
13820     getAt : function(index){
13821         return this.data.itemAt(index);
13822     },
13823
13824     /**
13825      * Returns a range of Records between specified indices.
13826      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13827      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13828      * @return {Roo.data.Record[]} An array of Records
13829      */
13830     getRange : function(start, end){
13831         return this.data.getRange(start, end);
13832     },
13833
13834     // private
13835     storeOptions : function(o){
13836         o = Roo.apply({}, o);
13837         delete o.callback;
13838         delete o.scope;
13839         this.lastOptions = o;
13840     },
13841
13842     /**
13843      * Loads the Record cache from the configured Proxy using the configured Reader.
13844      * <p>
13845      * If using remote paging, then the first load call must specify the <em>start</em>
13846      * and <em>limit</em> properties in the options.params property to establish the initial
13847      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13848      * <p>
13849      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13850      * and this call will return before the new data has been loaded. Perform any post-processing
13851      * in a callback function, or in a "load" event handler.</strong>
13852      * <p>
13853      * @param {Object} options An object containing properties which control loading options:<ul>
13854      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13855      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13856      * passed the following arguments:<ul>
13857      * <li>r : Roo.data.Record[]</li>
13858      * <li>options: Options object from the load call</li>
13859      * <li>success: Boolean success indicator</li></ul></li>
13860      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13861      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13862      * </ul>
13863      */
13864     load : function(options){
13865         options = options || {};
13866         if(this.fireEvent("beforeload", this, options) !== false){
13867             this.storeOptions(options);
13868             var p = Roo.apply(options.params || {}, this.baseParams);
13869             // if meta was not loaded from remote source.. try requesting it.
13870             if (!this.reader.metaFromRemote) {
13871                 p._requestMeta = 1;
13872             }
13873             if(this.sortInfo && this.remoteSort){
13874                 var pn = this.paramNames;
13875                 p[pn["sort"]] = this.sortInfo.field;
13876                 p[pn["dir"]] = this.sortInfo.direction;
13877             }
13878             if (this.multiSort) {
13879                 var pn = this.paramNames;
13880                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13881             }
13882             
13883             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13884         }
13885     },
13886
13887     /**
13888      * Reloads the Record cache from the configured Proxy using the configured Reader and
13889      * the options from the last load operation performed.
13890      * @param {Object} options (optional) An object containing properties which may override the options
13891      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13892      * the most recently used options are reused).
13893      */
13894     reload : function(options){
13895         this.load(Roo.applyIf(options||{}, this.lastOptions));
13896     },
13897
13898     // private
13899     // Called as a callback by the Reader during a load operation.
13900     loadRecords : function(o, options, success){
13901         if(!o || success === false){
13902             if(success !== false){
13903                 this.fireEvent("load", this, [], options, o);
13904             }
13905             if(options.callback){
13906                 options.callback.call(options.scope || this, [], options, false);
13907             }
13908             return;
13909         }
13910         // if data returned failure - throw an exception.
13911         if (o.success === false) {
13912             // show a message if no listener is registered.
13913             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13914                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13915             }
13916             // loadmask wil be hooked into this..
13917             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13918             return;
13919         }
13920         var r = o.records, t = o.totalRecords || r.length;
13921         
13922         this.fireEvent("beforeloadadd", this, r, options, o);
13923         
13924         if(!options || options.add !== true){
13925             if(this.pruneModifiedRecords){
13926                 this.modified = [];
13927             }
13928             for(var i = 0, len = r.length; i < len; i++){
13929                 r[i].join(this);
13930             }
13931             if(this.snapshot){
13932                 this.data = this.snapshot;
13933                 delete this.snapshot;
13934             }
13935             this.data.clear();
13936             this.data.addAll(r);
13937             this.totalLength = t;
13938             this.applySort();
13939             this.fireEvent("datachanged", this);
13940         }else{
13941             this.totalLength = Math.max(t, this.data.length+r.length);
13942             this.add(r);
13943         }
13944         
13945         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13946                 
13947             var e = new Roo.data.Record({});
13948
13949             e.set(this.parent.displayField, this.parent.emptyTitle);
13950             e.set(this.parent.valueField, '');
13951
13952             this.insert(0, e);
13953         }
13954             
13955         this.fireEvent("load", this, r, options, o);
13956         if(options.callback){
13957             options.callback.call(options.scope || this, r, options, true);
13958         }
13959     },
13960
13961
13962     /**
13963      * Loads data from a passed data block. A Reader which understands the format of the data
13964      * must have been configured in the constructor.
13965      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13966      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13967      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13968      */
13969     loadData : function(o, append){
13970         var r = this.reader.readRecords(o);
13971         this.loadRecords(r, {add: append}, true);
13972     },
13973     
13974      /**
13975      * using 'cn' the nested child reader read the child array into it's child stores.
13976      * @param {Object} rec The record with a 'children array
13977      */
13978     loadDataFromChildren : function(rec)
13979     {
13980         this.loadData(this.reader.toLoadData(rec));
13981     },
13982     
13983
13984     /**
13985      * Gets the number of cached records.
13986      * <p>
13987      * <em>If using paging, this may not be the total size of the dataset. If the data object
13988      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13989      * the data set size</em>
13990      */
13991     getCount : function(){
13992         return this.data.length || 0;
13993     },
13994
13995     /**
13996      * Gets the total number of records in the dataset as returned by the server.
13997      * <p>
13998      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13999      * the dataset size</em>
14000      */
14001     getTotalCount : function(){
14002         return this.totalLength || 0;
14003     },
14004
14005     /**
14006      * Returns the sort state of the Store as an object with two properties:
14007      * <pre><code>
14008  field {String} The name of the field by which the Records are sorted
14009  direction {String} The sort order, "ASC" or "DESC"
14010      * </code></pre>
14011      */
14012     getSortState : function(){
14013         return this.sortInfo;
14014     },
14015
14016     // private
14017     applySort : function(){
14018         if(this.sortInfo && !this.remoteSort){
14019             var s = this.sortInfo, f = s.field;
14020             var st = this.fields.get(f).sortType;
14021             var fn = function(r1, r2){
14022                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14023                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14024             };
14025             this.data.sort(s.direction, fn);
14026             if(this.snapshot && this.snapshot != this.data){
14027                 this.snapshot.sort(s.direction, fn);
14028             }
14029         }
14030     },
14031
14032     /**
14033      * Sets the default sort column and order to be used by the next load operation.
14034      * @param {String} fieldName The name of the field to sort by.
14035      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14036      */
14037     setDefaultSort : function(field, dir){
14038         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14039     },
14040
14041     /**
14042      * Sort the Records.
14043      * If remote sorting is used, the sort is performed on the server, and the cache is
14044      * reloaded. If local sorting is used, the cache is sorted internally.
14045      * @param {String} fieldName The name of the field to sort by.
14046      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14047      */
14048     sort : function(fieldName, dir){
14049         var f = this.fields.get(fieldName);
14050         if(!dir){
14051             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14052             
14053             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14054                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14055             }else{
14056                 dir = f.sortDir;
14057             }
14058         }
14059         this.sortToggle[f.name] = dir;
14060         this.sortInfo = {field: f.name, direction: dir};
14061         if(!this.remoteSort){
14062             this.applySort();
14063             this.fireEvent("datachanged", this);
14064         }else{
14065             this.load(this.lastOptions);
14066         }
14067     },
14068
14069     /**
14070      * Calls the specified function for each of the Records in the cache.
14071      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14072      * Returning <em>false</em> aborts and exits the iteration.
14073      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14074      */
14075     each : function(fn, scope){
14076         this.data.each(fn, scope);
14077     },
14078
14079     /**
14080      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14081      * (e.g., during paging).
14082      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14083      */
14084     getModifiedRecords : function(){
14085         return this.modified;
14086     },
14087
14088     // private
14089     createFilterFn : function(property, value, anyMatch){
14090         if(!value.exec){ // not a regex
14091             value = String(value);
14092             if(value.length == 0){
14093                 return false;
14094             }
14095             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14096         }
14097         return function(r){
14098             return value.test(r.data[property]);
14099         };
14100     },
14101
14102     /**
14103      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14104      * @param {String} property A field on your records
14105      * @param {Number} start The record index to start at (defaults to 0)
14106      * @param {Number} end The last record index to include (defaults to length - 1)
14107      * @return {Number} The sum
14108      */
14109     sum : function(property, start, end){
14110         var rs = this.data.items, v = 0;
14111         start = start || 0;
14112         end = (end || end === 0) ? end : rs.length-1;
14113
14114         for(var i = start; i <= end; i++){
14115             v += (rs[i].data[property] || 0);
14116         }
14117         return v;
14118     },
14119
14120     /**
14121      * Filter the records by a specified property.
14122      * @param {String} field A field on your records
14123      * @param {String/RegExp} value Either a string that the field
14124      * should start with or a RegExp to test against the field
14125      * @param {Boolean} anyMatch True to match any part not just the beginning
14126      */
14127     filter : function(property, value, anyMatch){
14128         var fn = this.createFilterFn(property, value, anyMatch);
14129         return fn ? this.filterBy(fn) : this.clearFilter();
14130     },
14131
14132     /**
14133      * Filter by a function. The specified function will be called with each
14134      * record in this data source. If the function returns true the record is included,
14135      * otherwise it is filtered.
14136      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14137      * @param {Object} scope (optional) The scope of the function (defaults to this)
14138      */
14139     filterBy : function(fn, scope){
14140         this.snapshot = this.snapshot || this.data;
14141         this.data = this.queryBy(fn, scope||this);
14142         this.fireEvent("datachanged", this);
14143     },
14144
14145     /**
14146      * Query the records by a specified property.
14147      * @param {String} field A field on your records
14148      * @param {String/RegExp} value Either a string that the field
14149      * should start with or a RegExp to test against the field
14150      * @param {Boolean} anyMatch True to match any part not just the beginning
14151      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14152      */
14153     query : function(property, value, anyMatch){
14154         var fn = this.createFilterFn(property, value, anyMatch);
14155         return fn ? this.queryBy(fn) : this.data.clone();
14156     },
14157
14158     /**
14159      * Query by a function. The specified function will be called with each
14160      * record in this data source. If the function returns true the record is included
14161      * in the results.
14162      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14163      * @param {Object} scope (optional) The scope of the function (defaults to this)
14164       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14165      **/
14166     queryBy : function(fn, scope){
14167         var data = this.snapshot || this.data;
14168         return data.filterBy(fn, scope||this);
14169     },
14170
14171     /**
14172      * Collects unique values for a particular dataIndex from this store.
14173      * @param {String} dataIndex The property to collect
14174      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14175      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14176      * @return {Array} An array of the unique values
14177      **/
14178     collect : function(dataIndex, allowNull, bypassFilter){
14179         var d = (bypassFilter === true && this.snapshot) ?
14180                 this.snapshot.items : this.data.items;
14181         var v, sv, r = [], l = {};
14182         for(var i = 0, len = d.length; i < len; i++){
14183             v = d[i].data[dataIndex];
14184             sv = String(v);
14185             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14186                 l[sv] = true;
14187                 r[r.length] = v;
14188             }
14189         }
14190         return r;
14191     },
14192
14193     /**
14194      * Revert to a view of the Record cache with no filtering applied.
14195      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14196      */
14197     clearFilter : function(suppressEvent){
14198         if(this.snapshot && this.snapshot != this.data){
14199             this.data = this.snapshot;
14200             delete this.snapshot;
14201             if(suppressEvent !== true){
14202                 this.fireEvent("datachanged", this);
14203             }
14204         }
14205     },
14206
14207     // private
14208     afterEdit : function(record){
14209         if(this.modified.indexOf(record) == -1){
14210             this.modified.push(record);
14211         }
14212         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14213     },
14214     
14215     // private
14216     afterReject : function(record){
14217         this.modified.remove(record);
14218         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14219     },
14220
14221     // private
14222     afterCommit : function(record){
14223         this.modified.remove(record);
14224         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14225     },
14226
14227     /**
14228      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14229      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14230      */
14231     commitChanges : function(){
14232         var m = this.modified.slice(0);
14233         this.modified = [];
14234         for(var i = 0, len = m.length; i < len; i++){
14235             m[i].commit();
14236         }
14237     },
14238
14239     /**
14240      * Cancel outstanding changes on all changed records.
14241      */
14242     rejectChanges : function(){
14243         var m = this.modified.slice(0);
14244         this.modified = [];
14245         for(var i = 0, len = m.length; i < len; i++){
14246             m[i].reject();
14247         }
14248     },
14249
14250     onMetaChange : function(meta, rtype, o){
14251         this.recordType = rtype;
14252         this.fields = rtype.prototype.fields;
14253         delete this.snapshot;
14254         this.sortInfo = meta.sortInfo || this.sortInfo;
14255         this.modified = [];
14256         this.fireEvent('metachange', this, this.reader.meta);
14257     },
14258     
14259     moveIndex : function(data, type)
14260     {
14261         var index = this.indexOf(data);
14262         
14263         var newIndex = index + type;
14264         
14265         this.remove(data);
14266         
14267         this.insert(newIndex, data);
14268         
14269     }
14270 });/*
14271  * Based on:
14272  * Ext JS Library 1.1.1
14273  * Copyright(c) 2006-2007, Ext JS, LLC.
14274  *
14275  * Originally Released Under LGPL - original licence link has changed is not relivant.
14276  *
14277  * Fork - LGPL
14278  * <script type="text/javascript">
14279  */
14280
14281 /**
14282  * @class Roo.data.SimpleStore
14283  * @extends Roo.data.Store
14284  * Small helper class to make creating Stores from Array data easier.
14285  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14286  * @cfg {Array} fields An array of field definition objects, or field name strings.
14287  * @cfg {Object} an existing reader (eg. copied from another store)
14288  * @cfg {Array} data The multi-dimensional array of data
14289  * @constructor
14290  * @param {Object} config
14291  */
14292 Roo.data.SimpleStore = function(config)
14293 {
14294     Roo.data.SimpleStore.superclass.constructor.call(this, {
14295         isLocal : true,
14296         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14297                 id: config.id
14298             },
14299             Roo.data.Record.create(config.fields)
14300         ),
14301         proxy : new Roo.data.MemoryProxy(config.data)
14302     });
14303     this.load();
14304 };
14305 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14306  * Based on:
14307  * Ext JS Library 1.1.1
14308  * Copyright(c) 2006-2007, Ext JS, LLC.
14309  *
14310  * Originally Released Under LGPL - original licence link has changed is not relivant.
14311  *
14312  * Fork - LGPL
14313  * <script type="text/javascript">
14314  */
14315
14316 /**
14317 /**
14318  * @extends Roo.data.Store
14319  * @class Roo.data.JsonStore
14320  * Small helper class to make creating Stores for JSON data easier. <br/>
14321 <pre><code>
14322 var store = new Roo.data.JsonStore({
14323     url: 'get-images.php',
14324     root: 'images',
14325     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14326 });
14327 </code></pre>
14328  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14329  * JsonReader and HttpProxy (unless inline data is provided).</b>
14330  * @cfg {Array} fields An array of field definition objects, or field name strings.
14331  * @constructor
14332  * @param {Object} config
14333  */
14334 Roo.data.JsonStore = function(c){
14335     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14336         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14337         reader: new Roo.data.JsonReader(c, c.fields)
14338     }));
14339 };
14340 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14341  * Based on:
14342  * Ext JS Library 1.1.1
14343  * Copyright(c) 2006-2007, Ext JS, LLC.
14344  *
14345  * Originally Released Under LGPL - original licence link has changed is not relivant.
14346  *
14347  * Fork - LGPL
14348  * <script type="text/javascript">
14349  */
14350
14351  
14352 Roo.data.Field = function(config){
14353     if(typeof config == "string"){
14354         config = {name: config};
14355     }
14356     Roo.apply(this, config);
14357     
14358     if(!this.type){
14359         this.type = "auto";
14360     }
14361     
14362     var st = Roo.data.SortTypes;
14363     // named sortTypes are supported, here we look them up
14364     if(typeof this.sortType == "string"){
14365         this.sortType = st[this.sortType];
14366     }
14367     
14368     // set default sortType for strings and dates
14369     if(!this.sortType){
14370         switch(this.type){
14371             case "string":
14372                 this.sortType = st.asUCString;
14373                 break;
14374             case "date":
14375                 this.sortType = st.asDate;
14376                 break;
14377             default:
14378                 this.sortType = st.none;
14379         }
14380     }
14381
14382     // define once
14383     var stripRe = /[\$,%]/g;
14384
14385     // prebuilt conversion function for this field, instead of
14386     // switching every time we're reading a value
14387     if(!this.convert){
14388         var cv, dateFormat = this.dateFormat;
14389         switch(this.type){
14390             case "":
14391             case "auto":
14392             case undefined:
14393                 cv = function(v){ return v; };
14394                 break;
14395             case "string":
14396                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14397                 break;
14398             case "int":
14399                 cv = function(v){
14400                     return v !== undefined && v !== null && v !== '' ?
14401                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14402                     };
14403                 break;
14404             case "float":
14405                 cv = function(v){
14406                     return v !== undefined && v !== null && v !== '' ?
14407                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14408                     };
14409                 break;
14410             case "bool":
14411             case "boolean":
14412                 cv = function(v){ return v === true || v === "true" || v == 1; };
14413                 break;
14414             case "date":
14415                 cv = function(v){
14416                     if(!v){
14417                         return '';
14418                     }
14419                     if(v instanceof Date){
14420                         return v;
14421                     }
14422                     if(dateFormat){
14423                         if(dateFormat == "timestamp"){
14424                             return new Date(v*1000);
14425                         }
14426                         return Date.parseDate(v, dateFormat);
14427                     }
14428                     var parsed = Date.parse(v);
14429                     return parsed ? new Date(parsed) : null;
14430                 };
14431              break;
14432             
14433         }
14434         this.convert = cv;
14435     }
14436 };
14437
14438 Roo.data.Field.prototype = {
14439     dateFormat: null,
14440     defaultValue: "",
14441     mapping: null,
14442     sortType : null,
14443     sortDir : "ASC"
14444 };/*
14445  * Based on:
14446  * Ext JS Library 1.1.1
14447  * Copyright(c) 2006-2007, Ext JS, LLC.
14448  *
14449  * Originally Released Under LGPL - original licence link has changed is not relivant.
14450  *
14451  * Fork - LGPL
14452  * <script type="text/javascript">
14453  */
14454  
14455 // Base class for reading structured data from a data source.  This class is intended to be
14456 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14457
14458 /**
14459  * @class Roo.data.DataReader
14460  * Base class for reading structured data from a data source.  This class is intended to be
14461  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14462  */
14463
14464 Roo.data.DataReader = function(meta, recordType){
14465     
14466     this.meta = meta;
14467     
14468     this.recordType = recordType instanceof Array ? 
14469         Roo.data.Record.create(recordType) : recordType;
14470 };
14471
14472 Roo.data.DataReader.prototype = {
14473     
14474     
14475     readerType : 'Data',
14476      /**
14477      * Create an empty record
14478      * @param {Object} data (optional) - overlay some values
14479      * @return {Roo.data.Record} record created.
14480      */
14481     newRow :  function(d) {
14482         var da =  {};
14483         this.recordType.prototype.fields.each(function(c) {
14484             switch( c.type) {
14485                 case 'int' : da[c.name] = 0; break;
14486                 case 'date' : da[c.name] = new Date(); break;
14487                 case 'float' : da[c.name] = 0.0; break;
14488                 case 'boolean' : da[c.name] = false; break;
14489                 default : da[c.name] = ""; break;
14490             }
14491             
14492         });
14493         return new this.recordType(Roo.apply(da, d));
14494     }
14495     
14496     
14497 };/*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507
14508 /**
14509  * @class Roo.data.DataProxy
14510  * @extends Roo.data.Observable
14511  * This class is an abstract base class for implementations which provide retrieval of
14512  * unformatted data objects.<br>
14513  * <p>
14514  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14515  * (of the appropriate type which knows how to parse the data object) to provide a block of
14516  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14517  * <p>
14518  * Custom implementations must implement the load method as described in
14519  * {@link Roo.data.HttpProxy#load}.
14520  */
14521 Roo.data.DataProxy = function(){
14522     this.addEvents({
14523         /**
14524          * @event beforeload
14525          * Fires before a network request is made to retrieve a data object.
14526          * @param {Object} This DataProxy object.
14527          * @param {Object} params The params parameter to the load function.
14528          */
14529         beforeload : true,
14530         /**
14531          * @event load
14532          * Fires before the load method's callback is called.
14533          * @param {Object} This DataProxy object.
14534          * @param {Object} o The data object.
14535          * @param {Object} arg The callback argument object passed to the load function.
14536          */
14537         load : true,
14538         /**
14539          * @event loadexception
14540          * Fires if an Exception occurs during data retrieval.
14541          * @param {Object} This DataProxy object.
14542          * @param {Object} o The data object.
14543          * @param {Object} arg The callback argument object passed to the load function.
14544          * @param {Object} e The Exception.
14545          */
14546         loadexception : true
14547     });
14548     Roo.data.DataProxy.superclass.constructor.call(this);
14549 };
14550
14551 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14552
14553     /**
14554      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14555      */
14556 /*
14557  * Based on:
14558  * Ext JS Library 1.1.1
14559  * Copyright(c) 2006-2007, Ext JS, LLC.
14560  *
14561  * Originally Released Under LGPL - original licence link has changed is not relivant.
14562  *
14563  * Fork - LGPL
14564  * <script type="text/javascript">
14565  */
14566 /**
14567  * @class Roo.data.MemoryProxy
14568  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14569  * to the Reader when its load method is called.
14570  * @constructor
14571  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14572  */
14573 Roo.data.MemoryProxy = function(data){
14574     if (data.data) {
14575         data = data.data;
14576     }
14577     Roo.data.MemoryProxy.superclass.constructor.call(this);
14578     this.data = data;
14579 };
14580
14581 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14582     
14583     /**
14584      * Load data from the requested source (in this case an in-memory
14585      * data object passed to the constructor), read the data object into
14586      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14587      * process that block using the passed callback.
14588      * @param {Object} params This parameter is not used by the MemoryProxy class.
14589      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14590      * object into a block of Roo.data.Records.
14591      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14592      * The function must be passed <ul>
14593      * <li>The Record block object</li>
14594      * <li>The "arg" argument from the load function</li>
14595      * <li>A boolean success indicator</li>
14596      * </ul>
14597      * @param {Object} scope The scope in which to call the callback
14598      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14599      */
14600     load : function(params, reader, callback, scope, arg){
14601         params = params || {};
14602         var result;
14603         try {
14604             result = reader.readRecords(params.data ? params.data :this.data);
14605         }catch(e){
14606             this.fireEvent("loadexception", this, arg, null, e);
14607             callback.call(scope, null, arg, false);
14608             return;
14609         }
14610         callback.call(scope, result, arg, true);
14611     },
14612     
14613     // private
14614     update : function(params, records){
14615         
14616     }
14617 });/*
14618  * Based on:
14619  * Ext JS Library 1.1.1
14620  * Copyright(c) 2006-2007, Ext JS, LLC.
14621  *
14622  * Originally Released Under LGPL - original licence link has changed is not relivant.
14623  *
14624  * Fork - LGPL
14625  * <script type="text/javascript">
14626  */
14627 /**
14628  * @class Roo.data.HttpProxy
14629  * @extends Roo.data.DataProxy
14630  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14631  * configured to reference a certain URL.<br><br>
14632  * <p>
14633  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14634  * from which the running page was served.<br><br>
14635  * <p>
14636  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14637  * <p>
14638  * Be aware that to enable the browser to parse an XML document, the server must set
14639  * the Content-Type header in the HTTP response to "text/xml".
14640  * @constructor
14641  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14642  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14643  * will be used to make the request.
14644  */
14645 Roo.data.HttpProxy = function(conn){
14646     Roo.data.HttpProxy.superclass.constructor.call(this);
14647     // is conn a conn config or a real conn?
14648     this.conn = conn;
14649     this.useAjax = !conn || !conn.events;
14650   
14651 };
14652
14653 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14654     // thse are take from connection...
14655     
14656     /**
14657      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14658      */
14659     /**
14660      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14661      * extra parameters to each request made by this object. (defaults to undefined)
14662      */
14663     /**
14664      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14665      *  to each request made by this object. (defaults to undefined)
14666      */
14667     /**
14668      * @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)
14669      */
14670     /**
14671      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14672      */
14673      /**
14674      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14675      * @type Boolean
14676      */
14677   
14678
14679     /**
14680      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14681      * @type Boolean
14682      */
14683     /**
14684      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14685      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14686      * a finer-grained basis than the DataProxy events.
14687      */
14688     getConnection : function(){
14689         return this.useAjax ? Roo.Ajax : this.conn;
14690     },
14691
14692     /**
14693      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14694      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14695      * process that block using the passed callback.
14696      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14697      * for the request to the remote server.
14698      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14699      * object into a block of Roo.data.Records.
14700      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14701      * The function must be passed <ul>
14702      * <li>The Record block object</li>
14703      * <li>The "arg" argument from the load function</li>
14704      * <li>A boolean success indicator</li>
14705      * </ul>
14706      * @param {Object} scope The scope in which to call the callback
14707      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14708      */
14709     load : function(params, reader, callback, scope, arg){
14710         if(this.fireEvent("beforeload", this, params) !== false){
14711             var  o = {
14712                 params : params || {},
14713                 request: {
14714                     callback : callback,
14715                     scope : scope,
14716                     arg : arg
14717                 },
14718                 reader: reader,
14719                 callback : this.loadResponse,
14720                 scope: this
14721             };
14722             if(this.useAjax){
14723                 Roo.applyIf(o, this.conn);
14724                 if(this.activeRequest){
14725                     Roo.Ajax.abort(this.activeRequest);
14726                 }
14727                 this.activeRequest = Roo.Ajax.request(o);
14728             }else{
14729                 this.conn.request(o);
14730             }
14731         }else{
14732             callback.call(scope||this, null, arg, false);
14733         }
14734     },
14735
14736     // private
14737     loadResponse : function(o, success, response){
14738         delete this.activeRequest;
14739         if(!success){
14740             this.fireEvent("loadexception", this, o, response);
14741             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14742             return;
14743         }
14744         var result;
14745         try {
14746             result = o.reader.read(response);
14747         }catch(e){
14748             this.fireEvent("loadexception", this, o, response, e);
14749             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14750             return;
14751         }
14752         
14753         this.fireEvent("load", this, o, o.request.arg);
14754         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14755     },
14756
14757     // private
14758     update : function(dataSet){
14759
14760     },
14761
14762     // private
14763     updateResponse : function(dataSet){
14764
14765     }
14766 });/*
14767  * Based on:
14768  * Ext JS Library 1.1.1
14769  * Copyright(c) 2006-2007, Ext JS, LLC.
14770  *
14771  * Originally Released Under LGPL - original licence link has changed is not relivant.
14772  *
14773  * Fork - LGPL
14774  * <script type="text/javascript">
14775  */
14776
14777 /**
14778  * @class Roo.data.ScriptTagProxy
14779  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14780  * other than the originating domain of the running page.<br><br>
14781  * <p>
14782  * <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
14783  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14784  * <p>
14785  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14786  * source code that is used as the source inside a &lt;script> tag.<br><br>
14787  * <p>
14788  * In order for the browser to process the returned data, the server must wrap the data object
14789  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14790  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14791  * depending on whether the callback name was passed:
14792  * <p>
14793  * <pre><code>
14794 boolean scriptTag = false;
14795 String cb = request.getParameter("callback");
14796 if (cb != null) {
14797     scriptTag = true;
14798     response.setContentType("text/javascript");
14799 } else {
14800     response.setContentType("application/x-json");
14801 }
14802 Writer out = response.getWriter();
14803 if (scriptTag) {
14804     out.write(cb + "(");
14805 }
14806 out.print(dataBlock.toJsonString());
14807 if (scriptTag) {
14808     out.write(");");
14809 }
14810 </pre></code>
14811  *
14812  * @constructor
14813  * @param {Object} config A configuration object.
14814  */
14815 Roo.data.ScriptTagProxy = function(config){
14816     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14817     Roo.apply(this, config);
14818     this.head = document.getElementsByTagName("head")[0];
14819 };
14820
14821 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14822
14823 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14824     /**
14825      * @cfg {String} url The URL from which to request the data object.
14826      */
14827     /**
14828      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14829      */
14830     timeout : 30000,
14831     /**
14832      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14833      * the server the name of the callback function set up by the load call to process the returned data object.
14834      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14835      * javascript output which calls this named function passing the data object as its only parameter.
14836      */
14837     callbackParam : "callback",
14838     /**
14839      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14840      * name to the request.
14841      */
14842     nocache : true,
14843
14844     /**
14845      * Load data from the configured URL, read the data object into
14846      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14847      * process that block using the passed callback.
14848      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14849      * for the request to the remote server.
14850      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14851      * object into a block of Roo.data.Records.
14852      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14853      * The function must be passed <ul>
14854      * <li>The Record block object</li>
14855      * <li>The "arg" argument from the load function</li>
14856      * <li>A boolean success indicator</li>
14857      * </ul>
14858      * @param {Object} scope The scope in which to call the callback
14859      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14860      */
14861     load : function(params, reader, callback, scope, arg){
14862         if(this.fireEvent("beforeload", this, params) !== false){
14863
14864             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14865
14866             var url = this.url;
14867             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14868             if(this.nocache){
14869                 url += "&_dc=" + (new Date().getTime());
14870             }
14871             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14872             var trans = {
14873                 id : transId,
14874                 cb : "stcCallback"+transId,
14875                 scriptId : "stcScript"+transId,
14876                 params : params,
14877                 arg : arg,
14878                 url : url,
14879                 callback : callback,
14880                 scope : scope,
14881                 reader : reader
14882             };
14883             var conn = this;
14884
14885             window[trans.cb] = function(o){
14886                 conn.handleResponse(o, trans);
14887             };
14888
14889             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14890
14891             if(this.autoAbort !== false){
14892                 this.abort();
14893             }
14894
14895             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14896
14897             var script = document.createElement("script");
14898             script.setAttribute("src", url);
14899             script.setAttribute("type", "text/javascript");
14900             script.setAttribute("id", trans.scriptId);
14901             this.head.appendChild(script);
14902
14903             this.trans = trans;
14904         }else{
14905             callback.call(scope||this, null, arg, false);
14906         }
14907     },
14908
14909     // private
14910     isLoading : function(){
14911         return this.trans ? true : false;
14912     },
14913
14914     /**
14915      * Abort the current server request.
14916      */
14917     abort : function(){
14918         if(this.isLoading()){
14919             this.destroyTrans(this.trans);
14920         }
14921     },
14922
14923     // private
14924     destroyTrans : function(trans, isLoaded){
14925         this.head.removeChild(document.getElementById(trans.scriptId));
14926         clearTimeout(trans.timeoutId);
14927         if(isLoaded){
14928             window[trans.cb] = undefined;
14929             try{
14930                 delete window[trans.cb];
14931             }catch(e){}
14932         }else{
14933             // if hasn't been loaded, wait for load to remove it to prevent script error
14934             window[trans.cb] = function(){
14935                 window[trans.cb] = undefined;
14936                 try{
14937                     delete window[trans.cb];
14938                 }catch(e){}
14939             };
14940         }
14941     },
14942
14943     // private
14944     handleResponse : function(o, trans){
14945         this.trans = false;
14946         this.destroyTrans(trans, true);
14947         var result;
14948         try {
14949             result = trans.reader.readRecords(o);
14950         }catch(e){
14951             this.fireEvent("loadexception", this, o, trans.arg, e);
14952             trans.callback.call(trans.scope||window, null, trans.arg, false);
14953             return;
14954         }
14955         this.fireEvent("load", this, o, trans.arg);
14956         trans.callback.call(trans.scope||window, result, trans.arg, true);
14957     },
14958
14959     // private
14960     handleFailure : function(trans){
14961         this.trans = false;
14962         this.destroyTrans(trans, false);
14963         this.fireEvent("loadexception", this, null, trans.arg);
14964         trans.callback.call(trans.scope||window, null, trans.arg, false);
14965     }
14966 });/*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976
14977 /**
14978  * @class Roo.data.JsonReader
14979  * @extends Roo.data.DataReader
14980  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14981  * based on mappings in a provided Roo.data.Record constructor.
14982  * 
14983  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14984  * in the reply previously. 
14985  * 
14986  * <p>
14987  * Example code:
14988  * <pre><code>
14989 var RecordDef = Roo.data.Record.create([
14990     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14991     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14992 ]);
14993 var myReader = new Roo.data.JsonReader({
14994     totalProperty: "results",    // The property which contains the total dataset size (optional)
14995     root: "rows",                // The property which contains an Array of row objects
14996     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14997 }, RecordDef);
14998 </code></pre>
14999  * <p>
15000  * This would consume a JSON file like this:
15001  * <pre><code>
15002 { 'results': 2, 'rows': [
15003     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15004     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15005 }
15006 </code></pre>
15007  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15008  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15009  * paged from the remote server.
15010  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15011  * @cfg {String} root name of the property which contains the Array of row objects.
15012  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15013  * @cfg {Array} fields Array of field definition objects
15014  * @constructor
15015  * Create a new JsonReader
15016  * @param {Object} meta Metadata configuration options
15017  * @param {Object} recordType Either an Array of field definition objects,
15018  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15019  */
15020 Roo.data.JsonReader = function(meta, recordType){
15021     
15022     meta = meta || {};
15023     // set some defaults:
15024     Roo.applyIf(meta, {
15025         totalProperty: 'total',
15026         successProperty : 'success',
15027         root : 'data',
15028         id : 'id'
15029     });
15030     
15031     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15032 };
15033 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15034     
15035     readerType : 'Json',
15036     
15037     /**
15038      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15039      * Used by Store query builder to append _requestMeta to params.
15040      * 
15041      */
15042     metaFromRemote : false,
15043     /**
15044      * This method is only used by a DataProxy which has retrieved data from a remote server.
15045      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15046      * @return {Object} data A data block which is used by an Roo.data.Store object as
15047      * a cache of Roo.data.Records.
15048      */
15049     read : function(response){
15050         var json = response.responseText;
15051        
15052         var o = /* eval:var:o */ eval("("+json+")");
15053         if(!o) {
15054             throw {message: "JsonReader.read: Json object not found"};
15055         }
15056         
15057         if(o.metaData){
15058             
15059             delete this.ef;
15060             this.metaFromRemote = true;
15061             this.meta = o.metaData;
15062             this.recordType = Roo.data.Record.create(o.metaData.fields);
15063             this.onMetaChange(this.meta, this.recordType, o);
15064         }
15065         return this.readRecords(o);
15066     },
15067
15068     // private function a store will implement
15069     onMetaChange : function(meta, recordType, o){
15070
15071     },
15072
15073     /**
15074          * @ignore
15075          */
15076     simpleAccess: function(obj, subsc) {
15077         return obj[subsc];
15078     },
15079
15080         /**
15081          * @ignore
15082          */
15083     getJsonAccessor: function(){
15084         var re = /[\[\.]/;
15085         return function(expr) {
15086             try {
15087                 return(re.test(expr))
15088                     ? new Function("obj", "return obj." + expr)
15089                     : function(obj){
15090                         return obj[expr];
15091                     };
15092             } catch(e){}
15093             return Roo.emptyFn;
15094         };
15095     }(),
15096
15097     /**
15098      * Create a data block containing Roo.data.Records from an XML document.
15099      * @param {Object} o An object which contains an Array of row objects in the property specified
15100      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15101      * which contains the total size of the dataset.
15102      * @return {Object} data A data block which is used by an Roo.data.Store object as
15103      * a cache of Roo.data.Records.
15104      */
15105     readRecords : function(o){
15106         /**
15107          * After any data loads, the raw JSON data is available for further custom processing.
15108          * @type Object
15109          */
15110         this.o = o;
15111         var s = this.meta, Record = this.recordType,
15112             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15113
15114 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15115         if (!this.ef) {
15116             if(s.totalProperty) {
15117                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15118                 }
15119                 if(s.successProperty) {
15120                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15121                 }
15122                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15123                 if (s.id) {
15124                         var g = this.getJsonAccessor(s.id);
15125                         this.getId = function(rec) {
15126                                 var r = g(rec);  
15127                                 return (r === undefined || r === "") ? null : r;
15128                         };
15129                 } else {
15130                         this.getId = function(){return null;};
15131                 }
15132             this.ef = [];
15133             for(var jj = 0; jj < fl; jj++){
15134                 f = fi[jj];
15135                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15136                 this.ef[jj] = this.getJsonAccessor(map);
15137             }
15138         }
15139
15140         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15141         if(s.totalProperty){
15142             var vt = parseInt(this.getTotal(o), 10);
15143             if(!isNaN(vt)){
15144                 totalRecords = vt;
15145             }
15146         }
15147         if(s.successProperty){
15148             var vs = this.getSuccess(o);
15149             if(vs === false || vs === 'false'){
15150                 success = false;
15151             }
15152         }
15153         var records = [];
15154         for(var i = 0; i < c; i++){
15155                 var n = root[i];
15156             var values = {};
15157             var id = this.getId(n);
15158             for(var j = 0; j < fl; j++){
15159                 f = fi[j];
15160             var v = this.ef[j](n);
15161             if (!f.convert) {
15162                 Roo.log('missing convert for ' + f.name);
15163                 Roo.log(f);
15164                 continue;
15165             }
15166             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15167             }
15168             var record = new Record(values, id);
15169             record.json = n;
15170             records[i] = record;
15171         }
15172         return {
15173             raw : o,
15174             success : success,
15175             records : records,
15176             totalRecords : totalRecords
15177         };
15178     },
15179     // used when loading children.. @see loadDataFromChildren
15180     toLoadData: function(rec)
15181     {
15182         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15183         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15184         return { data : data, total : data.length };
15185         
15186     }
15187 });/*
15188  * Based on:
15189  * Ext JS Library 1.1.1
15190  * Copyright(c) 2006-2007, Ext JS, LLC.
15191  *
15192  * Originally Released Under LGPL - original licence link has changed is not relivant.
15193  *
15194  * Fork - LGPL
15195  * <script type="text/javascript">
15196  */
15197
15198 /**
15199  * @class Roo.data.ArrayReader
15200  * @extends Roo.data.DataReader
15201  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15202  * Each element of that Array represents a row of data fields. The
15203  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15204  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15205  * <p>
15206  * Example code:.
15207  * <pre><code>
15208 var RecordDef = Roo.data.Record.create([
15209     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15210     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15211 ]);
15212 var myReader = new Roo.data.ArrayReader({
15213     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15214 }, RecordDef);
15215 </code></pre>
15216  * <p>
15217  * This would consume an Array like this:
15218  * <pre><code>
15219 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15220   </code></pre>
15221  
15222  * @constructor
15223  * Create a new JsonReader
15224  * @param {Object} meta Metadata configuration options.
15225  * @param {Object|Array} recordType Either an Array of field definition objects
15226  * 
15227  * @cfg {Array} fields Array of field definition objects
15228  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15229  * as specified to {@link Roo.data.Record#create},
15230  * or an {@link Roo.data.Record} object
15231  *
15232  * 
15233  * created using {@link Roo.data.Record#create}.
15234  */
15235 Roo.data.ArrayReader = function(meta, recordType)
15236 {    
15237     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15238 };
15239
15240 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15241     
15242       /**
15243      * Create a data block containing Roo.data.Records from an XML document.
15244      * @param {Object} o An Array of row objects which represents the dataset.
15245      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15246      * a cache of Roo.data.Records.
15247      */
15248     readRecords : function(o)
15249     {
15250         var sid = this.meta ? this.meta.id : null;
15251         var recordType = this.recordType, fields = recordType.prototype.fields;
15252         var records = [];
15253         var root = o;
15254         for(var i = 0; i < root.length; i++){
15255             var n = root[i];
15256             var values = {};
15257             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15258             for(var j = 0, jlen = fields.length; j < jlen; j++){
15259                 var f = fields.items[j];
15260                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15261                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15262                 v = f.convert(v);
15263                 values[f.name] = v;
15264             }
15265             var record = new recordType(values, id);
15266             record.json = n;
15267             records[records.length] = record;
15268         }
15269         return {
15270             records : records,
15271             totalRecords : records.length
15272         };
15273     },
15274     // used when loading children.. @see loadDataFromChildren
15275     toLoadData: function(rec)
15276     {
15277         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15278         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15279         
15280     }
15281     
15282     
15283 });/*
15284  * - LGPL
15285  * * 
15286  */
15287
15288 /**
15289  * @class Roo.bootstrap.ComboBox
15290  * @extends Roo.bootstrap.TriggerField
15291  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15292  * @cfg {Boolean} append (true|false) default false
15293  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15294  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15295  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15296  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15297  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15298  * @cfg {Boolean} animate default true
15299  * @cfg {Boolean} emptyResultText only for touch device
15300  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15301  * @cfg {String} emptyTitle default ''
15302  * @cfg {Number} width fixed with? experimental
15303  * @constructor
15304  * Create a new ComboBox.
15305  * @param {Object} config Configuration options
15306  */
15307 Roo.bootstrap.ComboBox = function(config){
15308     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15309     this.addEvents({
15310         /**
15311          * @event expand
15312          * Fires when the dropdown list is expanded
15313         * @param {Roo.bootstrap.ComboBox} combo This combo box
15314         */
15315         'expand' : true,
15316         /**
15317          * @event collapse
15318          * Fires when the dropdown list is collapsed
15319         * @param {Roo.bootstrap.ComboBox} combo This combo box
15320         */
15321         'collapse' : true,
15322         /**
15323          * @event beforeselect
15324          * Fires before a list item is selected. Return false to cancel the selection.
15325         * @param {Roo.bootstrap.ComboBox} combo This combo box
15326         * @param {Roo.data.Record} record The data record returned from the underlying store
15327         * @param {Number} index The index of the selected item in the dropdown list
15328         */
15329         'beforeselect' : true,
15330         /**
15331          * @event select
15332          * Fires when a list item is selected
15333         * @param {Roo.bootstrap.ComboBox} combo This combo box
15334         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15335         * @param {Number} index The index of the selected item in the dropdown list
15336         */
15337         'select' : true,
15338         /**
15339          * @event beforequery
15340          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15341          * The event object passed has these properties:
15342         * @param {Roo.bootstrap.ComboBox} combo This combo box
15343         * @param {String} query The query
15344         * @param {Boolean} forceAll true to force "all" query
15345         * @param {Boolean} cancel true to cancel the query
15346         * @param {Object} e The query event object
15347         */
15348         'beforequery': true,
15349          /**
15350          * @event add
15351          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15352         * @param {Roo.bootstrap.ComboBox} combo This combo box
15353         */
15354         'add' : true,
15355         /**
15356          * @event edit
15357          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15358         * @param {Roo.bootstrap.ComboBox} combo This combo box
15359         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15360         */
15361         'edit' : true,
15362         /**
15363          * @event remove
15364          * Fires when the remove value from the combobox array
15365         * @param {Roo.bootstrap.ComboBox} combo This combo box
15366         */
15367         'remove' : true,
15368         /**
15369          * @event afterremove
15370          * Fires when the remove value from the combobox array
15371         * @param {Roo.bootstrap.ComboBox} combo This combo box
15372         */
15373         'afterremove' : true,
15374         /**
15375          * @event specialfilter
15376          * Fires when specialfilter
15377             * @param {Roo.bootstrap.ComboBox} combo This combo box
15378             */
15379         'specialfilter' : true,
15380         /**
15381          * @event tick
15382          * Fires when tick the element
15383             * @param {Roo.bootstrap.ComboBox} combo This combo box
15384             */
15385         'tick' : true,
15386         /**
15387          * @event touchviewdisplay
15388          * Fires when touch view require special display (default is using displayField)
15389             * @param {Roo.bootstrap.ComboBox} combo This combo box
15390             * @param {Object} cfg set html .
15391             */
15392         'touchviewdisplay' : true
15393         
15394     });
15395     
15396     this.item = [];
15397     this.tickItems = [];
15398     
15399     this.selectedIndex = -1;
15400     if(this.mode == 'local'){
15401         if(config.queryDelay === undefined){
15402             this.queryDelay = 10;
15403         }
15404         if(config.minChars === undefined){
15405             this.minChars = 0;
15406         }
15407     }
15408 };
15409
15410 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15411      
15412     /**
15413      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15414      * rendering into an Roo.Editor, defaults to false)
15415      */
15416     /**
15417      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15418      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15419      */
15420     /**
15421      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15422      */
15423     /**
15424      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15425      * the dropdown list (defaults to undefined, with no header element)
15426      */
15427
15428      /**
15429      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15430      */
15431      
15432      /**
15433      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15434      */
15435     listWidth: undefined,
15436     /**
15437      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15438      * mode = 'remote' or 'text' if mode = 'local')
15439      */
15440     displayField: undefined,
15441     
15442     /**
15443      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15444      * mode = 'remote' or 'value' if mode = 'local'). 
15445      * Note: use of a valueField requires the user make a selection
15446      * in order for a value to be mapped.
15447      */
15448     valueField: undefined,
15449     /**
15450      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15451      */
15452     modalTitle : '',
15453     
15454     /**
15455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15456      * field's data value (defaults to the underlying DOM element's name)
15457      */
15458     hiddenName: undefined,
15459     /**
15460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15461      */
15462     listClass: '',
15463     /**
15464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15465      */
15466     selectedClass: 'active',
15467     
15468     /**
15469      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15470      */
15471     shadow:'sides',
15472     /**
15473      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15474      * anchor positions (defaults to 'tl-bl')
15475      */
15476     listAlign: 'tl-bl?',
15477     /**
15478      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15479      */
15480     maxHeight: 300,
15481     /**
15482      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15483      * query specified by the allQuery config option (defaults to 'query')
15484      */
15485     triggerAction: 'query',
15486     /**
15487      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15488      * (defaults to 4, does not apply if editable = false)
15489      */
15490     minChars : 4,
15491     /**
15492      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15493      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15494      */
15495     typeAhead: false,
15496     /**
15497      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15498      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15499      */
15500     queryDelay: 500,
15501     /**
15502      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15503      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15504      */
15505     pageSize: 0,
15506     /**
15507      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15508      * when editable = true (defaults to false)
15509      */
15510     selectOnFocus:false,
15511     /**
15512      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15513      */
15514     queryParam: 'query',
15515     /**
15516      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15517      * when mode = 'remote' (defaults to 'Loading...')
15518      */
15519     loadingText: 'Loading...',
15520     /**
15521      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15522      */
15523     resizable: false,
15524     /**
15525      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15526      */
15527     handleHeight : 8,
15528     /**
15529      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15530      * traditional select (defaults to true)
15531      */
15532     editable: true,
15533     /**
15534      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15535      */
15536     allQuery: '',
15537     /**
15538      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15539      */
15540     mode: 'remote',
15541     /**
15542      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15543      * listWidth has a higher value)
15544      */
15545     minListWidth : 70,
15546     /**
15547      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15548      * allow the user to set arbitrary text into the field (defaults to false)
15549      */
15550     forceSelection:false,
15551     /**
15552      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15553      * if typeAhead = true (defaults to 250)
15554      */
15555     typeAheadDelay : 250,
15556     /**
15557      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15558      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15559      */
15560     valueNotFoundText : undefined,
15561     /**
15562      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15563      */
15564     blockFocus : false,
15565     
15566     /**
15567      * @cfg {Boolean} disableClear Disable showing of clear button.
15568      */
15569     disableClear : false,
15570     /**
15571      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15572      */
15573     alwaysQuery : false,
15574     
15575     /**
15576      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15577      */
15578     multiple : false,
15579     
15580     /**
15581      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15582      */
15583     invalidClass : "has-warning",
15584     
15585     /**
15586      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15587      */
15588     validClass : "has-success",
15589     
15590     /**
15591      * @cfg {Boolean} specialFilter (true|false) special filter default false
15592      */
15593     specialFilter : false,
15594     
15595     /**
15596      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15597      */
15598     mobileTouchView : true,
15599     
15600     /**
15601      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15602      */
15603     useNativeIOS : false,
15604     
15605     /**
15606      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15607      */
15608     mobile_restrict_height : false,
15609     
15610     ios_options : false,
15611     
15612     //private
15613     addicon : false,
15614     editicon: false,
15615     
15616     page: 0,
15617     hasQuery: false,
15618     append: false,
15619     loadNext: false,
15620     autoFocus : true,
15621     tickable : false,
15622     btnPosition : 'right',
15623     triggerList : true,
15624     showToggleBtn : true,
15625     animate : true,
15626     emptyResultText: 'Empty',
15627     triggerText : 'Select',
15628     emptyTitle : '',
15629     width : false,
15630     
15631     // element that contains real text value.. (when hidden is used..)
15632     
15633     getAutoCreate : function()
15634     {   
15635         var cfg = false;
15636         //render
15637         /*
15638          * Render classic select for iso
15639          */
15640         
15641         if(Roo.isIOS && this.useNativeIOS){
15642             cfg = this.getAutoCreateNativeIOS();
15643             return cfg;
15644         }
15645         
15646         /*
15647          * Touch Devices
15648          */
15649         
15650         if(Roo.isTouch && this.mobileTouchView){
15651             cfg = this.getAutoCreateTouchView();
15652             return cfg;;
15653         }
15654         
15655         /*
15656          *  Normal ComboBox
15657          */
15658         if(!this.tickable){
15659             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15660             return cfg;
15661         }
15662         
15663         /*
15664          *  ComboBox with tickable selections
15665          */
15666              
15667         var align = this.labelAlign || this.parentLabelAlign();
15668         
15669         cfg = {
15670             cls : 'form-group roo-combobox-tickable' //input-group
15671         };
15672         
15673         var btn_text_select = '';
15674         var btn_text_done = '';
15675         var btn_text_cancel = '';
15676         
15677         if (this.btn_text_show) {
15678             btn_text_select = 'Select';
15679             btn_text_done = 'Done';
15680             btn_text_cancel = 'Cancel'; 
15681         }
15682         
15683         var buttons = {
15684             tag : 'div',
15685             cls : 'tickable-buttons',
15686             cn : [
15687                 {
15688                     tag : 'button',
15689                     type : 'button',
15690                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15691                     //html : this.triggerText
15692                     html: btn_text_select
15693                 },
15694                 {
15695                     tag : 'button',
15696                     type : 'button',
15697                     name : 'ok',
15698                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15699                     //html : 'Done'
15700                     html: btn_text_done
15701                 },
15702                 {
15703                     tag : 'button',
15704                     type : 'button',
15705                     name : 'cancel',
15706                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15707                     //html : 'Cancel'
15708                     html: btn_text_cancel
15709                 }
15710             ]
15711         };
15712         
15713         if(this.editable){
15714             buttons.cn.unshift({
15715                 tag: 'input',
15716                 cls: 'roo-select2-search-field-input'
15717             });
15718         }
15719         
15720         var _this = this;
15721         
15722         Roo.each(buttons.cn, function(c){
15723             if (_this.size) {
15724                 c.cls += ' btn-' + _this.size;
15725             }
15726
15727             if (_this.disabled) {
15728                 c.disabled = true;
15729             }
15730         });
15731         
15732         var box = {
15733             tag: 'div',
15734             style : 'display: contents',
15735             cn: [
15736                 {
15737                     tag: 'input',
15738                     type : 'hidden',
15739                     cls: 'form-hidden-field'
15740                 },
15741                 {
15742                     tag: 'ul',
15743                     cls: 'roo-select2-choices',
15744                     cn:[
15745                         {
15746                             tag: 'li',
15747                             cls: 'roo-select2-search-field',
15748                             cn: [
15749                                 buttons
15750                             ]
15751                         }
15752                     ]
15753                 }
15754             ]
15755         };
15756         
15757         var combobox = {
15758             cls: 'roo-select2-container input-group roo-select2-container-multi',
15759             cn: [
15760                 
15761                 box
15762 //                {
15763 //                    tag: 'ul',
15764 //                    cls: 'typeahead typeahead-long dropdown-menu',
15765 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15766 //                }
15767             ]
15768         };
15769         
15770         if(this.hasFeedback && !this.allowBlank){
15771             
15772             var feedback = {
15773                 tag: 'span',
15774                 cls: 'glyphicon form-control-feedback'
15775             };
15776
15777             combobox.cn.push(feedback);
15778         }
15779         
15780         
15781         
15782         var indicator = {
15783             tag : 'i',
15784             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15785             tooltip : 'This field is required'
15786         };
15787         if (Roo.bootstrap.version == 4) {
15788             indicator = {
15789                 tag : 'i',
15790                 style : 'display:none'
15791             };
15792         }
15793         if (align ==='left' && this.fieldLabel.length) {
15794             
15795             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15796             
15797             cfg.cn = [
15798                 indicator,
15799                 {
15800                     tag: 'label',
15801                     'for' :  id,
15802                     cls : 'control-label col-form-label',
15803                     html : this.fieldLabel
15804
15805                 },
15806                 {
15807                     cls : "", 
15808                     cn: [
15809                         combobox
15810                     ]
15811                 }
15812
15813             ];
15814             
15815             var labelCfg = cfg.cn[1];
15816             var contentCfg = cfg.cn[2];
15817             
15818
15819             if(this.indicatorpos == 'right'){
15820                 
15821                 cfg.cn = [
15822                     {
15823                         tag: 'label',
15824                         'for' :  id,
15825                         cls : 'control-label col-form-label',
15826                         cn : [
15827                             {
15828                                 tag : 'span',
15829                                 html : this.fieldLabel
15830                             },
15831                             indicator
15832                         ]
15833                     },
15834                     {
15835                         cls : "",
15836                         cn: [
15837                             combobox
15838                         ]
15839                     }
15840
15841                 ];
15842                 
15843                 
15844                 
15845                 labelCfg = cfg.cn[0];
15846                 contentCfg = cfg.cn[1];
15847             
15848             }
15849             
15850             if(this.labelWidth > 12){
15851                 labelCfg.style = "width: " + this.labelWidth + 'px';
15852             }
15853             if(this.width * 1 > 0){
15854                 contentCfg.style = "width: " + this.width + 'px';
15855             }
15856             if(this.labelWidth < 13 && this.labelmd == 0){
15857                 this.labelmd = this.labelWidth;
15858             }
15859             
15860             if(this.labellg > 0){
15861                 labelCfg.cls += ' col-lg-' + this.labellg;
15862                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15863             }
15864             
15865             if(this.labelmd > 0){
15866                 labelCfg.cls += ' col-md-' + this.labelmd;
15867                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15868             }
15869             
15870             if(this.labelsm > 0){
15871                 labelCfg.cls += ' col-sm-' + this.labelsm;
15872                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15873             }
15874             
15875             if(this.labelxs > 0){
15876                 labelCfg.cls += ' col-xs-' + this.labelxs;
15877                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15878             }
15879                 
15880                 
15881         } else if ( this.fieldLabel.length) {
15882 //                Roo.log(" label");
15883                  cfg.cn = [
15884                    indicator,
15885                     {
15886                         tag: 'label',
15887                         //cls : 'input-group-addon',
15888                         html : this.fieldLabel
15889                     },
15890                     combobox
15891                 ];
15892                 
15893                 if(this.indicatorpos == 'right'){
15894                     cfg.cn = [
15895                         {
15896                             tag: 'label',
15897                             //cls : 'input-group-addon',
15898                             html : this.fieldLabel
15899                         },
15900                         indicator,
15901                         combobox
15902                     ];
15903                     
15904                 }
15905
15906         } else {
15907             
15908 //                Roo.log(" no label && no align");
15909                 cfg = combobox
15910                      
15911                 
15912         }
15913          
15914         var settings=this;
15915         ['xs','sm','md','lg'].map(function(size){
15916             if (settings[size]) {
15917                 cfg.cls += ' col-' + size + '-' + settings[size];
15918             }
15919         });
15920         
15921         return cfg;
15922         
15923     },
15924     
15925     _initEventsCalled : false,
15926     
15927     // private
15928     initEvents: function()
15929     {   
15930         if (this._initEventsCalled) { // as we call render... prevent looping...
15931             return;
15932         }
15933         this._initEventsCalled = true;
15934         
15935         if (!this.store) {
15936             throw "can not find store for combo";
15937         }
15938         
15939         this.indicator = this.indicatorEl();
15940         
15941         this.store = Roo.factory(this.store, Roo.data);
15942         this.store.parent = this;
15943         
15944         // if we are building from html. then this element is so complex, that we can not really
15945         // use the rendered HTML.
15946         // so we have to trash and replace the previous code.
15947         if (Roo.XComponent.build_from_html) {
15948             // remove this element....
15949             var e = this.el.dom, k=0;
15950             while (e ) { e = e.previousSibling;  ++k;}
15951
15952             this.el.remove();
15953             
15954             this.el=false;
15955             this.rendered = false;
15956             
15957             this.render(this.parent().getChildContainer(true), k);
15958         }
15959         
15960         if(Roo.isIOS && this.useNativeIOS){
15961             this.initIOSView();
15962             return;
15963         }
15964         
15965         /*
15966          * Touch Devices
15967          */
15968         
15969         if(Roo.isTouch && this.mobileTouchView){
15970             this.initTouchView();
15971             return;
15972         }
15973         
15974         if(this.tickable){
15975             this.initTickableEvents();
15976             return;
15977         }
15978         
15979         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15980         
15981         if(this.hiddenName){
15982             
15983             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15984             
15985             this.hiddenField.dom.value =
15986                 this.hiddenValue !== undefined ? this.hiddenValue :
15987                 this.value !== undefined ? this.value : '';
15988
15989             // prevent input submission
15990             this.el.dom.removeAttribute('name');
15991             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15992              
15993              
15994         }
15995         //if(Roo.isGecko){
15996         //    this.el.dom.setAttribute('autocomplete', 'off');
15997         //}
15998         
15999         var cls = 'x-combo-list';
16000         
16001         //this.list = new Roo.Layer({
16002         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16003         //});
16004         
16005         var _this = this;
16006         
16007         (function(){
16008             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16009             _this.list.setWidth(lw);
16010         }).defer(100);
16011         
16012         this.list.on('mouseover', this.onViewOver, this);
16013         this.list.on('mousemove', this.onViewMove, this);
16014         this.list.on('scroll', this.onViewScroll, this);
16015         
16016         /*
16017         this.list.swallowEvent('mousewheel');
16018         this.assetHeight = 0;
16019
16020         if(this.title){
16021             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16022             this.assetHeight += this.header.getHeight();
16023         }
16024
16025         this.innerList = this.list.createChild({cls:cls+'-inner'});
16026         this.innerList.on('mouseover', this.onViewOver, this);
16027         this.innerList.on('mousemove', this.onViewMove, this);
16028         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16029         
16030         if(this.allowBlank && !this.pageSize && !this.disableClear){
16031             this.footer = this.list.createChild({cls:cls+'-ft'});
16032             this.pageTb = new Roo.Toolbar(this.footer);
16033            
16034         }
16035         if(this.pageSize){
16036             this.footer = this.list.createChild({cls:cls+'-ft'});
16037             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16038                     {pageSize: this.pageSize});
16039             
16040         }
16041         
16042         if (this.pageTb && this.allowBlank && !this.disableClear) {
16043             var _this = this;
16044             this.pageTb.add(new Roo.Toolbar.Fill(), {
16045                 cls: 'x-btn-icon x-btn-clear',
16046                 text: '&#160;',
16047                 handler: function()
16048                 {
16049                     _this.collapse();
16050                     _this.clearValue();
16051                     _this.onSelect(false, -1);
16052                 }
16053             });
16054         }
16055         if (this.footer) {
16056             this.assetHeight += this.footer.getHeight();
16057         }
16058         */
16059             
16060         if(!this.tpl){
16061             this.tpl = Roo.bootstrap.version == 4 ?
16062                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16063                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16064         }
16065
16066         this.view = new Roo.View(this.list, this.tpl, {
16067             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16068         });
16069         //this.view.wrapEl.setDisplayed(false);
16070         this.view.on('click', this.onViewClick, this);
16071         
16072         
16073         this.store.on('beforeload', this.onBeforeLoad, this);
16074         this.store.on('load', this.onLoad, this);
16075         this.store.on('loadexception', this.onLoadException, this);
16076         /*
16077         if(this.resizable){
16078             this.resizer = new Roo.Resizable(this.list,  {
16079                pinned:true, handles:'se'
16080             });
16081             this.resizer.on('resize', function(r, w, h){
16082                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16083                 this.listWidth = w;
16084                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16085                 this.restrictHeight();
16086             }, this);
16087             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16088         }
16089         */
16090         if(!this.editable){
16091             this.editable = true;
16092             this.setEditable(false);
16093         }
16094         
16095         /*
16096         
16097         if (typeof(this.events.add.listeners) != 'undefined') {
16098             
16099             this.addicon = this.wrap.createChild(
16100                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16101        
16102             this.addicon.on('click', function(e) {
16103                 this.fireEvent('add', this);
16104             }, this);
16105         }
16106         if (typeof(this.events.edit.listeners) != 'undefined') {
16107             
16108             this.editicon = this.wrap.createChild(
16109                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16110             if (this.addicon) {
16111                 this.editicon.setStyle('margin-left', '40px');
16112             }
16113             this.editicon.on('click', function(e) {
16114                 
16115                 // we fire even  if inothing is selected..
16116                 this.fireEvent('edit', this, this.lastData );
16117                 
16118             }, this);
16119         }
16120         */
16121         
16122         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16123             "up" : function(e){
16124                 this.inKeyMode = true;
16125                 this.selectPrev();
16126             },
16127
16128             "down" : function(e){
16129                 if(!this.isExpanded()){
16130                     this.onTriggerClick();
16131                 }else{
16132                     this.inKeyMode = true;
16133                     this.selectNext();
16134                 }
16135             },
16136
16137             "enter" : function(e){
16138 //                this.onViewClick();
16139                 //return true;
16140                 this.collapse();
16141                 
16142                 if(this.fireEvent("specialkey", this, e)){
16143                     this.onViewClick(false);
16144                 }
16145                 
16146                 return true;
16147             },
16148
16149             "esc" : function(e){
16150                 this.collapse();
16151             },
16152
16153             "tab" : function(e){
16154                 this.collapse();
16155                 
16156                 if(this.fireEvent("specialkey", this, e)){
16157                     this.onViewClick(false);
16158                 }
16159                 
16160                 return true;
16161             },
16162
16163             scope : this,
16164
16165             doRelay : function(foo, bar, hname){
16166                 if(hname == 'down' || this.scope.isExpanded()){
16167                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16168                 }
16169                 return true;
16170             },
16171
16172             forceKeyDown: true
16173         });
16174         
16175         
16176         this.queryDelay = Math.max(this.queryDelay || 10,
16177                 this.mode == 'local' ? 10 : 250);
16178         
16179         
16180         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16181         
16182         if(this.typeAhead){
16183             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16184         }
16185         if(this.editable !== false){
16186             this.inputEl().on("keyup", this.onKeyUp, this);
16187         }
16188         if(this.forceSelection){
16189             this.inputEl().on('blur', this.doForce, this);
16190         }
16191         
16192         if(this.multiple){
16193             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16194             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16195         }
16196     },
16197     
16198     initTickableEvents: function()
16199     {   
16200         this.createList();
16201         
16202         if(this.hiddenName){
16203             
16204             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16205             
16206             this.hiddenField.dom.value =
16207                 this.hiddenValue !== undefined ? this.hiddenValue :
16208                 this.value !== undefined ? this.value : '';
16209
16210             // prevent input submission
16211             this.el.dom.removeAttribute('name');
16212             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16213              
16214              
16215         }
16216         
16217 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16218         
16219         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16220         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16221         if(this.triggerList){
16222             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16223         }
16224          
16225         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16226         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16227         
16228         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16229         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16230         
16231         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16232         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16233         
16234         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16235         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16236         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16237         
16238         this.okBtn.hide();
16239         this.cancelBtn.hide();
16240         
16241         var _this = this;
16242         
16243         (function(){
16244             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16245             _this.list.setWidth(lw);
16246         }).defer(100);
16247         
16248         this.list.on('mouseover', this.onViewOver, this);
16249         this.list.on('mousemove', this.onViewMove, this);
16250         
16251         this.list.on('scroll', this.onViewScroll, this);
16252         
16253         if(!this.tpl){
16254             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16255                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16256         }
16257
16258         this.view = new Roo.View(this.list, this.tpl, {
16259             singleSelect:true,
16260             tickable:true,
16261             parent:this,
16262             store: this.store,
16263             selectedClass: this.selectedClass
16264         });
16265         
16266         //this.view.wrapEl.setDisplayed(false);
16267         this.view.on('click', this.onViewClick, this);
16268         
16269         
16270         
16271         this.store.on('beforeload', this.onBeforeLoad, this);
16272         this.store.on('load', this.onLoad, this);
16273         this.store.on('loadexception', this.onLoadException, this);
16274         
16275         if(this.editable){
16276             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16277                 "up" : function(e){
16278                     this.inKeyMode = true;
16279                     this.selectPrev();
16280                 },
16281
16282                 "down" : function(e){
16283                     this.inKeyMode = true;
16284                     this.selectNext();
16285                 },
16286
16287                 "enter" : function(e){
16288                     if(this.fireEvent("specialkey", this, e)){
16289                         this.onViewClick(false);
16290                     }
16291                     
16292                     return true;
16293                 },
16294
16295                 "esc" : function(e){
16296                     this.onTickableFooterButtonClick(e, false, false);
16297                 },
16298
16299                 "tab" : function(e){
16300                     this.fireEvent("specialkey", this, e);
16301                     
16302                     this.onTickableFooterButtonClick(e, false, false);
16303                     
16304                     return true;
16305                 },
16306
16307                 scope : this,
16308
16309                 doRelay : function(e, fn, key){
16310                     if(this.scope.isExpanded()){
16311                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16312                     }
16313                     return true;
16314                 },
16315
16316                 forceKeyDown: true
16317             });
16318         }
16319         
16320         this.queryDelay = Math.max(this.queryDelay || 10,
16321                 this.mode == 'local' ? 10 : 250);
16322         
16323         
16324         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16325         
16326         if(this.typeAhead){
16327             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16328         }
16329         
16330         if(this.editable !== false){
16331             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16332         }
16333         
16334         this.indicator = this.indicatorEl();
16335         
16336         if(this.indicator){
16337             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16338             this.indicator.hide();
16339         }
16340         
16341     },
16342
16343     onDestroy : function(){
16344         if(this.view){
16345             this.view.setStore(null);
16346             this.view.el.removeAllListeners();
16347             this.view.el.remove();
16348             this.view.purgeListeners();
16349         }
16350         if(this.list){
16351             this.list.dom.innerHTML  = '';
16352         }
16353         
16354         if(this.store){
16355             this.store.un('beforeload', this.onBeforeLoad, this);
16356             this.store.un('load', this.onLoad, this);
16357             this.store.un('loadexception', this.onLoadException, this);
16358         }
16359         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16360     },
16361
16362     // private
16363     fireKey : function(e){
16364         if(e.isNavKeyPress() && !this.list.isVisible()){
16365             this.fireEvent("specialkey", this, e);
16366         }
16367     },
16368
16369     // private
16370     onResize: function(w, h)
16371     {
16372         
16373         
16374 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16375 //        
16376 //        if(typeof w != 'number'){
16377 //            // we do not handle it!?!?
16378 //            return;
16379 //        }
16380 //        var tw = this.trigger.getWidth();
16381 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16382 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16383 //        var x = w - tw;
16384 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16385 //            
16386 //        //this.trigger.setStyle('left', x+'px');
16387 //        
16388 //        if(this.list && this.listWidth === undefined){
16389 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16390 //            this.list.setWidth(lw);
16391 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16392 //        }
16393         
16394     
16395         
16396     },
16397
16398     /**
16399      * Allow or prevent the user from directly editing the field text.  If false is passed,
16400      * the user will only be able to select from the items defined in the dropdown list.  This method
16401      * is the runtime equivalent of setting the 'editable' config option at config time.
16402      * @param {Boolean} value True to allow the user to directly edit the field text
16403      */
16404     setEditable : function(value){
16405         if(value == this.editable){
16406             return;
16407         }
16408         this.editable = value;
16409         if(!value){
16410             this.inputEl().dom.setAttribute('readOnly', true);
16411             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16412             this.inputEl().addClass('x-combo-noedit');
16413         }else{
16414             this.inputEl().dom.removeAttribute('readOnly');
16415             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16416             this.inputEl().removeClass('x-combo-noedit');
16417         }
16418     },
16419
16420     // private
16421     
16422     onBeforeLoad : function(combo,opts){
16423         if(!this.hasFocus){
16424             return;
16425         }
16426          if (!opts.add) {
16427             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16428          }
16429         this.restrictHeight();
16430         this.selectedIndex = -1;
16431     },
16432
16433     // private
16434     onLoad : function(){
16435         
16436         this.hasQuery = false;
16437         
16438         if(!this.hasFocus){
16439             return;
16440         }
16441         
16442         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16443             this.loading.hide();
16444         }
16445         
16446         if(this.store.getCount() > 0){
16447             
16448             this.expand();
16449             this.restrictHeight();
16450             if(this.lastQuery == this.allQuery){
16451                 if(this.editable && !this.tickable){
16452                     this.inputEl().dom.select();
16453                 }
16454                 
16455                 if(
16456                     !this.selectByValue(this.value, true) &&
16457                     this.autoFocus && 
16458                     (
16459                         !this.store.lastOptions ||
16460                         typeof(this.store.lastOptions.add) == 'undefined' || 
16461                         this.store.lastOptions.add != true
16462                     )
16463                 ){
16464                     this.select(0, true);
16465                 }
16466             }else{
16467                 if(this.autoFocus){
16468                     this.selectNext();
16469                 }
16470                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16471                     this.taTask.delay(this.typeAheadDelay);
16472                 }
16473             }
16474         }else{
16475             this.onEmptyResults();
16476         }
16477         
16478         //this.el.focus();
16479     },
16480     // private
16481     onLoadException : function()
16482     {
16483         this.hasQuery = false;
16484         
16485         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16486             this.loading.hide();
16487         }
16488         
16489         if(this.tickable && this.editable){
16490             return;
16491         }
16492         
16493         this.collapse();
16494         // only causes errors at present
16495         //Roo.log(this.store.reader.jsonData);
16496         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16497             // fixme
16498             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16499         //}
16500         
16501         
16502     },
16503     // private
16504     onTypeAhead : function(){
16505         if(this.store.getCount() > 0){
16506             var r = this.store.getAt(0);
16507             var newValue = r.data[this.displayField];
16508             var len = newValue.length;
16509             var selStart = this.getRawValue().length;
16510             
16511             if(selStart != len){
16512                 this.setRawValue(newValue);
16513                 this.selectText(selStart, newValue.length);
16514             }
16515         }
16516     },
16517
16518     // private
16519     onSelect : function(record, index){
16520         
16521         if(this.fireEvent('beforeselect', this, record, index) !== false){
16522         
16523             this.setFromData(index > -1 ? record.data : false);
16524             
16525             this.collapse();
16526             this.fireEvent('select', this, record, index);
16527         }
16528     },
16529
16530     /**
16531      * Returns the currently selected field value or empty string if no value is set.
16532      * @return {String} value The selected value
16533      */
16534     getValue : function()
16535     {
16536         if(Roo.isIOS && this.useNativeIOS){
16537             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16538         }
16539         
16540         if(this.multiple){
16541             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16542         }
16543         
16544         if(this.valueField){
16545             return typeof this.value != 'undefined' ? this.value : '';
16546         }else{
16547             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16548         }
16549     },
16550     
16551     getRawValue : function()
16552     {
16553         if(Roo.isIOS && this.useNativeIOS){
16554             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16555         }
16556         
16557         var v = this.inputEl().getValue();
16558         
16559         return v;
16560     },
16561
16562     /**
16563      * Clears any text/value currently set in the field
16564      */
16565     clearValue : function(){
16566         
16567         if(this.hiddenField){
16568             this.hiddenField.dom.value = '';
16569         }
16570         this.value = '';
16571         this.setRawValue('');
16572         this.lastSelectionText = '';
16573         this.lastData = false;
16574         
16575         var close = this.closeTriggerEl();
16576         
16577         if(close){
16578             close.hide();
16579         }
16580         
16581         this.validate();
16582         
16583     },
16584
16585     /**
16586      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16587      * will be displayed in the field.  If the value does not match the data value of an existing item,
16588      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16589      * Otherwise the field will be blank (although the value will still be set).
16590      * @param {String} value The value to match
16591      */
16592     setValue : function(v)
16593     {
16594         if(Roo.isIOS && this.useNativeIOS){
16595             this.setIOSValue(v);
16596             return;
16597         }
16598         
16599         if(this.multiple){
16600             this.syncValue();
16601             return;
16602         }
16603         
16604         var text = v;
16605         if(this.valueField){
16606             var r = this.findRecord(this.valueField, v);
16607             if(r){
16608                 text = r.data[this.displayField];
16609             }else if(this.valueNotFoundText !== undefined){
16610                 text = this.valueNotFoundText;
16611             }
16612         }
16613         this.lastSelectionText = text;
16614         if(this.hiddenField){
16615             this.hiddenField.dom.value = v;
16616         }
16617         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16618         this.value = v;
16619         
16620         var close = this.closeTriggerEl();
16621         
16622         if(close){
16623             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16624         }
16625         
16626         this.validate();
16627     },
16628     /**
16629      * @property {Object} the last set data for the element
16630      */
16631     
16632     lastData : false,
16633     /**
16634      * Sets the value of the field based on a object which is related to the record format for the store.
16635      * @param {Object} value the value to set as. or false on reset?
16636      */
16637     setFromData : function(o){
16638         
16639         if(this.multiple){
16640             this.addItem(o);
16641             return;
16642         }
16643             
16644         var dv = ''; // display value
16645         var vv = ''; // value value..
16646         this.lastData = o;
16647         if (this.displayField) {
16648             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16649         } else {
16650             // this is an error condition!!!
16651             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16652         }
16653         
16654         if(this.valueField){
16655             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16656         }
16657         
16658         var close = this.closeTriggerEl();
16659         
16660         if(close){
16661             if(dv.length || vv * 1 > 0){
16662                 close.show() ;
16663                 this.blockFocus=true;
16664             } else {
16665                 close.hide();
16666             }             
16667         }
16668         
16669         if(this.hiddenField){
16670             this.hiddenField.dom.value = vv;
16671             
16672             this.lastSelectionText = dv;
16673             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16674             this.value = vv;
16675             return;
16676         }
16677         // no hidden field.. - we store the value in 'value', but still display
16678         // display field!!!!
16679         this.lastSelectionText = dv;
16680         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16681         this.value = vv;
16682         
16683         
16684         
16685     },
16686     // private
16687     reset : function(){
16688         // overridden so that last data is reset..
16689         
16690         if(this.multiple){
16691             this.clearItem();
16692             return;
16693         }
16694         
16695         this.setValue(this.originalValue);
16696         //this.clearInvalid();
16697         this.lastData = false;
16698         if (this.view) {
16699             this.view.clearSelections();
16700         }
16701         
16702         this.validate();
16703     },
16704     // private
16705     findRecord : function(prop, value){
16706         var record;
16707         if(this.store.getCount() > 0){
16708             this.store.each(function(r){
16709                 if(r.data[prop] == value){
16710                     record = r;
16711                     return false;
16712                 }
16713                 return true;
16714             });
16715         }
16716         return record;
16717     },
16718     
16719     getName: function()
16720     {
16721         // returns hidden if it's set..
16722         if (!this.rendered) {return ''};
16723         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16724         
16725     },
16726     // private
16727     onViewMove : function(e, t){
16728         this.inKeyMode = false;
16729     },
16730
16731     // private
16732     onViewOver : function(e, t){
16733         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16734             return;
16735         }
16736         var item = this.view.findItemFromChild(t);
16737         
16738         if(item){
16739             var index = this.view.indexOf(item);
16740             this.select(index, false);
16741         }
16742     },
16743
16744     // private
16745     onViewClick : function(view, doFocus, el, e)
16746     {
16747         var index = this.view.getSelectedIndexes()[0];
16748         
16749         var r = this.store.getAt(index);
16750         
16751         if(this.tickable){
16752             
16753             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16754                 return;
16755             }
16756             
16757             var rm = false;
16758             var _this = this;
16759             
16760             Roo.each(this.tickItems, function(v,k){
16761                 
16762                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16763                     Roo.log(v);
16764                     _this.tickItems.splice(k, 1);
16765                     
16766                     if(typeof(e) == 'undefined' && view == false){
16767                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16768                     }
16769                     
16770                     rm = true;
16771                     return;
16772                 }
16773             });
16774             
16775             if(rm){
16776                 return;
16777             }
16778             
16779             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16780                 this.tickItems.push(r.data);
16781             }
16782             
16783             if(typeof(e) == 'undefined' && view == false){
16784                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16785             }
16786                     
16787             return;
16788         }
16789         
16790         if(r){
16791             this.onSelect(r, index);
16792         }
16793         if(doFocus !== false && !this.blockFocus){
16794             this.inputEl().focus();
16795         }
16796     },
16797
16798     // private
16799     restrictHeight : function(){
16800         //this.innerList.dom.style.height = '';
16801         //var inner = this.innerList.dom;
16802         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16803         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16804         //this.list.beginUpdate();
16805         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16806         this.list.alignTo(this.inputEl(), this.listAlign);
16807         this.list.alignTo(this.inputEl(), this.listAlign);
16808         //this.list.endUpdate();
16809     },
16810
16811     // private
16812     onEmptyResults : function(){
16813         
16814         if(this.tickable && this.editable){
16815             this.hasFocus = false;
16816             this.restrictHeight();
16817             return;
16818         }
16819         
16820         this.collapse();
16821     },
16822
16823     /**
16824      * Returns true if the dropdown list is expanded, else false.
16825      */
16826     isExpanded : function(){
16827         return this.list.isVisible();
16828     },
16829
16830     /**
16831      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16832      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16833      * @param {String} value The data value of the item to select
16834      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16835      * selected item if it is not currently in view (defaults to true)
16836      * @return {Boolean} True if the value matched an item in the list, else false
16837      */
16838     selectByValue : function(v, scrollIntoView){
16839         if(v !== undefined && v !== null){
16840             var r = this.findRecord(this.valueField || this.displayField, v);
16841             if(r){
16842                 this.select(this.store.indexOf(r), scrollIntoView);
16843                 return true;
16844             }
16845         }
16846         return false;
16847     },
16848
16849     /**
16850      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16851      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16852      * @param {Number} index The zero-based index of the list item to select
16853      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16854      * selected item if it is not currently in view (defaults to true)
16855      */
16856     select : function(index, scrollIntoView){
16857         this.selectedIndex = index;
16858         this.view.select(index);
16859         if(scrollIntoView !== false){
16860             var el = this.view.getNode(index);
16861             /*
16862              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16863              */
16864             if(el){
16865                 this.list.scrollChildIntoView(el, false);
16866             }
16867         }
16868     },
16869
16870     // private
16871     selectNext : function(){
16872         var ct = this.store.getCount();
16873         if(ct > 0){
16874             if(this.selectedIndex == -1){
16875                 this.select(0);
16876             }else if(this.selectedIndex < ct-1){
16877                 this.select(this.selectedIndex+1);
16878             }
16879         }
16880     },
16881
16882     // private
16883     selectPrev : function(){
16884         var ct = this.store.getCount();
16885         if(ct > 0){
16886             if(this.selectedIndex == -1){
16887                 this.select(0);
16888             }else if(this.selectedIndex != 0){
16889                 this.select(this.selectedIndex-1);
16890             }
16891         }
16892     },
16893
16894     // private
16895     onKeyUp : function(e){
16896         if(this.editable !== false && !e.isSpecialKey()){
16897             this.lastKey = e.getKey();
16898             this.dqTask.delay(this.queryDelay);
16899         }
16900     },
16901
16902     // private
16903     validateBlur : function(){
16904         return !this.list || !this.list.isVisible();   
16905     },
16906
16907     // private
16908     initQuery : function(){
16909         
16910         var v = this.getRawValue();
16911         
16912         if(this.tickable && this.editable){
16913             v = this.tickableInputEl().getValue();
16914         }
16915         
16916         this.doQuery(v);
16917     },
16918
16919     // private
16920     doForce : function(){
16921         if(this.inputEl().dom.value.length > 0){
16922             this.inputEl().dom.value =
16923                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16924              
16925         }
16926     },
16927
16928     /**
16929      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16930      * query allowing the query action to be canceled if needed.
16931      * @param {String} query The SQL query to execute
16932      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16933      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16934      * saved in the current store (defaults to false)
16935      */
16936     doQuery : function(q, forceAll){
16937         
16938         if(q === undefined || q === null){
16939             q = '';
16940         }
16941         var qe = {
16942             query: q,
16943             forceAll: forceAll,
16944             combo: this,
16945             cancel:false
16946         };
16947         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16948             return false;
16949         }
16950         q = qe.query;
16951         
16952         forceAll = qe.forceAll;
16953         if(forceAll === true || (q.length >= this.minChars)){
16954             
16955             this.hasQuery = true;
16956             
16957             if(this.lastQuery != q || this.alwaysQuery){
16958                 this.lastQuery = q;
16959                 if(this.mode == 'local'){
16960                     this.selectedIndex = -1;
16961                     if(forceAll){
16962                         this.store.clearFilter();
16963                     }else{
16964                         
16965                         if(this.specialFilter){
16966                             this.fireEvent('specialfilter', this);
16967                             this.onLoad();
16968                             return;
16969                         }
16970                         
16971                         this.store.filter(this.displayField, q);
16972                     }
16973                     
16974                     this.store.fireEvent("datachanged", this.store);
16975                     
16976                     this.onLoad();
16977                     
16978                     
16979                 }else{
16980                     
16981                     this.store.baseParams[this.queryParam] = q;
16982                     
16983                     var options = {params : this.getParams(q)};
16984                     
16985                     if(this.loadNext){
16986                         options.add = true;
16987                         options.params.start = this.page * this.pageSize;
16988                     }
16989                     
16990                     this.store.load(options);
16991                     
16992                     /*
16993                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16994                      *  we should expand the list on onLoad
16995                      *  so command out it
16996                      */
16997 //                    this.expand();
16998                 }
16999             }else{
17000                 this.selectedIndex = -1;
17001                 this.onLoad();   
17002             }
17003         }
17004         
17005         this.loadNext = false;
17006     },
17007     
17008     // private
17009     getParams : function(q){
17010         var p = {};
17011         //p[this.queryParam] = q;
17012         
17013         if(this.pageSize){
17014             p.start = 0;
17015             p.limit = this.pageSize;
17016         }
17017         return p;
17018     },
17019
17020     /**
17021      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17022      */
17023     collapse : function(){
17024         if(!this.isExpanded()){
17025             return;
17026         }
17027         
17028         this.list.hide();
17029         
17030         this.hasFocus = false;
17031         
17032         if(this.tickable){
17033             this.okBtn.hide();
17034             this.cancelBtn.hide();
17035             this.trigger.show();
17036             
17037             if(this.editable){
17038                 this.tickableInputEl().dom.value = '';
17039                 this.tickableInputEl().blur();
17040             }
17041             
17042         }
17043         
17044         Roo.get(document).un('mousedown', this.collapseIf, this);
17045         Roo.get(document).un('mousewheel', this.collapseIf, this);
17046         if (!this.editable) {
17047             Roo.get(document).un('keydown', this.listKeyPress, this);
17048         }
17049         this.fireEvent('collapse', this);
17050         
17051         this.validate();
17052     },
17053
17054     // private
17055     collapseIf : function(e){
17056         var in_combo  = e.within(this.el);
17057         var in_list =  e.within(this.list);
17058         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17059         
17060         if (in_combo || in_list || is_list) {
17061             //e.stopPropagation();
17062             return;
17063         }
17064         
17065         if(this.tickable){
17066             this.onTickableFooterButtonClick(e, false, false);
17067         }
17068
17069         this.collapse();
17070         
17071     },
17072
17073     /**
17074      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17075      */
17076     expand : function(){
17077        
17078         if(this.isExpanded() || !this.hasFocus){
17079             return;
17080         }
17081         
17082         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17083         this.list.setWidth(lw);
17084         
17085         Roo.log('expand');
17086         
17087         this.list.show();
17088         
17089         this.restrictHeight();
17090         
17091         if(this.tickable){
17092             
17093             this.tickItems = Roo.apply([], this.item);
17094             
17095             this.okBtn.show();
17096             this.cancelBtn.show();
17097             this.trigger.hide();
17098             
17099             if(this.editable){
17100                 this.tickableInputEl().focus();
17101             }
17102             
17103         }
17104         
17105         Roo.get(document).on('mousedown', this.collapseIf, this);
17106         Roo.get(document).on('mousewheel', this.collapseIf, this);
17107         if (!this.editable) {
17108             Roo.get(document).on('keydown', this.listKeyPress, this);
17109         }
17110         
17111         this.fireEvent('expand', this);
17112     },
17113
17114     // private
17115     // Implements the default empty TriggerField.onTriggerClick function
17116     onTriggerClick : function(e)
17117     {
17118         Roo.log('trigger click');
17119         
17120         if(this.disabled || !this.triggerList){
17121             return;
17122         }
17123         
17124         this.page = 0;
17125         this.loadNext = false;
17126         
17127         if(this.isExpanded()){
17128             this.collapse();
17129             if (!this.blockFocus) {
17130                 this.inputEl().focus();
17131             }
17132             
17133         }else {
17134             this.hasFocus = true;
17135             if(this.triggerAction == 'all') {
17136                 this.doQuery(this.allQuery, true);
17137             } else {
17138                 this.doQuery(this.getRawValue());
17139             }
17140             if (!this.blockFocus) {
17141                 this.inputEl().focus();
17142             }
17143         }
17144     },
17145     
17146     onTickableTriggerClick : function(e)
17147     {
17148         if(this.disabled){
17149             return;
17150         }
17151         
17152         this.page = 0;
17153         this.loadNext = false;
17154         this.hasFocus = true;
17155         
17156         if(this.triggerAction == 'all') {
17157             this.doQuery(this.allQuery, true);
17158         } else {
17159             this.doQuery(this.getRawValue());
17160         }
17161     },
17162     
17163     onSearchFieldClick : function(e)
17164     {
17165         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17166             this.onTickableFooterButtonClick(e, false, false);
17167             return;
17168         }
17169         
17170         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17171             return;
17172         }
17173         
17174         this.page = 0;
17175         this.loadNext = false;
17176         this.hasFocus = true;
17177         
17178         if(this.triggerAction == 'all') {
17179             this.doQuery(this.allQuery, true);
17180         } else {
17181             this.doQuery(this.getRawValue());
17182         }
17183     },
17184     
17185     listKeyPress : function(e)
17186     {
17187         //Roo.log('listkeypress');
17188         // scroll to first matching element based on key pres..
17189         if (e.isSpecialKey()) {
17190             return false;
17191         }
17192         var k = String.fromCharCode(e.getKey()).toUpperCase();
17193         //Roo.log(k);
17194         var match  = false;
17195         var csel = this.view.getSelectedNodes();
17196         var cselitem = false;
17197         if (csel.length) {
17198             var ix = this.view.indexOf(csel[0]);
17199             cselitem  = this.store.getAt(ix);
17200             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17201                 cselitem = false;
17202             }
17203             
17204         }
17205         
17206         this.store.each(function(v) { 
17207             if (cselitem) {
17208                 // start at existing selection.
17209                 if (cselitem.id == v.id) {
17210                     cselitem = false;
17211                 }
17212                 return true;
17213             }
17214                 
17215             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17216                 match = this.store.indexOf(v);
17217                 return false;
17218             }
17219             return true;
17220         }, this);
17221         
17222         if (match === false) {
17223             return true; // no more action?
17224         }
17225         // scroll to?
17226         this.view.select(match);
17227         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17228         sn.scrollIntoView(sn.dom.parentNode, false);
17229     },
17230     
17231     onViewScroll : function(e, t){
17232         
17233         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){
17234             return;
17235         }
17236         
17237         this.hasQuery = true;
17238         
17239         this.loading = this.list.select('.loading', true).first();
17240         
17241         if(this.loading === null){
17242             this.list.createChild({
17243                 tag: 'div',
17244                 cls: 'loading roo-select2-more-results roo-select2-active',
17245                 html: 'Loading more results...'
17246             });
17247             
17248             this.loading = this.list.select('.loading', true).first();
17249             
17250             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17251             
17252             this.loading.hide();
17253         }
17254         
17255         this.loading.show();
17256         
17257         var _combo = this;
17258         
17259         this.page++;
17260         this.loadNext = true;
17261         
17262         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17263         
17264         return;
17265     },
17266     
17267     addItem : function(o)
17268     {   
17269         var dv = ''; // display value
17270         
17271         if (this.displayField) {
17272             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17273         } else {
17274             // this is an error condition!!!
17275             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17276         }
17277         
17278         if(!dv.length){
17279             return;
17280         }
17281         
17282         var choice = this.choices.createChild({
17283             tag: 'li',
17284             cls: 'roo-select2-search-choice',
17285             cn: [
17286                 {
17287                     tag: 'div',
17288                     html: dv
17289                 },
17290                 {
17291                     tag: 'a',
17292                     href: '#',
17293                     cls: 'roo-select2-search-choice-close fa fa-times',
17294                     tabindex: '-1'
17295                 }
17296             ]
17297             
17298         }, this.searchField);
17299         
17300         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17301         
17302         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17303         
17304         this.item.push(o);
17305         
17306         this.lastData = o;
17307         
17308         this.syncValue();
17309         
17310         this.inputEl().dom.value = '';
17311         
17312         this.validate();
17313     },
17314     
17315     onRemoveItem : function(e, _self, o)
17316     {
17317         e.preventDefault();
17318         
17319         this.lastItem = Roo.apply([], this.item);
17320         
17321         var index = this.item.indexOf(o.data) * 1;
17322         
17323         if( index < 0){
17324             Roo.log('not this item?!');
17325             return;
17326         }
17327         
17328         this.item.splice(index, 1);
17329         o.item.remove();
17330         
17331         this.syncValue();
17332         
17333         this.fireEvent('remove', this, e);
17334         
17335         this.validate();
17336         
17337     },
17338     
17339     syncValue : function()
17340     {
17341         if(!this.item.length){
17342             this.clearValue();
17343             return;
17344         }
17345             
17346         var value = [];
17347         var _this = this;
17348         Roo.each(this.item, function(i){
17349             if(_this.valueField){
17350                 value.push(i[_this.valueField]);
17351                 return;
17352             }
17353
17354             value.push(i);
17355         });
17356
17357         this.value = value.join(',');
17358
17359         if(this.hiddenField){
17360             this.hiddenField.dom.value = this.value;
17361         }
17362         
17363         this.store.fireEvent("datachanged", this.store);
17364         
17365         this.validate();
17366     },
17367     
17368     clearItem : function()
17369     {
17370         if(!this.multiple){
17371             return;
17372         }
17373         
17374         this.item = [];
17375         
17376         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17377            c.remove();
17378         });
17379         
17380         this.syncValue();
17381         
17382         this.validate();
17383         
17384         if(this.tickable && !Roo.isTouch){
17385             this.view.refresh();
17386         }
17387     },
17388     
17389     inputEl: function ()
17390     {
17391         if(Roo.isIOS && this.useNativeIOS){
17392             return this.el.select('select.roo-ios-select', true).first();
17393         }
17394         
17395         if(Roo.isTouch && this.mobileTouchView){
17396             return this.el.select('input.form-control',true).first();
17397         }
17398         
17399         if(this.tickable){
17400             return this.searchField;
17401         }
17402         
17403         return this.el.select('input.form-control',true).first();
17404     },
17405     
17406     onTickableFooterButtonClick : function(e, btn, el)
17407     {
17408         e.preventDefault();
17409         
17410         this.lastItem = Roo.apply([], this.item);
17411         
17412         if(btn && btn.name == 'cancel'){
17413             this.tickItems = Roo.apply([], this.item);
17414             this.collapse();
17415             return;
17416         }
17417         
17418         this.clearItem();
17419         
17420         var _this = this;
17421         
17422         Roo.each(this.tickItems, function(o){
17423             _this.addItem(o);
17424         });
17425         
17426         this.collapse();
17427         
17428     },
17429     
17430     validate : function()
17431     {
17432         if(this.getVisibilityEl().hasClass('hidden')){
17433             return true;
17434         }
17435         
17436         var v = this.getRawValue();
17437         
17438         if(this.multiple){
17439             v = this.getValue();
17440         }
17441         
17442         if(this.disabled || this.allowBlank || v.length){
17443             this.markValid();
17444             return true;
17445         }
17446         
17447         this.markInvalid();
17448         return false;
17449     },
17450     
17451     tickableInputEl : function()
17452     {
17453         if(!this.tickable || !this.editable){
17454             return this.inputEl();
17455         }
17456         
17457         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17458     },
17459     
17460     
17461     getAutoCreateTouchView : function()
17462     {
17463         var id = Roo.id();
17464         
17465         var cfg = {
17466             cls: 'form-group' //input-group
17467         };
17468         
17469         var input =  {
17470             tag: 'input',
17471             id : id,
17472             type : this.inputType,
17473             cls : 'form-control x-combo-noedit',
17474             autocomplete: 'new-password',
17475             placeholder : this.placeholder || '',
17476             readonly : true
17477         };
17478         
17479         if (this.name) {
17480             input.name = this.name;
17481         }
17482         
17483         if (this.size) {
17484             input.cls += ' input-' + this.size;
17485         }
17486         
17487         if (this.disabled) {
17488             input.disabled = true;
17489         }
17490         
17491         var inputblock = {
17492             cls : 'roo-combobox-wrap',
17493             cn : [
17494                 input
17495             ]
17496         };
17497         
17498         if(this.before){
17499             inputblock.cls += ' input-group';
17500             
17501             inputblock.cn.unshift({
17502                 tag :'span',
17503                 cls : 'input-group-addon input-group-prepend input-group-text',
17504                 html : this.before
17505             });
17506         }
17507         
17508         if(this.removable && !this.multiple){
17509             inputblock.cls += ' roo-removable';
17510             
17511             inputblock.cn.push({
17512                 tag: 'button',
17513                 html : 'x',
17514                 cls : 'roo-combo-removable-btn close'
17515             });
17516         }
17517
17518         if(this.hasFeedback && !this.allowBlank){
17519             
17520             inputblock.cls += ' has-feedback';
17521             
17522             inputblock.cn.push({
17523                 tag: 'span',
17524                 cls: 'glyphicon form-control-feedback'
17525             });
17526             
17527         }
17528         
17529         if (this.after) {
17530             
17531             inputblock.cls += (this.before) ? '' : ' input-group';
17532             
17533             inputblock.cn.push({
17534                 tag :'span',
17535                 cls : 'input-group-addon input-group-append input-group-text',
17536                 html : this.after
17537             });
17538         }
17539
17540         
17541         var ibwrap = inputblock;
17542         
17543         if(this.multiple){
17544             ibwrap = {
17545                 tag: 'ul',
17546                 cls: 'roo-select2-choices',
17547                 cn:[
17548                     {
17549                         tag: 'li',
17550                         cls: 'roo-select2-search-field',
17551                         cn: [
17552
17553                             inputblock
17554                         ]
17555                     }
17556                 ]
17557             };
17558         
17559             
17560         }
17561         
17562         var combobox = {
17563             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17564             cn: [
17565                 {
17566                     tag: 'input',
17567                     type : 'hidden',
17568                     cls: 'form-hidden-field'
17569                 },
17570                 ibwrap
17571             ]
17572         };
17573         
17574         if(!this.multiple && this.showToggleBtn){
17575             
17576             var caret = {
17577                 cls: 'caret'
17578             };
17579             
17580             if (this.caret != false) {
17581                 caret = {
17582                      tag: 'i',
17583                      cls: 'fa fa-' + this.caret
17584                 };
17585                 
17586             }
17587             
17588             combobox.cn.push({
17589                 tag :'span',
17590                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17591                 cn : [
17592                     Roo.bootstrap.version == 3 ? caret : '',
17593                     {
17594                         tag: 'span',
17595                         cls: 'combobox-clear',
17596                         cn  : [
17597                             {
17598                                 tag : 'i',
17599                                 cls: 'icon-remove'
17600                             }
17601                         ]
17602                     }
17603                 ]
17604
17605             })
17606         }
17607         
17608         if(this.multiple){
17609             combobox.cls += ' roo-select2-container-multi';
17610         }
17611         
17612         var required =  this.allowBlank ?  {
17613                     tag : 'i',
17614                     style: 'display: none'
17615                 } : {
17616                    tag : 'i',
17617                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17618                    tooltip : 'This field is required'
17619                 };
17620         
17621         var align = this.labelAlign || this.parentLabelAlign();
17622         
17623         if (align ==='left' && this.fieldLabel.length) {
17624
17625             cfg.cn = [
17626                 required,
17627                 {
17628                     tag: 'label',
17629                     cls : 'control-label col-form-label',
17630                     html : this.fieldLabel
17631
17632                 },
17633                 {
17634                     cls : 'roo-combobox-wrap ', 
17635                     cn: [
17636                         combobox
17637                     ]
17638                 }
17639             ];
17640             
17641             var labelCfg = cfg.cn[1];
17642             var contentCfg = cfg.cn[2];
17643             
17644
17645             if(this.indicatorpos == 'right'){
17646                 cfg.cn = [
17647                     {
17648                         tag: 'label',
17649                         'for' :  id,
17650                         cls : 'control-label col-form-label',
17651                         cn : [
17652                             {
17653                                 tag : 'span',
17654                                 html : this.fieldLabel
17655                             },
17656                             required
17657                         ]
17658                     },
17659                     {
17660                         cls : "roo-combobox-wrap ",
17661                         cn: [
17662                             combobox
17663                         ]
17664                     }
17665
17666                 ];
17667                 
17668                 labelCfg = cfg.cn[0];
17669                 contentCfg = cfg.cn[1];
17670             }
17671             
17672            
17673             
17674             if(this.labelWidth > 12){
17675                 labelCfg.style = "width: " + this.labelWidth + 'px';
17676             }
17677            
17678             if(this.labelWidth < 13 && this.labelmd == 0){
17679                 this.labelmd = this.labelWidth;
17680             }
17681             
17682             if(this.labellg > 0){
17683                 labelCfg.cls += ' col-lg-' + this.labellg;
17684                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17685             }
17686             
17687             if(this.labelmd > 0){
17688                 labelCfg.cls += ' col-md-' + this.labelmd;
17689                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17690             }
17691             
17692             if(this.labelsm > 0){
17693                 labelCfg.cls += ' col-sm-' + this.labelsm;
17694                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17695             }
17696             
17697             if(this.labelxs > 0){
17698                 labelCfg.cls += ' col-xs-' + this.labelxs;
17699                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17700             }
17701                 
17702                 
17703         } else if ( this.fieldLabel.length) {
17704             cfg.cn = [
17705                required,
17706                 {
17707                     tag: 'label',
17708                     cls : 'control-label',
17709                     html : this.fieldLabel
17710
17711                 },
17712                 {
17713                     cls : '', 
17714                     cn: [
17715                         combobox
17716                     ]
17717                 }
17718             ];
17719             
17720             if(this.indicatorpos == 'right'){
17721                 cfg.cn = [
17722                     {
17723                         tag: 'label',
17724                         cls : 'control-label',
17725                         html : this.fieldLabel,
17726                         cn : [
17727                             required
17728                         ]
17729                     },
17730                     {
17731                         cls : '', 
17732                         cn: [
17733                             combobox
17734                         ]
17735                     }
17736                 ];
17737             }
17738         } else {
17739             cfg.cn = combobox;    
17740         }
17741         
17742         
17743         var settings = this;
17744         
17745         ['xs','sm','md','lg'].map(function(size){
17746             if (settings[size]) {
17747                 cfg.cls += ' col-' + size + '-' + settings[size];
17748             }
17749         });
17750         
17751         return cfg;
17752     },
17753     
17754     initTouchView : function()
17755     {
17756         this.renderTouchView();
17757         
17758         this.touchViewEl.on('scroll', function(){
17759             this.el.dom.scrollTop = 0;
17760         }, this);
17761         
17762         this.originalValue = this.getValue();
17763         
17764         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17765         
17766         this.inputEl().on("click", this.showTouchView, this);
17767         if (this.triggerEl) {
17768             this.triggerEl.on("click", this.showTouchView, this);
17769         }
17770         
17771         
17772         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17773         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17774         
17775         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17776         
17777         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17778         this.store.on('load', this.onTouchViewLoad, this);
17779         this.store.on('loadexception', this.onTouchViewLoadException, this);
17780         
17781         if(this.hiddenName){
17782             
17783             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17784             
17785             this.hiddenField.dom.value =
17786                 this.hiddenValue !== undefined ? this.hiddenValue :
17787                 this.value !== undefined ? this.value : '';
17788         
17789             this.el.dom.removeAttribute('name');
17790             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17791         }
17792         
17793         if(this.multiple){
17794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17796         }
17797         
17798         if(this.removable && !this.multiple){
17799             var close = this.closeTriggerEl();
17800             if(close){
17801                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17802                 close.on('click', this.removeBtnClick, this, close);
17803             }
17804         }
17805         /*
17806          * fix the bug in Safari iOS8
17807          */
17808         this.inputEl().on("focus", function(e){
17809             document.activeElement.blur();
17810         }, this);
17811         
17812         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17813         
17814         return;
17815         
17816         
17817     },
17818     
17819     renderTouchView : function()
17820     {
17821         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17822         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17823         
17824         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17825         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17826         
17827         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17828         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17829         this.touchViewBodyEl.setStyle('overflow', 'auto');
17830         
17831         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17832         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17833         
17834         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17835         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17836         
17837     },
17838     
17839     showTouchView : function()
17840     {
17841         if(this.disabled){
17842             return;
17843         }
17844         
17845         this.touchViewHeaderEl.hide();
17846
17847         if(this.modalTitle.length){
17848             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17849             this.touchViewHeaderEl.show();
17850         }
17851
17852         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17853         this.touchViewEl.show();
17854
17855         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17856         
17857         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17858         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17859
17860         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17861
17862         if(this.modalTitle.length){
17863             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17864         }
17865         
17866         this.touchViewBodyEl.setHeight(bodyHeight);
17867
17868         if(this.animate){
17869             var _this = this;
17870             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17871         }else{
17872             this.touchViewEl.addClass(['in','show']);
17873         }
17874         
17875         if(this._touchViewMask){
17876             Roo.get(document.body).addClass("x-body-masked");
17877             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17878             this._touchViewMask.setStyle('z-index', 10000);
17879             this._touchViewMask.addClass('show');
17880         }
17881         
17882         this.doTouchViewQuery();
17883         
17884     },
17885     
17886     hideTouchView : function()
17887     {
17888         this.touchViewEl.removeClass(['in','show']);
17889
17890         if(this.animate){
17891             var _this = this;
17892             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17893         }else{
17894             this.touchViewEl.setStyle('display', 'none');
17895         }
17896         
17897         if(this._touchViewMask){
17898             this._touchViewMask.removeClass('show');
17899             Roo.get(document.body).removeClass("x-body-masked");
17900         }
17901     },
17902     
17903     setTouchViewValue : function()
17904     {
17905         if(this.multiple){
17906             this.clearItem();
17907         
17908             var _this = this;
17909
17910             Roo.each(this.tickItems, function(o){
17911                 this.addItem(o);
17912             }, this);
17913         }
17914         
17915         this.hideTouchView();
17916     },
17917     
17918     doTouchViewQuery : function()
17919     {
17920         var qe = {
17921             query: '',
17922             forceAll: true,
17923             combo: this,
17924             cancel:false
17925         };
17926         
17927         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17928             return false;
17929         }
17930         
17931         if(!this.alwaysQuery || this.mode == 'local'){
17932             this.onTouchViewLoad();
17933             return;
17934         }
17935         
17936         this.store.load();
17937     },
17938     
17939     onTouchViewBeforeLoad : function(combo,opts)
17940     {
17941         return;
17942     },
17943
17944     // private
17945     onTouchViewLoad : function()
17946     {
17947         if(this.store.getCount() < 1){
17948             this.onTouchViewEmptyResults();
17949             return;
17950         }
17951         
17952         this.clearTouchView();
17953         
17954         var rawValue = this.getRawValue();
17955         
17956         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17957         
17958         this.tickItems = [];
17959         
17960         this.store.data.each(function(d, rowIndex){
17961             var row = this.touchViewListGroup.createChild(template);
17962             
17963             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17964                 row.addClass(d.data.cls);
17965             }
17966             
17967             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17968                 var cfg = {
17969                     data : d.data,
17970                     html : d.data[this.displayField]
17971                 };
17972                 
17973                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17974                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17975                 }
17976             }
17977             row.removeClass('selected');
17978             if(!this.multiple && this.valueField &&
17979                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17980             {
17981                 // radio buttons..
17982                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17983                 row.addClass('selected');
17984             }
17985             
17986             if(this.multiple && this.valueField &&
17987                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17988             {
17989                 
17990                 // checkboxes...
17991                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17992                 this.tickItems.push(d.data);
17993             }
17994             
17995             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17996             
17997         }, this);
17998         
17999         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18000         
18001         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18002
18003         if(this.modalTitle.length){
18004             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18005         }
18006
18007         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18008         
18009         if(this.mobile_restrict_height && listHeight < bodyHeight){
18010             this.touchViewBodyEl.setHeight(listHeight);
18011         }
18012         
18013         var _this = this;
18014         
18015         if(firstChecked && listHeight > bodyHeight){
18016             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18017         }
18018         
18019     },
18020     
18021     onTouchViewLoadException : function()
18022     {
18023         this.hideTouchView();
18024     },
18025     
18026     onTouchViewEmptyResults : function()
18027     {
18028         this.clearTouchView();
18029         
18030         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18031         
18032         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18033         
18034     },
18035     
18036     clearTouchView : function()
18037     {
18038         this.touchViewListGroup.dom.innerHTML = '';
18039     },
18040     
18041     onTouchViewClick : function(e, el, o)
18042     {
18043         e.preventDefault();
18044         
18045         var row = o.row;
18046         var rowIndex = o.rowIndex;
18047         
18048         var r = this.store.getAt(rowIndex);
18049         
18050         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18051             
18052             if(!this.multiple){
18053                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18054                     c.dom.removeAttribute('checked');
18055                 }, this);
18056
18057                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18058
18059                 this.setFromData(r.data);
18060
18061                 var close = this.closeTriggerEl();
18062
18063                 if(close){
18064                     close.show();
18065                 }
18066
18067                 this.hideTouchView();
18068
18069                 this.fireEvent('select', this, r, rowIndex);
18070
18071                 return;
18072             }
18073
18074             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18075                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18076                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18077                 return;
18078             }
18079
18080             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18081             this.addItem(r.data);
18082             this.tickItems.push(r.data);
18083         }
18084     },
18085     
18086     getAutoCreateNativeIOS : function()
18087     {
18088         var cfg = {
18089             cls: 'form-group' //input-group,
18090         };
18091         
18092         var combobox =  {
18093             tag: 'select',
18094             cls : 'roo-ios-select'
18095         };
18096         
18097         if (this.name) {
18098             combobox.name = this.name;
18099         }
18100         
18101         if (this.disabled) {
18102             combobox.disabled = true;
18103         }
18104         
18105         var settings = this;
18106         
18107         ['xs','sm','md','lg'].map(function(size){
18108             if (settings[size]) {
18109                 cfg.cls += ' col-' + size + '-' + settings[size];
18110             }
18111         });
18112         
18113         cfg.cn = combobox;
18114         
18115         return cfg;
18116         
18117     },
18118     
18119     initIOSView : function()
18120     {
18121         this.store.on('load', this.onIOSViewLoad, this);
18122         
18123         return;
18124     },
18125     
18126     onIOSViewLoad : function()
18127     {
18128         if(this.store.getCount() < 1){
18129             return;
18130         }
18131         
18132         this.clearIOSView();
18133         
18134         if(this.allowBlank) {
18135             
18136             var default_text = '-- SELECT --';
18137             
18138             if(this.placeholder.length){
18139                 default_text = this.placeholder;
18140             }
18141             
18142             if(this.emptyTitle.length){
18143                 default_text += ' - ' + this.emptyTitle + ' -';
18144             }
18145             
18146             var opt = this.inputEl().createChild({
18147                 tag: 'option',
18148                 value : 0,
18149                 html : default_text
18150             });
18151             
18152             var o = {};
18153             o[this.valueField] = 0;
18154             o[this.displayField] = default_text;
18155             
18156             this.ios_options.push({
18157                 data : o,
18158                 el : opt
18159             });
18160             
18161         }
18162         
18163         this.store.data.each(function(d, rowIndex){
18164             
18165             var html = '';
18166             
18167             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18168                 html = d.data[this.displayField];
18169             }
18170             
18171             var value = '';
18172             
18173             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18174                 value = d.data[this.valueField];
18175             }
18176             
18177             var option = {
18178                 tag: 'option',
18179                 value : value,
18180                 html : html
18181             };
18182             
18183             if(this.value == d.data[this.valueField]){
18184                 option['selected'] = true;
18185             }
18186             
18187             var opt = this.inputEl().createChild(option);
18188             
18189             this.ios_options.push({
18190                 data : d.data,
18191                 el : opt
18192             });
18193             
18194         }, this);
18195         
18196         this.inputEl().on('change', function(){
18197            this.fireEvent('select', this);
18198         }, this);
18199         
18200     },
18201     
18202     clearIOSView: function()
18203     {
18204         this.inputEl().dom.innerHTML = '';
18205         
18206         this.ios_options = [];
18207     },
18208     
18209     setIOSValue: function(v)
18210     {
18211         this.value = v;
18212         
18213         if(!this.ios_options){
18214             return;
18215         }
18216         
18217         Roo.each(this.ios_options, function(opts){
18218            
18219            opts.el.dom.removeAttribute('selected');
18220            
18221            if(opts.data[this.valueField] != v){
18222                return;
18223            }
18224            
18225            opts.el.dom.setAttribute('selected', true);
18226            
18227         }, this);
18228     }
18229
18230     /** 
18231     * @cfg {Boolean} grow 
18232     * @hide 
18233     */
18234     /** 
18235     * @cfg {Number} growMin 
18236     * @hide 
18237     */
18238     /** 
18239     * @cfg {Number} growMax 
18240     * @hide 
18241     */
18242     /**
18243      * @hide
18244      * @method autoSize
18245      */
18246 });
18247
18248 Roo.apply(Roo.bootstrap.ComboBox,  {
18249     
18250     header : {
18251         tag: 'div',
18252         cls: 'modal-header',
18253         cn: [
18254             {
18255                 tag: 'h4',
18256                 cls: 'modal-title'
18257             }
18258         ]
18259     },
18260     
18261     body : {
18262         tag: 'div',
18263         cls: 'modal-body',
18264         cn: [
18265             {
18266                 tag: 'ul',
18267                 cls: 'list-group'
18268             }
18269         ]
18270     },
18271     
18272     listItemRadio : {
18273         tag: 'li',
18274         cls: 'list-group-item',
18275         cn: [
18276             {
18277                 tag: 'span',
18278                 cls: 'roo-combobox-list-group-item-value'
18279             },
18280             {
18281                 tag: 'div',
18282                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18283                 cn: [
18284                     {
18285                         tag: 'input',
18286                         type: 'radio'
18287                     },
18288                     {
18289                         tag: 'label'
18290                     }
18291                 ]
18292             }
18293         ]
18294     },
18295     
18296     listItemCheckbox : {
18297         tag: 'li',
18298         cls: 'list-group-item',
18299         cn: [
18300             {
18301                 tag: 'span',
18302                 cls: 'roo-combobox-list-group-item-value'
18303             },
18304             {
18305                 tag: 'div',
18306                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18307                 cn: [
18308                     {
18309                         tag: 'input',
18310                         type: 'checkbox'
18311                     },
18312                     {
18313                         tag: 'label'
18314                     }
18315                 ]
18316             }
18317         ]
18318     },
18319     
18320     emptyResult : {
18321         tag: 'div',
18322         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18323     },
18324     
18325     footer : {
18326         tag: 'div',
18327         cls: 'modal-footer',
18328         cn: [
18329             {
18330                 tag: 'div',
18331                 cls: 'row',
18332                 cn: [
18333                     {
18334                         tag: 'div',
18335                         cls: 'col-xs-6 text-left',
18336                         cn: {
18337                             tag: 'button',
18338                             cls: 'btn btn-danger roo-touch-view-cancel',
18339                             html: 'Cancel'
18340                         }
18341                     },
18342                     {
18343                         tag: 'div',
18344                         cls: 'col-xs-6 text-right',
18345                         cn: {
18346                             tag: 'button',
18347                             cls: 'btn btn-success roo-touch-view-ok',
18348                             html: 'OK'
18349                         }
18350                     }
18351                 ]
18352             }
18353         ]
18354         
18355     }
18356 });
18357
18358 Roo.apply(Roo.bootstrap.ComboBox,  {
18359     
18360     touchViewTemplate : {
18361         tag: 'div',
18362         cls: 'modal fade roo-combobox-touch-view',
18363         cn: [
18364             {
18365                 tag: 'div',
18366                 cls: 'modal-dialog',
18367                 style : 'position:fixed', // we have to fix position....
18368                 cn: [
18369                     {
18370                         tag: 'div',
18371                         cls: 'modal-content',
18372                         cn: [
18373                             Roo.bootstrap.ComboBox.header,
18374                             Roo.bootstrap.ComboBox.body,
18375                             Roo.bootstrap.ComboBox.footer
18376                         ]
18377                     }
18378                 ]
18379             }
18380         ]
18381     }
18382 });/*
18383  * Based on:
18384  * Ext JS Library 1.1.1
18385  * Copyright(c) 2006-2007, Ext JS, LLC.
18386  *
18387  * Originally Released Under LGPL - original licence link has changed is not relivant.
18388  *
18389  * Fork - LGPL
18390  * <script type="text/javascript">
18391  */
18392
18393 /**
18394  * @class Roo.View
18395  * @extends Roo.util.Observable
18396  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18397  * This class also supports single and multi selection modes. <br>
18398  * Create a data model bound view:
18399  <pre><code>
18400  var store = new Roo.data.Store(...);
18401
18402  var view = new Roo.View({
18403     el : "my-element",
18404     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18405  
18406     singleSelect: true,
18407     selectedClass: "ydataview-selected",
18408     store: store
18409  });
18410
18411  // listen for node click?
18412  view.on("click", function(vw, index, node, e){
18413  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18414  });
18415
18416  // load XML data
18417  dataModel.load("foobar.xml");
18418  </code></pre>
18419  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18420  * <br><br>
18421  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18422  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18423  * 
18424  * Note: old style constructor is still suported (container, template, config)
18425  * 
18426  * @constructor
18427  * Create a new View
18428  * @param {Object} config The config object
18429  * 
18430  */
18431 Roo.View = function(config, depreciated_tpl, depreciated_config){
18432     
18433     this.parent = false;
18434     
18435     if (typeof(depreciated_tpl) == 'undefined') {
18436         // new way.. - universal constructor.
18437         Roo.apply(this, config);
18438         this.el  = Roo.get(this.el);
18439     } else {
18440         // old format..
18441         this.el  = Roo.get(config);
18442         this.tpl = depreciated_tpl;
18443         Roo.apply(this, depreciated_config);
18444     }
18445     this.wrapEl  = this.el.wrap().wrap();
18446     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18447     
18448     
18449     if(typeof(this.tpl) == "string"){
18450         this.tpl = new Roo.Template(this.tpl);
18451     } else {
18452         // support xtype ctors..
18453         this.tpl = new Roo.factory(this.tpl, Roo);
18454     }
18455     
18456     
18457     this.tpl.compile();
18458     
18459     /** @private */
18460     this.addEvents({
18461         /**
18462          * @event beforeclick
18463          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
18470         /**
18471          * @event click
18472          * Fires when a template node is 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             "click" : true,
18479         /**
18480          * @event dblclick
18481          * Fires when a template node is double 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             "dblclick" : true,
18488         /**
18489          * @event contextmenu
18490          * Fires when a template node is right clicked.
18491          * @param {Roo.View} this
18492          * @param {Number} index The index of the target node
18493          * @param {HTMLElement} node The target node
18494          * @param {Roo.EventObject} e The raw event object
18495          */
18496             "contextmenu" : true,
18497         /**
18498          * @event selectionchange
18499          * Fires when the selected nodes change.
18500          * @param {Roo.View} this
18501          * @param {Array} selections Array of the selected nodes
18502          */
18503             "selectionchange" : true,
18504     
18505         /**
18506          * @event beforeselect
18507          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18508          * @param {Roo.View} this
18509          * @param {HTMLElement} node The node to be selected
18510          * @param {Array} selections Array of currently selected nodes
18511          */
18512             "beforeselect" : true,
18513         /**
18514          * @event preparedata
18515          * Fires on every row to render, to allow you to change the data.
18516          * @param {Roo.View} this
18517          * @param {Object} data to be rendered (change this)
18518          */
18519           "preparedata" : true
18520           
18521           
18522         });
18523
18524
18525
18526     this.el.on({
18527         "click": this.onClick,
18528         "dblclick": this.onDblClick,
18529         "contextmenu": this.onContextMenu,
18530         scope:this
18531     });
18532
18533     this.selections = [];
18534     this.nodes = [];
18535     this.cmp = new Roo.CompositeElementLite([]);
18536     if(this.store){
18537         this.store = Roo.factory(this.store, Roo.data);
18538         this.setStore(this.store, true);
18539     }
18540     
18541     if ( this.footer && this.footer.xtype) {
18542            
18543          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18544         
18545         this.footer.dataSource = this.store;
18546         this.footer.container = fctr;
18547         this.footer = Roo.factory(this.footer, Roo);
18548         fctr.insertFirst(this.el);
18549         
18550         // this is a bit insane - as the paging toolbar seems to detach the el..
18551 //        dom.parentNode.parentNode.parentNode
18552          // they get detached?
18553     }
18554     
18555     
18556     Roo.View.superclass.constructor.call(this);
18557     
18558     
18559 };
18560
18561 Roo.extend(Roo.View, Roo.util.Observable, {
18562     
18563      /**
18564      * @cfg {Roo.data.Store} store Data store to load data from.
18565      */
18566     store : false,
18567     
18568     /**
18569      * @cfg {String|Roo.Element} el The container element.
18570      */
18571     el : '',
18572     
18573     /**
18574      * @cfg {String|Roo.Template} tpl The template used by this View 
18575      */
18576     tpl : false,
18577     /**
18578      * @cfg {String} dataName the named area of the template to use as the data area
18579      *                          Works with domtemplates roo-name="name"
18580      */
18581     dataName: false,
18582     /**
18583      * @cfg {String} selectedClass The css class to add to selected nodes
18584      */
18585     selectedClass : "x-view-selected",
18586      /**
18587      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18588      */
18589     emptyText : "",
18590     
18591     /**
18592      * @cfg {String} text to display on mask (default Loading)
18593      */
18594     mask : false,
18595     /**
18596      * @cfg {Boolean} multiSelect Allow multiple selection
18597      */
18598     multiSelect : false,
18599     /**
18600      * @cfg {Boolean} singleSelect Allow single selection
18601      */
18602     singleSelect:  false,
18603     
18604     /**
18605      * @cfg {Boolean} toggleSelect - selecting 
18606      */
18607     toggleSelect : false,
18608     
18609     /**
18610      * @cfg {Boolean} tickable - selecting 
18611      */
18612     tickable : false,
18613     
18614     /**
18615      * Returns the element this view is bound to.
18616      * @return {Roo.Element}
18617      */
18618     getEl : function(){
18619         return this.wrapEl;
18620     },
18621     
18622     
18623
18624     /**
18625      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18626      */
18627     refresh : function(){
18628         //Roo.log('refresh');
18629         var t = this.tpl;
18630         
18631         // if we are using something like 'domtemplate', then
18632         // the what gets used is:
18633         // t.applySubtemplate(NAME, data, wrapping data..)
18634         // the outer template then get' applied with
18635         //     the store 'extra data'
18636         // and the body get's added to the
18637         //      roo-name="data" node?
18638         //      <span class='roo-tpl-{name}'></span> ?????
18639         
18640         
18641         
18642         this.clearSelections();
18643         this.el.update("");
18644         var html = [];
18645         var records = this.store.getRange();
18646         if(records.length < 1) {
18647             
18648             // is this valid??  = should it render a template??
18649             
18650             this.el.update(this.emptyText);
18651             return;
18652         }
18653         var el = this.el;
18654         if (this.dataName) {
18655             this.el.update(t.apply(this.store.meta)); //????
18656             el = this.el.child('.roo-tpl-' + this.dataName);
18657         }
18658         
18659         for(var i = 0, len = records.length; i < len; i++){
18660             var data = this.prepareData(records[i].data, i, records[i]);
18661             this.fireEvent("preparedata", this, data, i, records[i]);
18662             
18663             var d = Roo.apply({}, data);
18664             
18665             if(this.tickable){
18666                 Roo.apply(d, {'roo-id' : Roo.id()});
18667                 
18668                 var _this = this;
18669             
18670                 Roo.each(this.parent.item, function(item){
18671                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18672                         return;
18673                     }
18674                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18675                 });
18676             }
18677             
18678             html[html.length] = Roo.util.Format.trim(
18679                 this.dataName ?
18680                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18681                     t.apply(d)
18682             );
18683         }
18684         
18685         
18686         
18687         el.update(html.join(""));
18688         this.nodes = el.dom.childNodes;
18689         this.updateIndexes(0);
18690     },
18691     
18692
18693     /**
18694      * Function to override to reformat the data that is sent to
18695      * the template for each node.
18696      * DEPRICATED - use the preparedata event handler.
18697      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18698      * a JSON object for an UpdateManager bound view).
18699      */
18700     prepareData : function(data, index, record)
18701     {
18702         this.fireEvent("preparedata", this, data, index, record);
18703         return data;
18704     },
18705
18706     onUpdate : function(ds, record){
18707         // Roo.log('on update');   
18708         this.clearSelections();
18709         var index = this.store.indexOf(record);
18710         var n = this.nodes[index];
18711         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18712         n.parentNode.removeChild(n);
18713         this.updateIndexes(index, index);
18714     },
18715
18716     
18717     
18718 // --------- FIXME     
18719     onAdd : function(ds, records, index)
18720     {
18721         //Roo.log(['on Add', ds, records, index] );        
18722         this.clearSelections();
18723         if(this.nodes.length == 0){
18724             this.refresh();
18725             return;
18726         }
18727         var n = this.nodes[index];
18728         for(var i = 0, len = records.length; i < len; i++){
18729             var d = this.prepareData(records[i].data, i, records[i]);
18730             if(n){
18731                 this.tpl.insertBefore(n, d);
18732             }else{
18733                 
18734                 this.tpl.append(this.el, d);
18735             }
18736         }
18737         this.updateIndexes(index);
18738     },
18739
18740     onRemove : function(ds, record, index){
18741        // Roo.log('onRemove');
18742         this.clearSelections();
18743         var el = this.dataName  ?
18744             this.el.child('.roo-tpl-' + this.dataName) :
18745             this.el; 
18746         
18747         el.dom.removeChild(this.nodes[index]);
18748         this.updateIndexes(index);
18749     },
18750
18751     /**
18752      * Refresh an individual node.
18753      * @param {Number} index
18754      */
18755     refreshNode : function(index){
18756         this.onUpdate(this.store, this.store.getAt(index));
18757     },
18758
18759     updateIndexes : function(startIndex, endIndex){
18760         var ns = this.nodes;
18761         startIndex = startIndex || 0;
18762         endIndex = endIndex || ns.length - 1;
18763         for(var i = startIndex; i <= endIndex; i++){
18764             ns[i].nodeIndex = i;
18765         }
18766     },
18767
18768     /**
18769      * Changes the data store this view uses and refresh the view.
18770      * @param {Store} store
18771      */
18772     setStore : function(store, initial){
18773         if(!initial && this.store){
18774             this.store.un("datachanged", this.refresh);
18775             this.store.un("add", this.onAdd);
18776             this.store.un("remove", this.onRemove);
18777             this.store.un("update", this.onUpdate);
18778             this.store.un("clear", this.refresh);
18779             this.store.un("beforeload", this.onBeforeLoad);
18780             this.store.un("load", this.onLoad);
18781             this.store.un("loadexception", this.onLoad);
18782         }
18783         if(store){
18784           
18785             store.on("datachanged", this.refresh, this);
18786             store.on("add", this.onAdd, this);
18787             store.on("remove", this.onRemove, this);
18788             store.on("update", this.onUpdate, this);
18789             store.on("clear", this.refresh, this);
18790             store.on("beforeload", this.onBeforeLoad, this);
18791             store.on("load", this.onLoad, this);
18792             store.on("loadexception", this.onLoad, this);
18793         }
18794         
18795         if(store){
18796             this.refresh();
18797         }
18798     },
18799     /**
18800      * onbeforeLoad - masks the loading area.
18801      *
18802      */
18803     onBeforeLoad : function(store,opts)
18804     {
18805          //Roo.log('onBeforeLoad');   
18806         if (!opts.add) {
18807             this.el.update("");
18808         }
18809         this.el.mask(this.mask ? this.mask : "Loading" ); 
18810     },
18811     onLoad : function ()
18812     {
18813         this.el.unmask();
18814     },
18815     
18816
18817     /**
18818      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18819      * @param {HTMLElement} node
18820      * @return {HTMLElement} The template node
18821      */
18822     findItemFromChild : function(node){
18823         var el = this.dataName  ?
18824             this.el.child('.roo-tpl-' + this.dataName,true) :
18825             this.el.dom; 
18826         
18827         if(!node || node.parentNode == el){
18828                     return node;
18829             }
18830             var p = node.parentNode;
18831             while(p && p != el){
18832             if(p.parentNode == el){
18833                 return p;
18834             }
18835             p = p.parentNode;
18836         }
18837             return null;
18838     },
18839
18840     /** @ignore */
18841     onClick : function(e){
18842         var item = this.findItemFromChild(e.getTarget());
18843         if(item){
18844             var index = this.indexOf(item);
18845             if(this.onItemClick(item, index, e) !== false){
18846                 this.fireEvent("click", this, index, item, e);
18847             }
18848         }else{
18849             this.clearSelections();
18850         }
18851     },
18852
18853     /** @ignore */
18854     onContextMenu : function(e){
18855         var item = this.findItemFromChild(e.getTarget());
18856         if(item){
18857             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18858         }
18859     },
18860
18861     /** @ignore */
18862     onDblClick : function(e){
18863         var item = this.findItemFromChild(e.getTarget());
18864         if(item){
18865             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18866         }
18867     },
18868
18869     onItemClick : function(item, index, e)
18870     {
18871         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18872             return false;
18873         }
18874         if (this.toggleSelect) {
18875             var m = this.isSelected(item) ? 'unselect' : 'select';
18876             //Roo.log(m);
18877             var _t = this;
18878             _t[m](item, true, false);
18879             return true;
18880         }
18881         if(this.multiSelect || this.singleSelect){
18882             if(this.multiSelect && e.shiftKey && this.lastSelection){
18883                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18884             }else{
18885                 this.select(item, this.multiSelect && e.ctrlKey);
18886                 this.lastSelection = item;
18887             }
18888             
18889             if(!this.tickable){
18890                 e.preventDefault();
18891             }
18892             
18893         }
18894         return true;
18895     },
18896
18897     /**
18898      * Get the number of selected nodes.
18899      * @return {Number}
18900      */
18901     getSelectionCount : function(){
18902         return this.selections.length;
18903     },
18904
18905     /**
18906      * Get the currently selected nodes.
18907      * @return {Array} An array of HTMLElements
18908      */
18909     getSelectedNodes : function(){
18910         return this.selections;
18911     },
18912
18913     /**
18914      * Get the indexes of the selected nodes.
18915      * @return {Array}
18916      */
18917     getSelectedIndexes : function(){
18918         var indexes = [], s = this.selections;
18919         for(var i = 0, len = s.length; i < len; i++){
18920             indexes.push(s[i].nodeIndex);
18921         }
18922         return indexes;
18923     },
18924
18925     /**
18926      * Clear all selections
18927      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18928      */
18929     clearSelections : function(suppressEvent){
18930         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18931             this.cmp.elements = this.selections;
18932             this.cmp.removeClass(this.selectedClass);
18933             this.selections = [];
18934             if(!suppressEvent){
18935                 this.fireEvent("selectionchange", this, this.selections);
18936             }
18937         }
18938     },
18939
18940     /**
18941      * Returns true if the passed node is selected
18942      * @param {HTMLElement/Number} node The node or node index
18943      * @return {Boolean}
18944      */
18945     isSelected : function(node){
18946         var s = this.selections;
18947         if(s.length < 1){
18948             return false;
18949         }
18950         node = this.getNode(node);
18951         return s.indexOf(node) !== -1;
18952     },
18953
18954     /**
18955      * Selects nodes.
18956      * @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
18957      * @param {Boolean} keepExisting (optional) true to keep existing selections
18958      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18959      */
18960     select : function(nodeInfo, keepExisting, suppressEvent){
18961         if(nodeInfo instanceof Array){
18962             if(!keepExisting){
18963                 this.clearSelections(true);
18964             }
18965             for(var i = 0, len = nodeInfo.length; i < len; i++){
18966                 this.select(nodeInfo[i], true, true);
18967             }
18968             return;
18969         } 
18970         var node = this.getNode(nodeInfo);
18971         if(!node || this.isSelected(node)){
18972             return; // already selected.
18973         }
18974         if(!keepExisting){
18975             this.clearSelections(true);
18976         }
18977         
18978         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18979             Roo.fly(node).addClass(this.selectedClass);
18980             this.selections.push(node);
18981             if(!suppressEvent){
18982                 this.fireEvent("selectionchange", this, this.selections);
18983             }
18984         }
18985         
18986         
18987     },
18988       /**
18989      * Unselects nodes.
18990      * @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
18991      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18992      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18993      */
18994     unselect : function(nodeInfo, keepExisting, suppressEvent)
18995     {
18996         if(nodeInfo instanceof Array){
18997             Roo.each(this.selections, function(s) {
18998                 this.unselect(s, nodeInfo);
18999             }, this);
19000             return;
19001         }
19002         var node = this.getNode(nodeInfo);
19003         if(!node || !this.isSelected(node)){
19004             //Roo.log("not selected");
19005             return; // not selected.
19006         }
19007         // fireevent???
19008         var ns = [];
19009         Roo.each(this.selections, function(s) {
19010             if (s == node ) {
19011                 Roo.fly(node).removeClass(this.selectedClass);
19012
19013                 return;
19014             }
19015             ns.push(s);
19016         },this);
19017         
19018         this.selections= ns;
19019         this.fireEvent("selectionchange", this, this.selections);
19020     },
19021
19022     /**
19023      * Gets a template node.
19024      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025      * @return {HTMLElement} The node or null if it wasn't found
19026      */
19027     getNode : function(nodeInfo){
19028         if(typeof nodeInfo == "string"){
19029             return document.getElementById(nodeInfo);
19030         }else if(typeof nodeInfo == "number"){
19031             return this.nodes[nodeInfo];
19032         }
19033         return nodeInfo;
19034     },
19035
19036     /**
19037      * Gets a range template nodes.
19038      * @param {Number} startIndex
19039      * @param {Number} endIndex
19040      * @return {Array} An array of nodes
19041      */
19042     getNodes : function(start, end){
19043         var ns = this.nodes;
19044         start = start || 0;
19045         end = typeof end == "undefined" ? ns.length - 1 : end;
19046         var nodes = [];
19047         if(start <= end){
19048             for(var i = start; i <= end; i++){
19049                 nodes.push(ns[i]);
19050             }
19051         } else{
19052             for(var i = start; i >= end; i--){
19053                 nodes.push(ns[i]);
19054             }
19055         }
19056         return nodes;
19057     },
19058
19059     /**
19060      * Finds the index of the passed node
19061      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19062      * @return {Number} The index of the node or -1
19063      */
19064     indexOf : function(node){
19065         node = this.getNode(node);
19066         if(typeof node.nodeIndex == "number"){
19067             return node.nodeIndex;
19068         }
19069         var ns = this.nodes;
19070         for(var i = 0, len = ns.length; i < len; i++){
19071             if(ns[i] == node){
19072                 return i;
19073             }
19074         }
19075         return -1;
19076     }
19077 });
19078 /*
19079  * - LGPL
19080  *
19081  * based on jquery fullcalendar
19082  * 
19083  */
19084
19085 Roo.bootstrap = Roo.bootstrap || {};
19086 /**
19087  * @class Roo.bootstrap.Calendar
19088  * @extends Roo.bootstrap.Component
19089  * Bootstrap Calendar class
19090  * @cfg {Boolean} loadMask (true|false) default false
19091  * @cfg {Object} header generate the user specific header of the calendar, default false
19092
19093  * @constructor
19094  * Create a new Container
19095  * @param {Object} config The config object
19096  */
19097
19098
19099
19100 Roo.bootstrap.Calendar = function(config){
19101     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19102      this.addEvents({
19103         /**
19104              * @event select
19105              * Fires when a date is selected
19106              * @param {DatePicker} this
19107              * @param {Date} date The selected date
19108              */
19109         'select': true,
19110         /**
19111              * @event monthchange
19112              * Fires when the displayed month changes 
19113              * @param {DatePicker} this
19114              * @param {Date} date The selected month
19115              */
19116         'monthchange': true,
19117         /**
19118              * @event evententer
19119              * Fires when mouse over an event
19120              * @param {Calendar} this
19121              * @param {event} Event
19122              */
19123         'evententer': true,
19124         /**
19125              * @event eventleave
19126              * Fires when the mouse leaves an
19127              * @param {Calendar} this
19128              * @param {event}
19129              */
19130         'eventleave': true,
19131         /**
19132              * @event eventclick
19133              * Fires when the mouse click an
19134              * @param {Calendar} this
19135              * @param {event}
19136              */
19137         'eventclick': true
19138         
19139     });
19140
19141 };
19142
19143 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19144     
19145      /**
19146      * @cfg {Number} startDay
19147      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19148      */
19149     startDay : 0,
19150     
19151     loadMask : false,
19152     
19153     header : false,
19154       
19155     getAutoCreate : function(){
19156         
19157         
19158         var fc_button = function(name, corner, style, content ) {
19159             return Roo.apply({},{
19160                 tag : 'span',
19161                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19162                          (corner.length ?
19163                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19164                             ''
19165                         ),
19166                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19167                 unselectable: 'on'
19168             });
19169         };
19170         
19171         var header = {};
19172         
19173         if(!this.header){
19174             header = {
19175                 tag : 'table',
19176                 cls : 'fc-header',
19177                 style : 'width:100%',
19178                 cn : [
19179                     {
19180                         tag: 'tr',
19181                         cn : [
19182                             {
19183                                 tag : 'td',
19184                                 cls : 'fc-header-left',
19185                                 cn : [
19186                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19187                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19188                                     { tag: 'span', cls: 'fc-header-space' },
19189                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19190
19191
19192                                 ]
19193                             },
19194
19195                             {
19196                                 tag : 'td',
19197                                 cls : 'fc-header-center',
19198                                 cn : [
19199                                     {
19200                                         tag: 'span',
19201                                         cls: 'fc-header-title',
19202                                         cn : {
19203                                             tag: 'H2',
19204                                             html : 'month / year'
19205                                         }
19206                                     }
19207
19208                                 ]
19209                             },
19210                             {
19211                                 tag : 'td',
19212                                 cls : 'fc-header-right',
19213                                 cn : [
19214                               /*      fc_button('month', 'left', '', 'month' ),
19215                                     fc_button('week', '', '', 'week' ),
19216                                     fc_button('day', 'right', '', 'day' )
19217                                 */    
19218
19219                                 ]
19220                             }
19221
19222                         ]
19223                     }
19224                 ]
19225             };
19226         }
19227         
19228         header = this.header;
19229         
19230        
19231         var cal_heads = function() {
19232             var ret = [];
19233             // fixme - handle this.
19234             
19235             for (var i =0; i < Date.dayNames.length; i++) {
19236                 var d = Date.dayNames[i];
19237                 ret.push({
19238                     tag: 'th',
19239                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19240                     html : d.substring(0,3)
19241                 });
19242                 
19243             }
19244             ret[0].cls += ' fc-first';
19245             ret[6].cls += ' fc-last';
19246             return ret;
19247         };
19248         var cal_cell = function(n) {
19249             return  {
19250                 tag: 'td',
19251                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19252                 cn : [
19253                     {
19254                         cn : [
19255                             {
19256                                 cls: 'fc-day-number',
19257                                 html: 'D'
19258                             },
19259                             {
19260                                 cls: 'fc-day-content',
19261                              
19262                                 cn : [
19263                                      {
19264                                         style: 'position: relative;' // height: 17px;
19265                                     }
19266                                 ]
19267                             }
19268                             
19269                             
19270                         ]
19271                     }
19272                 ]
19273                 
19274             }
19275         };
19276         var cal_rows = function() {
19277             
19278             var ret = [];
19279             for (var r = 0; r < 6; r++) {
19280                 var row= {
19281                     tag : 'tr',
19282                     cls : 'fc-week',
19283                     cn : []
19284                 };
19285                 
19286                 for (var i =0; i < Date.dayNames.length; i++) {
19287                     var d = Date.dayNames[i];
19288                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19289
19290                 }
19291                 row.cn[0].cls+=' fc-first';
19292                 row.cn[0].cn[0].style = 'min-height:90px';
19293                 row.cn[6].cls+=' fc-last';
19294                 ret.push(row);
19295                 
19296             }
19297             ret[0].cls += ' fc-first';
19298             ret[4].cls += ' fc-prev-last';
19299             ret[5].cls += ' fc-last';
19300             return ret;
19301             
19302         };
19303         
19304         var cal_table = {
19305             tag: 'table',
19306             cls: 'fc-border-separate',
19307             style : 'width:100%',
19308             cellspacing  : 0,
19309             cn : [
19310                 { 
19311                     tag: 'thead',
19312                     cn : [
19313                         { 
19314                             tag: 'tr',
19315                             cls : 'fc-first fc-last',
19316                             cn : cal_heads()
19317                         }
19318                     ]
19319                 },
19320                 { 
19321                     tag: 'tbody',
19322                     cn : cal_rows()
19323                 }
19324                   
19325             ]
19326         };
19327          
19328          var cfg = {
19329             cls : 'fc fc-ltr',
19330             cn : [
19331                 header,
19332                 {
19333                     cls : 'fc-content',
19334                     style : "position: relative;",
19335                     cn : [
19336                         {
19337                             cls : 'fc-view fc-view-month fc-grid',
19338                             style : 'position: relative',
19339                             unselectable : 'on',
19340                             cn : [
19341                                 {
19342                                     cls : 'fc-event-container',
19343                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19344                                 },
19345                                 cal_table
19346                             ]
19347                         }
19348                     ]
19349     
19350                 }
19351            ] 
19352             
19353         };
19354         
19355          
19356         
19357         return cfg;
19358     },
19359     
19360     
19361     initEvents : function()
19362     {
19363         if(!this.store){
19364             throw "can not find store for calendar";
19365         }
19366         
19367         var mark = {
19368             tag: "div",
19369             cls:"x-dlg-mask",
19370             style: "text-align:center",
19371             cn: [
19372                 {
19373                     tag: "div",
19374                     style: "background-color:white;width:50%;margin:250 auto",
19375                     cn: [
19376                         {
19377                             tag: "img",
19378                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19379                         },
19380                         {
19381                             tag: "span",
19382                             html: "Loading"
19383                         }
19384                         
19385                     ]
19386                 }
19387             ]
19388         };
19389         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19390         
19391         var size = this.el.select('.fc-content', true).first().getSize();
19392         this.maskEl.setSize(size.width, size.height);
19393         this.maskEl.enableDisplayMode("block");
19394         if(!this.loadMask){
19395             this.maskEl.hide();
19396         }
19397         
19398         this.store = Roo.factory(this.store, Roo.data);
19399         this.store.on('load', this.onLoad, this);
19400         this.store.on('beforeload', this.onBeforeLoad, this);
19401         
19402         this.resize();
19403         
19404         this.cells = this.el.select('.fc-day',true);
19405         //Roo.log(this.cells);
19406         this.textNodes = this.el.query('.fc-day-number');
19407         this.cells.addClassOnOver('fc-state-hover');
19408         
19409         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19410         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19411         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19412         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19413         
19414         this.on('monthchange', this.onMonthChange, this);
19415         
19416         this.update(new Date().clearTime());
19417     },
19418     
19419     resize : function() {
19420         var sz  = this.el.getSize();
19421         
19422         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19423         this.el.select('.fc-day-content div',true).setHeight(34);
19424     },
19425     
19426     
19427     // private
19428     showPrevMonth : function(e){
19429         this.update(this.activeDate.add("mo", -1));
19430     },
19431     showToday : function(e){
19432         this.update(new Date().clearTime());
19433     },
19434     // private
19435     showNextMonth : function(e){
19436         this.update(this.activeDate.add("mo", 1));
19437     },
19438
19439     // private
19440     showPrevYear : function(){
19441         this.update(this.activeDate.add("y", -1));
19442     },
19443
19444     // private
19445     showNextYear : function(){
19446         this.update(this.activeDate.add("y", 1));
19447     },
19448
19449     
19450    // private
19451     update : function(date)
19452     {
19453         var vd = this.activeDate;
19454         this.activeDate = date;
19455 //        if(vd && this.el){
19456 //            var t = date.getTime();
19457 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19458 //                Roo.log('using add remove');
19459 //                
19460 //                this.fireEvent('monthchange', this, date);
19461 //                
19462 //                this.cells.removeClass("fc-state-highlight");
19463 //                this.cells.each(function(c){
19464 //                   if(c.dateValue == t){
19465 //                       c.addClass("fc-state-highlight");
19466 //                       setTimeout(function(){
19467 //                            try{c.dom.firstChild.focus();}catch(e){}
19468 //                       }, 50);
19469 //                       return false;
19470 //                   }
19471 //                   return true;
19472 //                });
19473 //                return;
19474 //            }
19475 //        }
19476         
19477         var days = date.getDaysInMonth();
19478         
19479         var firstOfMonth = date.getFirstDateOfMonth();
19480         var startingPos = firstOfMonth.getDay()-this.startDay;
19481         
19482         if(startingPos < this.startDay){
19483             startingPos += 7;
19484         }
19485         
19486         var pm = date.add(Date.MONTH, -1);
19487         var prevStart = pm.getDaysInMonth()-startingPos;
19488 //        
19489         this.cells = this.el.select('.fc-day',true);
19490         this.textNodes = this.el.query('.fc-day-number');
19491         this.cells.addClassOnOver('fc-state-hover');
19492         
19493         var cells = this.cells.elements;
19494         var textEls = this.textNodes;
19495         
19496         Roo.each(cells, function(cell){
19497             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19498         });
19499         
19500         days += startingPos;
19501
19502         // convert everything to numbers so it's fast
19503         var day = 86400000;
19504         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19505         //Roo.log(d);
19506         //Roo.log(pm);
19507         //Roo.log(prevStart);
19508         
19509         var today = new Date().clearTime().getTime();
19510         var sel = date.clearTime().getTime();
19511         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19512         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19513         var ddMatch = this.disabledDatesRE;
19514         var ddText = this.disabledDatesText;
19515         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19516         var ddaysText = this.disabledDaysText;
19517         var format = this.format;
19518         
19519         var setCellClass = function(cal, cell){
19520             cell.row = 0;
19521             cell.events = [];
19522             cell.more = [];
19523             //Roo.log('set Cell Class');
19524             cell.title = "";
19525             var t = d.getTime();
19526             
19527             //Roo.log(d);
19528             
19529             cell.dateValue = t;
19530             if(t == today){
19531                 cell.className += " fc-today";
19532                 cell.className += " fc-state-highlight";
19533                 cell.title = cal.todayText;
19534             }
19535             if(t == sel){
19536                 // disable highlight in other month..
19537                 //cell.className += " fc-state-highlight";
19538                 
19539             }
19540             // disabling
19541             if(t < min) {
19542                 cell.className = " fc-state-disabled";
19543                 cell.title = cal.minText;
19544                 return;
19545             }
19546             if(t > max) {
19547                 cell.className = " fc-state-disabled";
19548                 cell.title = cal.maxText;
19549                 return;
19550             }
19551             if(ddays){
19552                 if(ddays.indexOf(d.getDay()) != -1){
19553                     cell.title = ddaysText;
19554                     cell.className = " fc-state-disabled";
19555                 }
19556             }
19557             if(ddMatch && format){
19558                 var fvalue = d.dateFormat(format);
19559                 if(ddMatch.test(fvalue)){
19560                     cell.title = ddText.replace("%0", fvalue);
19561                     cell.className = " fc-state-disabled";
19562                 }
19563             }
19564             
19565             if (!cell.initialClassName) {
19566                 cell.initialClassName = cell.dom.className;
19567             }
19568             
19569             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19570         };
19571
19572         var i = 0;
19573         
19574         for(; i < startingPos; i++) {
19575             textEls[i].innerHTML = (++prevStart);
19576             d.setDate(d.getDate()+1);
19577             
19578             cells[i].className = "fc-past fc-other-month";
19579             setCellClass(this, cells[i]);
19580         }
19581         
19582         var intDay = 0;
19583         
19584         for(; i < days; i++){
19585             intDay = i - startingPos + 1;
19586             textEls[i].innerHTML = (intDay);
19587             d.setDate(d.getDate()+1);
19588             
19589             cells[i].className = ''; // "x-date-active";
19590             setCellClass(this, cells[i]);
19591         }
19592         var extraDays = 0;
19593         
19594         for(; i < 42; i++) {
19595             textEls[i].innerHTML = (++extraDays);
19596             d.setDate(d.getDate()+1);
19597             
19598             cells[i].className = "fc-future fc-other-month";
19599             setCellClass(this, cells[i]);
19600         }
19601         
19602         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19603         
19604         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19605         
19606         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19607         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19608         
19609         if(totalRows != 6){
19610             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19611             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19612         }
19613         
19614         this.fireEvent('monthchange', this, date);
19615         
19616         
19617         /*
19618         if(!this.internalRender){
19619             var main = this.el.dom.firstChild;
19620             var w = main.offsetWidth;
19621             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19622             Roo.fly(main).setWidth(w);
19623             this.internalRender = true;
19624             // opera does not respect the auto grow header center column
19625             // then, after it gets a width opera refuses to recalculate
19626             // without a second pass
19627             if(Roo.isOpera && !this.secondPass){
19628                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19629                 this.secondPass = true;
19630                 this.update.defer(10, this, [date]);
19631             }
19632         }
19633         */
19634         
19635     },
19636     
19637     findCell : function(dt) {
19638         dt = dt.clearTime().getTime();
19639         var ret = false;
19640         this.cells.each(function(c){
19641             //Roo.log("check " +c.dateValue + '?=' + dt);
19642             if(c.dateValue == dt){
19643                 ret = c;
19644                 return false;
19645             }
19646             return true;
19647         });
19648         
19649         return ret;
19650     },
19651     
19652     findCells : function(ev) {
19653         var s = ev.start.clone().clearTime().getTime();
19654        // Roo.log(s);
19655         var e= ev.end.clone().clearTime().getTime();
19656        // Roo.log(e);
19657         var ret = [];
19658         this.cells.each(function(c){
19659              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19660             
19661             if(c.dateValue > e){
19662                 return ;
19663             }
19664             if(c.dateValue < s){
19665                 return ;
19666             }
19667             ret.push(c);
19668         });
19669         
19670         return ret;    
19671     },
19672     
19673 //    findBestRow: function(cells)
19674 //    {
19675 //        var ret = 0;
19676 //        
19677 //        for (var i =0 ; i < cells.length;i++) {
19678 //            ret  = Math.max(cells[i].rows || 0,ret);
19679 //        }
19680 //        return ret;
19681 //        
19682 //    },
19683     
19684     
19685     addItem : function(ev)
19686     {
19687         // look for vertical location slot in
19688         var cells = this.findCells(ev);
19689         
19690 //        ev.row = this.findBestRow(cells);
19691         
19692         // work out the location.
19693         
19694         var crow = false;
19695         var rows = [];
19696         for(var i =0; i < cells.length; i++) {
19697             
19698             cells[i].row = cells[0].row;
19699             
19700             if(i == 0){
19701                 cells[i].row = cells[i].row + 1;
19702             }
19703             
19704             if (!crow) {
19705                 crow = {
19706                     start : cells[i],
19707                     end :  cells[i]
19708                 };
19709                 continue;
19710             }
19711             if (crow.start.getY() == cells[i].getY()) {
19712                 // on same row.
19713                 crow.end = cells[i];
19714                 continue;
19715             }
19716             // different row.
19717             rows.push(crow);
19718             crow = {
19719                 start: cells[i],
19720                 end : cells[i]
19721             };
19722             
19723         }
19724         
19725         rows.push(crow);
19726         ev.els = [];
19727         ev.rows = rows;
19728         ev.cells = cells;
19729         
19730         cells[0].events.push(ev);
19731         
19732         this.calevents.push(ev);
19733     },
19734     
19735     clearEvents: function() {
19736         
19737         if(!this.calevents){
19738             return;
19739         }
19740         
19741         Roo.each(this.cells.elements, function(c){
19742             c.row = 0;
19743             c.events = [];
19744             c.more = [];
19745         });
19746         
19747         Roo.each(this.calevents, function(e) {
19748             Roo.each(e.els, function(el) {
19749                 el.un('mouseenter' ,this.onEventEnter, this);
19750                 el.un('mouseleave' ,this.onEventLeave, this);
19751                 el.remove();
19752             },this);
19753         },this);
19754         
19755         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19756             e.remove();
19757         });
19758         
19759     },
19760     
19761     renderEvents: function()
19762     {   
19763         var _this = this;
19764         
19765         this.cells.each(function(c) {
19766             
19767             if(c.row < 5){
19768                 return;
19769             }
19770             
19771             var ev = c.events;
19772             
19773             var r = 4;
19774             if(c.row != c.events.length){
19775                 r = 4 - (4 - (c.row - c.events.length));
19776             }
19777             
19778             c.events = ev.slice(0, r);
19779             c.more = ev.slice(r);
19780             
19781             if(c.more.length && c.more.length == 1){
19782                 c.events.push(c.more.pop());
19783             }
19784             
19785             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19786             
19787         });
19788             
19789         this.cells.each(function(c) {
19790             
19791             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19792             
19793             
19794             for (var e = 0; e < c.events.length; e++){
19795                 var ev = c.events[e];
19796                 var rows = ev.rows;
19797                 
19798                 for(var i = 0; i < rows.length; i++) {
19799                 
19800                     // how many rows should it span..
19801
19802                     var  cfg = {
19803                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19804                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19805
19806                         unselectable : "on",
19807                         cn : [
19808                             {
19809                                 cls: 'fc-event-inner',
19810                                 cn : [
19811     //                                {
19812     //                                  tag:'span',
19813     //                                  cls: 'fc-event-time',
19814     //                                  html : cells.length > 1 ? '' : ev.time
19815     //                                },
19816                                     {
19817                                       tag:'span',
19818                                       cls: 'fc-event-title',
19819                                       html : String.format('{0}', ev.title)
19820                                     }
19821
19822
19823                                 ]
19824                             },
19825                             {
19826                                 cls: 'ui-resizable-handle ui-resizable-e',
19827                                 html : '&nbsp;&nbsp;&nbsp'
19828                             }
19829
19830                         ]
19831                     };
19832
19833                     if (i == 0) {
19834                         cfg.cls += ' fc-event-start';
19835                     }
19836                     if ((i+1) == rows.length) {
19837                         cfg.cls += ' fc-event-end';
19838                     }
19839
19840                     var ctr = _this.el.select('.fc-event-container',true).first();
19841                     var cg = ctr.createChild(cfg);
19842
19843                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19844                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19845
19846                     var r = (c.more.length) ? 1 : 0;
19847                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19848                     cg.setWidth(ebox.right - sbox.x -2);
19849
19850                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19851                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19852                     cg.on('click', _this.onEventClick, _this, ev);
19853
19854                     ev.els.push(cg);
19855                     
19856                 }
19857                 
19858             }
19859             
19860             
19861             if(c.more.length){
19862                 var  cfg = {
19863                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19864                     style : 'position: absolute',
19865                     unselectable : "on",
19866                     cn : [
19867                         {
19868                             cls: 'fc-event-inner',
19869                             cn : [
19870                                 {
19871                                   tag:'span',
19872                                   cls: 'fc-event-title',
19873                                   html : 'More'
19874                                 }
19875
19876
19877                             ]
19878                         },
19879                         {
19880                             cls: 'ui-resizable-handle ui-resizable-e',
19881                             html : '&nbsp;&nbsp;&nbsp'
19882                         }
19883
19884                     ]
19885                 };
19886
19887                 var ctr = _this.el.select('.fc-event-container',true).first();
19888                 var cg = ctr.createChild(cfg);
19889
19890                 var sbox = c.select('.fc-day-content',true).first().getBox();
19891                 var ebox = c.select('.fc-day-content',true).first().getBox();
19892                 //Roo.log(cg);
19893                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19894                 cg.setWidth(ebox.right - sbox.x -2);
19895
19896                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19897                 
19898             }
19899             
19900         });
19901         
19902         
19903         
19904     },
19905     
19906     onEventEnter: function (e, el,event,d) {
19907         this.fireEvent('evententer', this, el, event);
19908     },
19909     
19910     onEventLeave: function (e, el,event,d) {
19911         this.fireEvent('eventleave', this, el, event);
19912     },
19913     
19914     onEventClick: function (e, el,event,d) {
19915         this.fireEvent('eventclick', this, el, event);
19916     },
19917     
19918     onMonthChange: function () {
19919         this.store.load();
19920     },
19921     
19922     onMoreEventClick: function(e, el, more)
19923     {
19924         var _this = this;
19925         
19926         this.calpopover.placement = 'right';
19927         this.calpopover.setTitle('More');
19928         
19929         this.calpopover.setContent('');
19930         
19931         var ctr = this.calpopover.el.select('.popover-content', true).first();
19932         
19933         Roo.each(more, function(m){
19934             var cfg = {
19935                 cls : 'fc-event-hori fc-event-draggable',
19936                 html : m.title
19937             };
19938             var cg = ctr.createChild(cfg);
19939             
19940             cg.on('click', _this.onEventClick, _this, m);
19941         });
19942         
19943         this.calpopover.show(el);
19944         
19945         
19946     },
19947     
19948     onLoad: function () 
19949     {   
19950         this.calevents = [];
19951         var cal = this;
19952         
19953         if(this.store.getCount() > 0){
19954             this.store.data.each(function(d){
19955                cal.addItem({
19956                     id : d.data.id,
19957                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19958                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19959                     time : d.data.start_time,
19960                     title : d.data.title,
19961                     description : d.data.description,
19962                     venue : d.data.venue
19963                 });
19964             });
19965         }
19966         
19967         this.renderEvents();
19968         
19969         if(this.calevents.length && this.loadMask){
19970             this.maskEl.hide();
19971         }
19972     },
19973     
19974     onBeforeLoad: function()
19975     {
19976         this.clearEvents();
19977         if(this.loadMask){
19978             this.maskEl.show();
19979         }
19980     }
19981 });
19982
19983  
19984  /*
19985  * - LGPL
19986  *
19987  * element
19988  * 
19989  */
19990
19991 /**
19992  * @class Roo.bootstrap.Popover
19993  * @extends Roo.bootstrap.Component
19994  * Bootstrap Popover class
19995  * @cfg {String} html contents of the popover   (or false to use children..)
19996  * @cfg {String} title of popover (or false to hide)
19997  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19998  * @cfg {String} trigger click || hover (or false to trigger manually)
19999  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20000  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20001  *      - if false and it has a 'parent' then it will be automatically added to that element
20002  *      - if string - Roo.get  will be called 
20003  * @cfg {Number} delay - delay before showing
20004  
20005  * @constructor
20006  * Create a new Popover
20007  * @param {Object} config The config object
20008  */
20009
20010 Roo.bootstrap.Popover = function(config){
20011     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20012     
20013     this.addEvents({
20014         // raw events
20015          /**
20016          * @event show
20017          * After the popover show
20018          * 
20019          * @param {Roo.bootstrap.Popover} this
20020          */
20021         "show" : true,
20022         /**
20023          * @event hide
20024          * After the popover hide
20025          * 
20026          * @param {Roo.bootstrap.Popover} this
20027          */
20028         "hide" : true
20029     });
20030 };
20031
20032 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20033     
20034     title: false,
20035     html: false,
20036     
20037     placement : 'right',
20038     trigger : 'hover', // hover
20039     modal : false,
20040     delay : 0,
20041     
20042     over: false,
20043     
20044     can_build_overlaid : false,
20045     
20046     maskEl : false, // the mask element
20047     headerEl : false,
20048     contentEl : false,
20049     alignEl : false, // when show is called with an element - this get's stored.
20050     
20051     getChildContainer : function()
20052     {
20053         return this.contentEl;
20054         
20055     },
20056     getPopoverHeader : function()
20057     {
20058         this.title = true; // flag not to hide it..
20059         this.headerEl.addClass('p-0');
20060         return this.headerEl
20061     },
20062     
20063     
20064     getAutoCreate : function(){
20065          
20066         var cfg = {
20067            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20068            style: 'display:block',
20069            cn : [
20070                 {
20071                     cls : 'arrow'
20072                 },
20073                 {
20074                     cls : 'popover-inner ',
20075                     cn : [
20076                         {
20077                             tag: 'h3',
20078                             cls: 'popover-title popover-header',
20079                             html : this.title === false ? '' : this.title
20080                         },
20081                         {
20082                             cls : 'popover-content popover-body '  + (this.cls || ''),
20083                             html : this.html || ''
20084                         }
20085                     ]
20086                     
20087                 }
20088            ]
20089         };
20090         
20091         return cfg;
20092     },
20093     /**
20094      * @param {string} the title
20095      */
20096     setTitle: function(str)
20097     {
20098         this.title = str;
20099         if (this.el) {
20100             this.headerEl.dom.innerHTML = str;
20101         }
20102         
20103     },
20104     /**
20105      * @param {string} the body content
20106      */
20107     setContent: function(str)
20108     {
20109         this.html = str;
20110         if (this.contentEl) {
20111             this.contentEl.dom.innerHTML = str;
20112         }
20113         
20114     },
20115     // as it get's added to the bottom of the page.
20116     onRender : function(ct, position)
20117     {
20118         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20119         
20120         
20121         
20122         if(!this.el){
20123             var cfg = Roo.apply({},  this.getAutoCreate());
20124             cfg.id = Roo.id();
20125             
20126             if (this.cls) {
20127                 cfg.cls += ' ' + this.cls;
20128             }
20129             if (this.style) {
20130                 cfg.style = this.style;
20131             }
20132             //Roo.log("adding to ");
20133             this.el = Roo.get(document.body).createChild(cfg, position);
20134 //            Roo.log(this.el);
20135         }
20136         
20137         this.contentEl = this.el.select('.popover-content',true).first();
20138         this.headerEl =  this.el.select('.popover-title',true).first();
20139         
20140         var nitems = [];
20141         if(typeof(this.items) != 'undefined'){
20142             var items = this.items;
20143             delete this.items;
20144
20145             for(var i =0;i < items.length;i++) {
20146                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20147             }
20148         }
20149
20150         this.items = nitems;
20151         
20152         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20153         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20154         
20155         
20156         
20157         this.initEvents();
20158     },
20159     
20160     resizeMask : function()
20161     {
20162         this.maskEl.setSize(
20163             Roo.lib.Dom.getViewWidth(true),
20164             Roo.lib.Dom.getViewHeight(true)
20165         );
20166     },
20167     
20168     initEvents : function()
20169     {
20170         
20171         if (!this.modal) { 
20172             Roo.bootstrap.Popover.register(this);
20173         }
20174          
20175         this.arrowEl = this.el.select('.arrow',true).first();
20176         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20177         this.el.enableDisplayMode('block');
20178         this.el.hide();
20179  
20180         
20181         if (this.over === false && !this.parent()) {
20182             return; 
20183         }
20184         if (this.triggers === false) {
20185             return;
20186         }
20187          
20188         // support parent
20189         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20190         var triggers = this.trigger ? this.trigger.split(' ') : [];
20191         Roo.each(triggers, function(trigger) {
20192         
20193             if (trigger == 'click') {
20194                 on_el.on('click', this.toggle, this);
20195             } else if (trigger != 'manual') {
20196                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20197                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20198       
20199                 on_el.on(eventIn  ,this.enter, this);
20200                 on_el.on(eventOut, this.leave, this);
20201             }
20202         }, this);
20203     },
20204     
20205     
20206     // private
20207     timeout : null,
20208     hoverState : null,
20209     
20210     toggle : function () {
20211         this.hoverState == 'in' ? this.leave() : this.enter();
20212     },
20213     
20214     enter : function () {
20215         
20216         clearTimeout(this.timeout);
20217     
20218         this.hoverState = 'in';
20219     
20220         if (!this.delay || !this.delay.show) {
20221             this.show();
20222             return;
20223         }
20224         var _t = this;
20225         this.timeout = setTimeout(function () {
20226             if (_t.hoverState == 'in') {
20227                 _t.show();
20228             }
20229         }, this.delay.show)
20230     },
20231     
20232     leave : function() {
20233         clearTimeout(this.timeout);
20234     
20235         this.hoverState = 'out';
20236     
20237         if (!this.delay || !this.delay.hide) {
20238             this.hide();
20239             return;
20240         }
20241         var _t = this;
20242         this.timeout = setTimeout(function () {
20243             if (_t.hoverState == 'out') {
20244                 _t.hide();
20245             }
20246         }, this.delay.hide)
20247     },
20248     /**
20249      * Show the popover
20250      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20251      * @param {string} (left|right|top|bottom) position
20252      */
20253     show : function (on_el, placement)
20254     {
20255         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20256         on_el = on_el || false; // default to false
20257          
20258         if (!on_el) {
20259             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20260                 on_el = this.parent().el;
20261             } else if (this.over) {
20262                 on_el = Roo.get(this.over);
20263             }
20264             
20265         }
20266         
20267         this.alignEl = Roo.get( on_el );
20268
20269         if (!this.el) {
20270             this.render(document.body);
20271         }
20272         
20273         
20274          
20275         
20276         if (this.title === false) {
20277             this.headerEl.hide();
20278         }
20279         
20280        
20281         this.el.show();
20282         this.el.dom.style.display = 'block';
20283          
20284  
20285         if (this.alignEl) {
20286             this.updatePosition(this.placement, true);
20287              
20288         } else {
20289             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20290             var es = this.el.getSize();
20291             var x = Roo.lib.Dom.getViewWidth()/2;
20292             var y = Roo.lib.Dom.getViewHeight()/2;
20293             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20294             
20295         }
20296
20297         
20298         //var arrow = this.el.select('.arrow',true).first();
20299         //arrow.set(align[2], 
20300         
20301         this.el.addClass('in');
20302         
20303          
20304         
20305         this.hoverState = 'in';
20306         
20307         if (this.modal) {
20308             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20309             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20310             this.maskEl.dom.style.display = 'block';
20311             this.maskEl.addClass('show');
20312         }
20313         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20314  
20315         this.fireEvent('show', this);
20316         
20317     },
20318     /**
20319      * fire this manually after loading a grid in the table for example
20320      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20321      * @param {Boolean} try and move it if we cant get right position.
20322      */
20323     updatePosition : function(placement, try_move)
20324     {
20325         // allow for calling with no parameters
20326         placement = placement   ? placement :  this.placement;
20327         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20328         
20329         this.el.removeClass([
20330             'fade','top','bottom', 'left', 'right','in',
20331             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20332         ]);
20333         this.el.addClass(placement + ' bs-popover-' + placement);
20334         
20335         if (!this.alignEl ) {
20336             return false;
20337         }
20338         
20339         switch (placement) {
20340             case 'right':
20341                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20342                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20343                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20344                     //normal display... or moved up/down.
20345                     this.el.setXY(offset);
20346                     var xy = this.alignEl.getAnchorXY('tr', false);
20347                     xy[0]+=2;xy[1]+=5;
20348                     this.arrowEl.setXY(xy);
20349                     return true;
20350                 }
20351                 // continue through...
20352                 return this.updatePosition('left', false);
20353                 
20354             
20355             case 'left':
20356                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20357                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20358                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20359                     //normal display... or moved up/down.
20360                     this.el.setXY(offset);
20361                     var xy = this.alignEl.getAnchorXY('tl', false);
20362                     xy[0]-=10;xy[1]+=5; // << fix me
20363                     this.arrowEl.setXY(xy);
20364                     return true;
20365                 }
20366                 // call self...
20367                 return this.updatePosition('right', false);
20368             
20369             case 'top':
20370                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20371                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20372                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20373                     //normal display... or moved up/down.
20374                     this.el.setXY(offset);
20375                     var xy = this.alignEl.getAnchorXY('t', false);
20376                     xy[1]-=10; // << fix me
20377                     this.arrowEl.setXY(xy);
20378                     return true;
20379                 }
20380                 // fall through
20381                return this.updatePosition('bottom', false);
20382             
20383             case 'bottom':
20384                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20385                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20386                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20387                     //normal display... or moved up/down.
20388                     this.el.setXY(offset);
20389                     var xy = this.alignEl.getAnchorXY('b', false);
20390                      xy[1]+=2; // << fix me
20391                     this.arrowEl.setXY(xy);
20392                     return true;
20393                 }
20394                 // fall through
20395                 return this.updatePosition('top', false);
20396                 
20397             
20398         }
20399         
20400         
20401         return false;
20402     },
20403     
20404     hide : function()
20405     {
20406         this.el.setXY([0,0]);
20407         this.el.removeClass('in');
20408         this.el.hide();
20409         this.hoverState = null;
20410         this.maskEl.hide(); // always..
20411         this.fireEvent('hide', this);
20412     }
20413     
20414 });
20415
20416
20417 Roo.apply(Roo.bootstrap.Popover, {
20418
20419     alignment : {
20420         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20421         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20422         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20423         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20424     },
20425     
20426     zIndex : 20001,
20427
20428     clickHander : false,
20429     
20430     
20431
20432     onMouseDown : function(e)
20433     {
20434         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20435             /// what is nothing is showing..
20436             this.hideAll();
20437         }
20438          
20439     },
20440     
20441     
20442     popups : [],
20443     
20444     register : function(popup)
20445     {
20446         if (!Roo.bootstrap.Popover.clickHandler) {
20447             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20448         }
20449         // hide other popups.
20450         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20451         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20452         this.hideAll(); //<< why?
20453         //this.popups.push(popup);
20454     },
20455     hideAll : function()
20456     {
20457         this.popups.forEach(function(p) {
20458             p.hide();
20459         });
20460     },
20461     onShow : function() {
20462         Roo.bootstrap.Popover.popups.push(this);
20463     },
20464     onHide : function() {
20465         Roo.bootstrap.Popover.popups.remove(this);
20466     } 
20467
20468 });/*
20469  * - LGPL
20470  *
20471  * Card header - holder for the card header elements.
20472  * 
20473  */
20474
20475 /**
20476  * @class Roo.bootstrap.PopoverNav
20477  * @extends Roo.bootstrap.NavGroup
20478  * Bootstrap Popover header navigation class
20479  * @constructor
20480  * Create a new Popover Header Navigation 
20481  * @param {Object} config The config object
20482  */
20483
20484 Roo.bootstrap.PopoverNav = function(config){
20485     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20486 };
20487
20488 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20489     
20490     
20491     container_method : 'getPopoverHeader' 
20492     
20493      
20494     
20495     
20496    
20497 });
20498
20499  
20500
20501  /*
20502  * - LGPL
20503  *
20504  * Progress
20505  * 
20506  */
20507
20508 /**
20509  * @class Roo.bootstrap.Progress
20510  * @extends Roo.bootstrap.Component
20511  * Bootstrap Progress class
20512  * @cfg {Boolean} striped striped of the progress bar
20513  * @cfg {Boolean} active animated of the progress bar
20514  * 
20515  * 
20516  * @constructor
20517  * Create a new Progress
20518  * @param {Object} config The config object
20519  */
20520
20521 Roo.bootstrap.Progress = function(config){
20522     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20523 };
20524
20525 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20526     
20527     striped : false,
20528     active: false,
20529     
20530     getAutoCreate : function(){
20531         var cfg = {
20532             tag: 'div',
20533             cls: 'progress'
20534         };
20535         
20536         
20537         if(this.striped){
20538             cfg.cls += ' progress-striped';
20539         }
20540       
20541         if(this.active){
20542             cfg.cls += ' active';
20543         }
20544         
20545         
20546         return cfg;
20547     }
20548    
20549 });
20550
20551  
20552
20553  /*
20554  * - LGPL
20555  *
20556  * ProgressBar
20557  * 
20558  */
20559
20560 /**
20561  * @class Roo.bootstrap.ProgressBar
20562  * @extends Roo.bootstrap.Component
20563  * Bootstrap ProgressBar class
20564  * @cfg {Number} aria_valuenow aria-value now
20565  * @cfg {Number} aria_valuemin aria-value min
20566  * @cfg {Number} aria_valuemax aria-value max
20567  * @cfg {String} label label for the progress bar
20568  * @cfg {String} panel (success | info | warning | danger )
20569  * @cfg {String} role role of the progress bar
20570  * @cfg {String} sr_only text
20571  * 
20572  * 
20573  * @constructor
20574  * Create a new ProgressBar
20575  * @param {Object} config The config object
20576  */
20577
20578 Roo.bootstrap.ProgressBar = function(config){
20579     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20580 };
20581
20582 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20583     
20584     aria_valuenow : 0,
20585     aria_valuemin : 0,
20586     aria_valuemax : 100,
20587     label : false,
20588     panel : false,
20589     role : false,
20590     sr_only: false,
20591     
20592     getAutoCreate : function()
20593     {
20594         
20595         var cfg = {
20596             tag: 'div',
20597             cls: 'progress-bar',
20598             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20599         };
20600         
20601         if(this.sr_only){
20602             cfg.cn = {
20603                 tag: 'span',
20604                 cls: 'sr-only',
20605                 html: this.sr_only
20606             }
20607         }
20608         
20609         if(this.role){
20610             cfg.role = this.role;
20611         }
20612         
20613         if(this.aria_valuenow){
20614             cfg['aria-valuenow'] = this.aria_valuenow;
20615         }
20616         
20617         if(this.aria_valuemin){
20618             cfg['aria-valuemin'] = this.aria_valuemin;
20619         }
20620         
20621         if(this.aria_valuemax){
20622             cfg['aria-valuemax'] = this.aria_valuemax;
20623         }
20624         
20625         if(this.label && !this.sr_only){
20626             cfg.html = this.label;
20627         }
20628         
20629         if(this.panel){
20630             cfg.cls += ' progress-bar-' + this.panel;
20631         }
20632         
20633         return cfg;
20634     },
20635     
20636     update : function(aria_valuenow)
20637     {
20638         this.aria_valuenow = aria_valuenow;
20639         
20640         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20641     }
20642    
20643 });
20644
20645  
20646
20647  /*
20648  * - LGPL
20649  *
20650  * column
20651  * 
20652  */
20653
20654 /**
20655  * @class Roo.bootstrap.TabGroup
20656  * @extends Roo.bootstrap.Column
20657  * Bootstrap Column class
20658  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20659  * @cfg {Boolean} carousel true to make the group behave like a carousel
20660  * @cfg {Boolean} bullets show bullets for the panels
20661  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20662  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20663  * @cfg {Boolean} showarrow (true|false) show arrow default true
20664  * 
20665  * @constructor
20666  * Create a new TabGroup
20667  * @param {Object} config The config object
20668  */
20669
20670 Roo.bootstrap.TabGroup = function(config){
20671     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20672     if (!this.navId) {
20673         this.navId = Roo.id();
20674     }
20675     this.tabs = [];
20676     Roo.bootstrap.TabGroup.register(this);
20677     
20678 };
20679
20680 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20681     
20682     carousel : false,
20683     transition : false,
20684     bullets : 0,
20685     timer : 0,
20686     autoslide : false,
20687     slideFn : false,
20688     slideOnTouch : false,
20689     showarrow : true,
20690     
20691     getAutoCreate : function()
20692     {
20693         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20694         
20695         cfg.cls += ' tab-content';
20696         
20697         if (this.carousel) {
20698             cfg.cls += ' carousel slide';
20699             
20700             cfg.cn = [{
20701                cls : 'carousel-inner',
20702                cn : []
20703             }];
20704         
20705             if(this.bullets  && !Roo.isTouch){
20706                 
20707                 var bullets = {
20708                     cls : 'carousel-bullets',
20709                     cn : []
20710                 };
20711                
20712                 if(this.bullets_cls){
20713                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20714                 }
20715                 
20716                 bullets.cn.push({
20717                     cls : 'clear'
20718                 });
20719                 
20720                 cfg.cn[0].cn.push(bullets);
20721             }
20722             
20723             if(this.showarrow){
20724                 cfg.cn[0].cn.push({
20725                     tag : 'div',
20726                     class : 'carousel-arrow',
20727                     cn : [
20728                         {
20729                             tag : 'div',
20730                             class : 'carousel-prev',
20731                             cn : [
20732                                 {
20733                                     tag : 'i',
20734                                     class : 'fa fa-chevron-left'
20735                                 }
20736                             ]
20737                         },
20738                         {
20739                             tag : 'div',
20740                             class : 'carousel-next',
20741                             cn : [
20742                                 {
20743                                     tag : 'i',
20744                                     class : 'fa fa-chevron-right'
20745                                 }
20746                             ]
20747                         }
20748                     ]
20749                 });
20750             }
20751             
20752         }
20753         
20754         return cfg;
20755     },
20756     
20757     initEvents:  function()
20758     {
20759 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20760 //            this.el.on("touchstart", this.onTouchStart, this);
20761 //        }
20762         
20763         if(this.autoslide){
20764             var _this = this;
20765             
20766             this.slideFn = window.setInterval(function() {
20767                 _this.showPanelNext();
20768             }, this.timer);
20769         }
20770         
20771         if(this.showarrow){
20772             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20773             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20774         }
20775         
20776         
20777     },
20778     
20779 //    onTouchStart : function(e, el, o)
20780 //    {
20781 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20782 //            return;
20783 //        }
20784 //        
20785 //        this.showPanelNext();
20786 //    },
20787     
20788     
20789     getChildContainer : function()
20790     {
20791         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20792     },
20793     
20794     /**
20795     * register a Navigation item
20796     * @param {Roo.bootstrap.NavItem} the navitem to add
20797     */
20798     register : function(item)
20799     {
20800         this.tabs.push( item);
20801         item.navId = this.navId; // not really needed..
20802         this.addBullet();
20803     
20804     },
20805     
20806     getActivePanel : function()
20807     {
20808         var r = false;
20809         Roo.each(this.tabs, function(t) {
20810             if (t.active) {
20811                 r = t;
20812                 return false;
20813             }
20814             return null;
20815         });
20816         return r;
20817         
20818     },
20819     getPanelByName : function(n)
20820     {
20821         var r = false;
20822         Roo.each(this.tabs, function(t) {
20823             if (t.tabId == n) {
20824                 r = t;
20825                 return false;
20826             }
20827             return null;
20828         });
20829         return r;
20830     },
20831     indexOfPanel : function(p)
20832     {
20833         var r = false;
20834         Roo.each(this.tabs, function(t,i) {
20835             if (t.tabId == p.tabId) {
20836                 r = i;
20837                 return false;
20838             }
20839             return null;
20840         });
20841         return r;
20842     },
20843     /**
20844      * show a specific panel
20845      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20846      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20847      */
20848     showPanel : function (pan)
20849     {
20850         if(this.transition || typeof(pan) == 'undefined'){
20851             Roo.log("waiting for the transitionend");
20852             return false;
20853         }
20854         
20855         if (typeof(pan) == 'number') {
20856             pan = this.tabs[pan];
20857         }
20858         
20859         if (typeof(pan) == 'string') {
20860             pan = this.getPanelByName(pan);
20861         }
20862         
20863         var cur = this.getActivePanel();
20864         
20865         if(!pan || !cur){
20866             Roo.log('pan or acitve pan is undefined');
20867             return false;
20868         }
20869         
20870         if (pan.tabId == this.getActivePanel().tabId) {
20871             return true;
20872         }
20873         
20874         if (false === cur.fireEvent('beforedeactivate')) {
20875             return false;
20876         }
20877         
20878         if(this.bullets > 0 && !Roo.isTouch){
20879             this.setActiveBullet(this.indexOfPanel(pan));
20880         }
20881         
20882         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20883             
20884             //class="carousel-item carousel-item-next carousel-item-left"
20885             
20886             this.transition = true;
20887             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20888             var lr = dir == 'next' ? 'left' : 'right';
20889             pan.el.addClass(dir); // or prev
20890             pan.el.addClass('carousel-item-' + dir); // or prev
20891             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20892             cur.el.addClass(lr); // or right
20893             pan.el.addClass(lr);
20894             cur.el.addClass('carousel-item-' +lr); // or right
20895             pan.el.addClass('carousel-item-' +lr);
20896             
20897             
20898             var _this = this;
20899             cur.el.on('transitionend', function() {
20900                 Roo.log("trans end?");
20901                 
20902                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20903                 pan.setActive(true);
20904                 
20905                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20906                 cur.setActive(false);
20907                 
20908                 _this.transition = false;
20909                 
20910             }, this, { single:  true } );
20911             
20912             return true;
20913         }
20914         
20915         cur.setActive(false);
20916         pan.setActive(true);
20917         
20918         return true;
20919         
20920     },
20921     showPanelNext : function()
20922     {
20923         var i = this.indexOfPanel(this.getActivePanel());
20924         
20925         if (i >= this.tabs.length - 1 && !this.autoslide) {
20926             return;
20927         }
20928         
20929         if (i >= this.tabs.length - 1 && this.autoslide) {
20930             i = -1;
20931         }
20932         
20933         this.showPanel(this.tabs[i+1]);
20934     },
20935     
20936     showPanelPrev : function()
20937     {
20938         var i = this.indexOfPanel(this.getActivePanel());
20939         
20940         if (i  < 1 && !this.autoslide) {
20941             return;
20942         }
20943         
20944         if (i < 1 && this.autoslide) {
20945             i = this.tabs.length;
20946         }
20947         
20948         this.showPanel(this.tabs[i-1]);
20949     },
20950     
20951     
20952     addBullet: function()
20953     {
20954         if(!this.bullets || Roo.isTouch){
20955             return;
20956         }
20957         var ctr = this.el.select('.carousel-bullets',true).first();
20958         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20959         var bullet = ctr.createChild({
20960             cls : 'bullet bullet-' + i
20961         },ctr.dom.lastChild);
20962         
20963         
20964         var _this = this;
20965         
20966         bullet.on('click', (function(e, el, o, ii, t){
20967
20968             e.preventDefault();
20969
20970             this.showPanel(ii);
20971
20972             if(this.autoslide && this.slideFn){
20973                 clearInterval(this.slideFn);
20974                 this.slideFn = window.setInterval(function() {
20975                     _this.showPanelNext();
20976                 }, this.timer);
20977             }
20978
20979         }).createDelegate(this, [i, bullet], true));
20980                 
20981         
20982     },
20983      
20984     setActiveBullet : function(i)
20985     {
20986         if(Roo.isTouch){
20987             return;
20988         }
20989         
20990         Roo.each(this.el.select('.bullet', true).elements, function(el){
20991             el.removeClass('selected');
20992         });
20993
20994         var bullet = this.el.select('.bullet-' + i, true).first();
20995         
20996         if(!bullet){
20997             return;
20998         }
20999         
21000         bullet.addClass('selected');
21001     }
21002     
21003     
21004   
21005 });
21006
21007  
21008
21009  
21010  
21011 Roo.apply(Roo.bootstrap.TabGroup, {
21012     
21013     groups: {},
21014      /**
21015     * register a Navigation Group
21016     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21017     */
21018     register : function(navgrp)
21019     {
21020         this.groups[navgrp.navId] = navgrp;
21021         
21022     },
21023     /**
21024     * fetch a Navigation Group based on the navigation ID
21025     * if one does not exist , it will get created.
21026     * @param {string} the navgroup to add
21027     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21028     */
21029     get: function(navId) {
21030         if (typeof(this.groups[navId]) == 'undefined') {
21031             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21032         }
21033         return this.groups[navId] ;
21034     }
21035     
21036     
21037     
21038 });
21039
21040  /*
21041  * - LGPL
21042  *
21043  * TabPanel
21044  * 
21045  */
21046
21047 /**
21048  * @class Roo.bootstrap.TabPanel
21049  * @extends Roo.bootstrap.Component
21050  * Bootstrap TabPanel class
21051  * @cfg {Boolean} active panel active
21052  * @cfg {String} html panel content
21053  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21054  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21055  * @cfg {String} href click to link..
21056  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21057  * 
21058  * 
21059  * @constructor
21060  * Create a new TabPanel
21061  * @param {Object} config The config object
21062  */
21063
21064 Roo.bootstrap.TabPanel = function(config){
21065     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21066     this.addEvents({
21067         /**
21068              * @event changed
21069              * Fires when the active status changes
21070              * @param {Roo.bootstrap.TabPanel} this
21071              * @param {Boolean} state the new state
21072             
21073          */
21074         'changed': true,
21075         /**
21076              * @event beforedeactivate
21077              * Fires before a tab is de-activated - can be used to do validation on a form.
21078              * @param {Roo.bootstrap.TabPanel} this
21079              * @return {Boolean} false if there is an error
21080             
21081          */
21082         'beforedeactivate': true
21083      });
21084     
21085     this.tabId = this.tabId || Roo.id();
21086   
21087 };
21088
21089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21090     
21091     active: false,
21092     html: false,
21093     tabId: false,
21094     navId : false,
21095     href : '',
21096     touchSlide : false,
21097     getAutoCreate : function(){
21098         
21099         
21100         var cfg = {
21101             tag: 'div',
21102             // item is needed for carousel - not sure if it has any effect otherwise
21103             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21104             html: this.html || ''
21105         };
21106         
21107         if(this.active){
21108             cfg.cls += ' active';
21109         }
21110         
21111         if(this.tabId){
21112             cfg.tabId = this.tabId;
21113         }
21114         
21115         
21116         
21117         return cfg;
21118     },
21119     
21120     initEvents:  function()
21121     {
21122         var p = this.parent();
21123         
21124         this.navId = this.navId || p.navId;
21125         
21126         if (typeof(this.navId) != 'undefined') {
21127             // not really needed.. but just in case.. parent should be a NavGroup.
21128             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21129             
21130             tg.register(this);
21131             
21132             var i = tg.tabs.length - 1;
21133             
21134             if(this.active && tg.bullets > 0 && i < tg.bullets){
21135                 tg.setActiveBullet(i);
21136             }
21137         }
21138         
21139         this.el.on('click', this.onClick, this);
21140         
21141         if(Roo.isTouch && this.touchSlide){
21142             this.el.on("touchstart", this.onTouchStart, this);
21143             this.el.on("touchmove", this.onTouchMove, this);
21144             this.el.on("touchend", this.onTouchEnd, this);
21145         }
21146         
21147     },
21148     
21149     onRender : function(ct, position)
21150     {
21151         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21152     },
21153     
21154     setActive : function(state)
21155     {
21156         Roo.log("panel - set active " + this.tabId + "=" + state);
21157         
21158         this.active = state;
21159         if (!state) {
21160             this.el.removeClass('active');
21161             
21162         } else  if (!this.el.hasClass('active')) {
21163             this.el.addClass('active');
21164         }
21165         
21166         this.fireEvent('changed', this, state);
21167     },
21168     
21169     onClick : function(e)
21170     {
21171         e.preventDefault();
21172         
21173         if(!this.href.length){
21174             return;
21175         }
21176         
21177         window.location.href = this.href;
21178     },
21179     
21180     startX : 0,
21181     startY : 0,
21182     endX : 0,
21183     endY : 0,
21184     swiping : false,
21185     
21186     onTouchStart : function(e)
21187     {
21188         this.swiping = false;
21189         
21190         this.startX = e.browserEvent.touches[0].clientX;
21191         this.startY = e.browserEvent.touches[0].clientY;
21192     },
21193     
21194     onTouchMove : function(e)
21195     {
21196         this.swiping = true;
21197         
21198         this.endX = e.browserEvent.touches[0].clientX;
21199         this.endY = e.browserEvent.touches[0].clientY;
21200     },
21201     
21202     onTouchEnd : function(e)
21203     {
21204         if(!this.swiping){
21205             this.onClick(e);
21206             return;
21207         }
21208         
21209         var tabGroup = this.parent();
21210         
21211         if(this.endX > this.startX){ // swiping right
21212             tabGroup.showPanelPrev();
21213             return;
21214         }
21215         
21216         if(this.startX > this.endX){ // swiping left
21217             tabGroup.showPanelNext();
21218             return;
21219         }
21220     }
21221     
21222     
21223 });
21224  
21225
21226  
21227
21228  /*
21229  * - LGPL
21230  *
21231  * DateField
21232  * 
21233  */
21234
21235 /**
21236  * @class Roo.bootstrap.DateField
21237  * @extends Roo.bootstrap.Input
21238  * Bootstrap DateField class
21239  * @cfg {Number} weekStart default 0
21240  * @cfg {String} viewMode default empty, (months|years)
21241  * @cfg {String} minViewMode default empty, (months|years)
21242  * @cfg {Number} startDate default -Infinity
21243  * @cfg {Number} endDate default Infinity
21244  * @cfg {Boolean} todayHighlight default false
21245  * @cfg {Boolean} todayBtn default false
21246  * @cfg {Boolean} calendarWeeks default false
21247  * @cfg {Object} daysOfWeekDisabled default empty
21248  * @cfg {Boolean} singleMode default false (true | false)
21249  * 
21250  * @cfg {Boolean} keyboardNavigation default true
21251  * @cfg {String} language default en
21252  * 
21253  * @constructor
21254  * Create a new DateField
21255  * @param {Object} config The config object
21256  */
21257
21258 Roo.bootstrap.DateField = function(config){
21259     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21260      this.addEvents({
21261             /**
21262              * @event show
21263              * Fires when this field show.
21264              * @param {Roo.bootstrap.DateField} this
21265              * @param {Mixed} date The date value
21266              */
21267             show : true,
21268             /**
21269              * @event show
21270              * Fires when this field hide.
21271              * @param {Roo.bootstrap.DateField} this
21272              * @param {Mixed} date The date value
21273              */
21274             hide : true,
21275             /**
21276              * @event select
21277              * Fires when select a date.
21278              * @param {Roo.bootstrap.DateField} this
21279              * @param {Mixed} date The date value
21280              */
21281             select : true,
21282             /**
21283              * @event beforeselect
21284              * Fires when before select a date.
21285              * @param {Roo.bootstrap.DateField} this
21286              * @param {Mixed} date The date value
21287              */
21288             beforeselect : true
21289         });
21290 };
21291
21292 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21293     
21294     /**
21295      * @cfg {String} format
21296      * The default date format string which can be overriden for localization support.  The format must be
21297      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21298      */
21299     format : "m/d/y",
21300     /**
21301      * @cfg {String} altFormats
21302      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21303      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21304      */
21305     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21306     
21307     weekStart : 0,
21308     
21309     viewMode : '',
21310     
21311     minViewMode : '',
21312     
21313     todayHighlight : false,
21314     
21315     todayBtn: false,
21316     
21317     language: 'en',
21318     
21319     keyboardNavigation: true,
21320     
21321     calendarWeeks: false,
21322     
21323     startDate: -Infinity,
21324     
21325     endDate: Infinity,
21326     
21327     daysOfWeekDisabled: [],
21328     
21329     _events: [],
21330     
21331     singleMode : false,
21332     
21333     UTCDate: function()
21334     {
21335         return new Date(Date.UTC.apply(Date, arguments));
21336     },
21337     
21338     UTCToday: function()
21339     {
21340         var today = new Date();
21341         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21342     },
21343     
21344     getDate: function() {
21345             var d = this.getUTCDate();
21346             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21347     },
21348     
21349     getUTCDate: function() {
21350             return this.date;
21351     },
21352     
21353     setDate: function(d) {
21354             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21355     },
21356     
21357     setUTCDate: function(d) {
21358             this.date = d;
21359             this.setValue(this.formatDate(this.date));
21360     },
21361         
21362     onRender: function(ct, position)
21363     {
21364         
21365         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21366         
21367         this.language = this.language || 'en';
21368         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21369         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21370         
21371         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21372         this.format = this.format || 'm/d/y';
21373         this.isInline = false;
21374         this.isInput = true;
21375         this.component = this.el.select('.add-on', true).first() || false;
21376         this.component = (this.component && this.component.length === 0) ? false : this.component;
21377         this.hasInput = this.component && this.inputEl().length;
21378         
21379         if (typeof(this.minViewMode === 'string')) {
21380             switch (this.minViewMode) {
21381                 case 'months':
21382                     this.minViewMode = 1;
21383                     break;
21384                 case 'years':
21385                     this.minViewMode = 2;
21386                     break;
21387                 default:
21388                     this.minViewMode = 0;
21389                     break;
21390             }
21391         }
21392         
21393         if (typeof(this.viewMode === 'string')) {
21394             switch (this.viewMode) {
21395                 case 'months':
21396                     this.viewMode = 1;
21397                     break;
21398                 case 'years':
21399                     this.viewMode = 2;
21400                     break;
21401                 default:
21402                     this.viewMode = 0;
21403                     break;
21404             }
21405         }
21406                 
21407         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21408         
21409 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21410         
21411         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21412         
21413         this.picker().on('mousedown', this.onMousedown, this);
21414         this.picker().on('click', this.onClick, this);
21415         
21416         this.picker().addClass('datepicker-dropdown');
21417         
21418         this.startViewMode = this.viewMode;
21419         
21420         if(this.singleMode){
21421             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21422                 v.setVisibilityMode(Roo.Element.DISPLAY);
21423                 v.hide();
21424             });
21425             
21426             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21427                 v.setStyle('width', '189px');
21428             });
21429         }
21430         
21431         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21432             if(!this.calendarWeeks){
21433                 v.remove();
21434                 return;
21435             }
21436             
21437             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21438             v.attr('colspan', function(i, val){
21439                 return parseInt(val) + 1;
21440             });
21441         });
21442                         
21443         
21444         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21445         
21446         this.setStartDate(this.startDate);
21447         this.setEndDate(this.endDate);
21448         
21449         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21450         
21451         this.fillDow();
21452         this.fillMonths();
21453         this.update();
21454         this.showMode();
21455         
21456         if(this.isInline) {
21457             this.showPopup();
21458         }
21459     },
21460     
21461     picker : function()
21462     {
21463         return this.pickerEl;
21464 //        return this.el.select('.datepicker', true).first();
21465     },
21466     
21467     fillDow: function()
21468     {
21469         var dowCnt = this.weekStart;
21470         
21471         var dow = {
21472             tag: 'tr',
21473             cn: [
21474                 
21475             ]
21476         };
21477         
21478         if(this.calendarWeeks){
21479             dow.cn.push({
21480                 tag: 'th',
21481                 cls: 'cw',
21482                 html: '&nbsp;'
21483             })
21484         }
21485         
21486         while (dowCnt < this.weekStart + 7) {
21487             dow.cn.push({
21488                 tag: 'th',
21489                 cls: 'dow',
21490                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21491             });
21492         }
21493         
21494         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21495     },
21496     
21497     fillMonths: function()
21498     {    
21499         var i = 0;
21500         var months = this.picker().select('>.datepicker-months td', true).first();
21501         
21502         months.dom.innerHTML = '';
21503         
21504         while (i < 12) {
21505             var month = {
21506                 tag: 'span',
21507                 cls: 'month',
21508                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21509             };
21510             
21511             months.createChild(month);
21512         }
21513         
21514     },
21515     
21516     update: function()
21517     {
21518         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;
21519         
21520         if (this.date < this.startDate) {
21521             this.viewDate = new Date(this.startDate);
21522         } else if (this.date > this.endDate) {
21523             this.viewDate = new Date(this.endDate);
21524         } else {
21525             this.viewDate = new Date(this.date);
21526         }
21527         
21528         this.fill();
21529     },
21530     
21531     fill: function() 
21532     {
21533         var d = new Date(this.viewDate),
21534                 year = d.getUTCFullYear(),
21535                 month = d.getUTCMonth(),
21536                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21537                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21538                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21539                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21540                 currentDate = this.date && this.date.valueOf(),
21541                 today = this.UTCToday();
21542         
21543         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21544         
21545 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21546         
21547 //        this.picker.select('>tfoot th.today').
21548 //                                              .text(dates[this.language].today)
21549 //                                              .toggle(this.todayBtn !== false);
21550     
21551         this.updateNavArrows();
21552         this.fillMonths();
21553                                                 
21554         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21555         
21556         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21557          
21558         prevMonth.setUTCDate(day);
21559         
21560         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21561         
21562         var nextMonth = new Date(prevMonth);
21563         
21564         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21565         
21566         nextMonth = nextMonth.valueOf();
21567         
21568         var fillMonths = false;
21569         
21570         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21571         
21572         while(prevMonth.valueOf() <= nextMonth) {
21573             var clsName = '';
21574             
21575             if (prevMonth.getUTCDay() === this.weekStart) {
21576                 if(fillMonths){
21577                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21578                 }
21579                     
21580                 fillMonths = {
21581                     tag: 'tr',
21582                     cn: []
21583                 };
21584                 
21585                 if(this.calendarWeeks){
21586                     // ISO 8601: First week contains first thursday.
21587                     // ISO also states week starts on Monday, but we can be more abstract here.
21588                     var
21589                     // Start of current week: based on weekstart/current date
21590                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21591                     // Thursday of this week
21592                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21593                     // First Thursday of year, year from thursday
21594                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21595                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21596                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21597                     
21598                     fillMonths.cn.push({
21599                         tag: 'td',
21600                         cls: 'cw',
21601                         html: calWeek
21602                     });
21603                 }
21604             }
21605             
21606             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21607                 clsName += ' old';
21608             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21609                 clsName += ' new';
21610             }
21611             if (this.todayHighlight &&
21612                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21613                 prevMonth.getUTCMonth() == today.getMonth() &&
21614                 prevMonth.getUTCDate() == today.getDate()) {
21615                 clsName += ' today';
21616             }
21617             
21618             if (currentDate && prevMonth.valueOf() === currentDate) {
21619                 clsName += ' active';
21620             }
21621             
21622             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21623                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21624                     clsName += ' disabled';
21625             }
21626             
21627             fillMonths.cn.push({
21628                 tag: 'td',
21629                 cls: 'day ' + clsName,
21630                 html: prevMonth.getDate()
21631             });
21632             
21633             prevMonth.setDate(prevMonth.getDate()+1);
21634         }
21635           
21636         var currentYear = this.date && this.date.getUTCFullYear();
21637         var currentMonth = this.date && this.date.getUTCMonth();
21638         
21639         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21640         
21641         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21642             v.removeClass('active');
21643             
21644             if(currentYear === year && k === currentMonth){
21645                 v.addClass('active');
21646             }
21647             
21648             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21649                 v.addClass('disabled');
21650             }
21651             
21652         });
21653         
21654         
21655         year = parseInt(year/10, 10) * 10;
21656         
21657         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21658         
21659         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21660         
21661         year -= 1;
21662         for (var i = -1; i < 11; i++) {
21663             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21664                 tag: 'span',
21665                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21666                 html: year
21667             });
21668             
21669             year += 1;
21670         }
21671     },
21672     
21673     showMode: function(dir) 
21674     {
21675         if (dir) {
21676             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21677         }
21678         
21679         Roo.each(this.picker().select('>div',true).elements, function(v){
21680             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21681             v.hide();
21682         });
21683         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21684     },
21685     
21686     place: function()
21687     {
21688         if(this.isInline) {
21689             return;
21690         }
21691         
21692         this.picker().removeClass(['bottom', 'top']);
21693         
21694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21695             /*
21696              * place to the top of element!
21697              *
21698              */
21699             
21700             this.picker().addClass('top');
21701             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21702             
21703             return;
21704         }
21705         
21706         this.picker().addClass('bottom');
21707         
21708         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21709     },
21710     
21711     parseDate : function(value)
21712     {
21713         if(!value || value instanceof Date){
21714             return value;
21715         }
21716         var v = Date.parseDate(value, this.format);
21717         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21718             v = Date.parseDate(value, 'Y-m-d');
21719         }
21720         if(!v && this.altFormats){
21721             if(!this.altFormatsArray){
21722                 this.altFormatsArray = this.altFormats.split("|");
21723             }
21724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21725                 v = Date.parseDate(value, this.altFormatsArray[i]);
21726             }
21727         }
21728         return v;
21729     },
21730     
21731     formatDate : function(date, fmt)
21732     {   
21733         return (!date || !(date instanceof Date)) ?
21734         date : date.dateFormat(fmt || this.format);
21735     },
21736     
21737     onFocus : function()
21738     {
21739         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21740         this.showPopup();
21741     },
21742     
21743     onBlur : function()
21744     {
21745         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21746         
21747         var d = this.inputEl().getValue();
21748         
21749         this.setValue(d);
21750                 
21751         this.hidePopup();
21752     },
21753     
21754     showPopup : function()
21755     {
21756         this.picker().show();
21757         this.update();
21758         this.place();
21759         
21760         this.fireEvent('showpopup', this, this.date);
21761     },
21762     
21763     hidePopup : function()
21764     {
21765         if(this.isInline) {
21766             return;
21767         }
21768         this.picker().hide();
21769         this.viewMode = this.startViewMode;
21770         this.showMode();
21771         
21772         this.fireEvent('hidepopup', this, this.date);
21773         
21774     },
21775     
21776     onMousedown: function(e)
21777     {
21778         e.stopPropagation();
21779         e.preventDefault();
21780     },
21781     
21782     keyup: function(e)
21783     {
21784         Roo.bootstrap.DateField.superclass.keyup.call(this);
21785         this.update();
21786     },
21787
21788     setValue: function(v)
21789     {
21790         if(this.fireEvent('beforeselect', this, v) !== false){
21791             var d = new Date(this.parseDate(v) ).clearTime();
21792         
21793             if(isNaN(d.getTime())){
21794                 this.date = this.viewDate = '';
21795                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21796                 return;
21797             }
21798
21799             v = this.formatDate(d);
21800
21801             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21802
21803             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21804
21805             this.update();
21806
21807             this.fireEvent('select', this, this.date);
21808         }
21809     },
21810     
21811     getValue: function()
21812     {
21813         return this.formatDate(this.date);
21814     },
21815     
21816     fireKey: function(e)
21817     {
21818         if (!this.picker().isVisible()){
21819             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21820                 this.showPopup();
21821             }
21822             return;
21823         }
21824         
21825         var dateChanged = false,
21826         dir, day, month,
21827         newDate, newViewDate;
21828         
21829         switch(e.keyCode){
21830             case 27: // escape
21831                 this.hidePopup();
21832                 e.preventDefault();
21833                 break;
21834             case 37: // left
21835             case 39: // right
21836                 if (!this.keyboardNavigation) {
21837                     break;
21838                 }
21839                 dir = e.keyCode == 37 ? -1 : 1;
21840                 
21841                 if (e.ctrlKey){
21842                     newDate = this.moveYear(this.date, dir);
21843                     newViewDate = this.moveYear(this.viewDate, dir);
21844                 } else if (e.shiftKey){
21845                     newDate = this.moveMonth(this.date, dir);
21846                     newViewDate = this.moveMonth(this.viewDate, dir);
21847                 } else {
21848                     newDate = new Date(this.date);
21849                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21850                     newViewDate = new Date(this.viewDate);
21851                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21852                 }
21853                 if (this.dateWithinRange(newDate)){
21854                     this.date = newDate;
21855                     this.viewDate = newViewDate;
21856                     this.setValue(this.formatDate(this.date));
21857 //                    this.update();
21858                     e.preventDefault();
21859                     dateChanged = true;
21860                 }
21861                 break;
21862             case 38: // up
21863             case 40: // down
21864                 if (!this.keyboardNavigation) {
21865                     break;
21866                 }
21867                 dir = e.keyCode == 38 ? -1 : 1;
21868                 if (e.ctrlKey){
21869                     newDate = this.moveYear(this.date, dir);
21870                     newViewDate = this.moveYear(this.viewDate, dir);
21871                 } else if (e.shiftKey){
21872                     newDate = this.moveMonth(this.date, dir);
21873                     newViewDate = this.moveMonth(this.viewDate, dir);
21874                 } else {
21875                     newDate = new Date(this.date);
21876                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21877                     newViewDate = new Date(this.viewDate);
21878                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21879                 }
21880                 if (this.dateWithinRange(newDate)){
21881                     this.date = newDate;
21882                     this.viewDate = newViewDate;
21883                     this.setValue(this.formatDate(this.date));
21884 //                    this.update();
21885                     e.preventDefault();
21886                     dateChanged = true;
21887                 }
21888                 break;
21889             case 13: // enter
21890                 this.setValue(this.formatDate(this.date));
21891                 this.hidePopup();
21892                 e.preventDefault();
21893                 break;
21894             case 9: // tab
21895                 this.setValue(this.formatDate(this.date));
21896                 this.hidePopup();
21897                 break;
21898             case 16: // shift
21899             case 17: // ctrl
21900             case 18: // alt
21901                 break;
21902             default :
21903                 this.hidePopup();
21904                 
21905         }
21906     },
21907     
21908     
21909     onClick: function(e) 
21910     {
21911         e.stopPropagation();
21912         e.preventDefault();
21913         
21914         var target = e.getTarget();
21915         
21916         if(target.nodeName.toLowerCase() === 'i'){
21917             target = Roo.get(target).dom.parentNode;
21918         }
21919         
21920         var nodeName = target.nodeName;
21921         var className = target.className;
21922         var html = target.innerHTML;
21923         //Roo.log(nodeName);
21924         
21925         switch(nodeName.toLowerCase()) {
21926             case 'th':
21927                 switch(className) {
21928                     case 'switch':
21929                         this.showMode(1);
21930                         break;
21931                     case 'prev':
21932                     case 'next':
21933                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21934                         switch(this.viewMode){
21935                                 case 0:
21936                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21937                                         break;
21938                                 case 1:
21939                                 case 2:
21940                                         this.viewDate = this.moveYear(this.viewDate, dir);
21941                                         break;
21942                         }
21943                         this.fill();
21944                         break;
21945                     case 'today':
21946                         var date = new Date();
21947                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21948 //                        this.fill()
21949                         this.setValue(this.formatDate(this.date));
21950                         
21951                         this.hidePopup();
21952                         break;
21953                 }
21954                 break;
21955             case 'span':
21956                 if (className.indexOf('disabled') < 0) {
21957                     if (!this.viewDate) {
21958                         this.viewDate = new Date();
21959                     }
21960                     this.viewDate.setUTCDate(1);
21961                     if (className.indexOf('month') > -1) {
21962                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21963                     } else {
21964                         var year = parseInt(html, 10) || 0;
21965                         this.viewDate.setUTCFullYear(year);
21966                         
21967                     }
21968                     
21969                     if(this.singleMode){
21970                         this.setValue(this.formatDate(this.viewDate));
21971                         this.hidePopup();
21972                         return;
21973                     }
21974                     
21975                     this.showMode(-1);
21976                     this.fill();
21977                 }
21978                 break;
21979                 
21980             case 'td':
21981                 //Roo.log(className);
21982                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21983                     var day = parseInt(html, 10) || 1;
21984                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21985                         month = (this.viewDate || new Date()).getUTCMonth();
21986
21987                     if (className.indexOf('old') > -1) {
21988                         if(month === 0 ){
21989                             month = 11;
21990                             year -= 1;
21991                         }else{
21992                             month -= 1;
21993                         }
21994                     } else if (className.indexOf('new') > -1) {
21995                         if (month == 11) {
21996                             month = 0;
21997                             year += 1;
21998                         } else {
21999                             month += 1;
22000                         }
22001                     }
22002                     //Roo.log([year,month,day]);
22003                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22004                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22005 //                    this.fill();
22006                     //Roo.log(this.formatDate(this.date));
22007                     this.setValue(this.formatDate(this.date));
22008                     this.hidePopup();
22009                 }
22010                 break;
22011         }
22012     },
22013     
22014     setStartDate: function(startDate)
22015     {
22016         this.startDate = startDate || -Infinity;
22017         if (this.startDate !== -Infinity) {
22018             this.startDate = this.parseDate(this.startDate);
22019         }
22020         this.update();
22021         this.updateNavArrows();
22022     },
22023
22024     setEndDate: function(endDate)
22025     {
22026         this.endDate = endDate || Infinity;
22027         if (this.endDate !== Infinity) {
22028             this.endDate = this.parseDate(this.endDate);
22029         }
22030         this.update();
22031         this.updateNavArrows();
22032     },
22033     
22034     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22035     {
22036         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22037         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22038             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22039         }
22040         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22041             return parseInt(d, 10);
22042         });
22043         this.update();
22044         this.updateNavArrows();
22045     },
22046     
22047     updateNavArrows: function() 
22048     {
22049         if(this.singleMode){
22050             return;
22051         }
22052         
22053         var d = new Date(this.viewDate),
22054         year = d.getUTCFullYear(),
22055         month = d.getUTCMonth();
22056         
22057         Roo.each(this.picker().select('.prev', true).elements, function(v){
22058             v.show();
22059             switch (this.viewMode) {
22060                 case 0:
22061
22062                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22063                         v.hide();
22064                     }
22065                     break;
22066                 case 1:
22067                 case 2:
22068                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22069                         v.hide();
22070                     }
22071                     break;
22072             }
22073         });
22074         
22075         Roo.each(this.picker().select('.next', true).elements, function(v){
22076             v.show();
22077             switch (this.viewMode) {
22078                 case 0:
22079
22080                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22081                         v.hide();
22082                     }
22083                     break;
22084                 case 1:
22085                 case 2:
22086                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22087                         v.hide();
22088                     }
22089                     break;
22090             }
22091         })
22092     },
22093     
22094     moveMonth: function(date, dir)
22095     {
22096         if (!dir) {
22097             return date;
22098         }
22099         var new_date = new Date(date.valueOf()),
22100         day = new_date.getUTCDate(),
22101         month = new_date.getUTCMonth(),
22102         mag = Math.abs(dir),
22103         new_month, test;
22104         dir = dir > 0 ? 1 : -1;
22105         if (mag == 1){
22106             test = dir == -1
22107             // If going back one month, make sure month is not current month
22108             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22109             ? function(){
22110                 return new_date.getUTCMonth() == month;
22111             }
22112             // If going forward one month, make sure month is as expected
22113             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22114             : function(){
22115                 return new_date.getUTCMonth() != new_month;
22116             };
22117             new_month = month + dir;
22118             new_date.setUTCMonth(new_month);
22119             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22120             if (new_month < 0 || new_month > 11) {
22121                 new_month = (new_month + 12) % 12;
22122             }
22123         } else {
22124             // For magnitudes >1, move one month at a time...
22125             for (var i=0; i<mag; i++) {
22126                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22127                 new_date = this.moveMonth(new_date, dir);
22128             }
22129             // ...then reset the day, keeping it in the new month
22130             new_month = new_date.getUTCMonth();
22131             new_date.setUTCDate(day);
22132             test = function(){
22133                 return new_month != new_date.getUTCMonth();
22134             };
22135         }
22136         // Common date-resetting loop -- if date is beyond end of month, make it
22137         // end of month
22138         while (test()){
22139             new_date.setUTCDate(--day);
22140             new_date.setUTCMonth(new_month);
22141         }
22142         return new_date;
22143     },
22144
22145     moveYear: function(date, dir)
22146     {
22147         return this.moveMonth(date, dir*12);
22148     },
22149
22150     dateWithinRange: function(date)
22151     {
22152         return date >= this.startDate && date <= this.endDate;
22153     },
22154
22155     
22156     remove: function() 
22157     {
22158         this.picker().remove();
22159     },
22160     
22161     validateValue : function(value)
22162     {
22163         if(this.getVisibilityEl().hasClass('hidden')){
22164             return true;
22165         }
22166         
22167         if(value.length < 1)  {
22168             if(this.allowBlank){
22169                 return true;
22170             }
22171             return false;
22172         }
22173         
22174         if(value.length < this.minLength){
22175             return false;
22176         }
22177         if(value.length > this.maxLength){
22178             return false;
22179         }
22180         if(this.vtype){
22181             var vt = Roo.form.VTypes;
22182             if(!vt[this.vtype](value, this)){
22183                 return false;
22184             }
22185         }
22186         if(typeof this.validator == "function"){
22187             var msg = this.validator(value);
22188             if(msg !== true){
22189                 return false;
22190             }
22191         }
22192         
22193         if(this.regex && !this.regex.test(value)){
22194             return false;
22195         }
22196         
22197         if(typeof(this.parseDate(value)) == 'undefined'){
22198             return false;
22199         }
22200         
22201         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22202             return false;
22203         }      
22204         
22205         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22206             return false;
22207         } 
22208         
22209         
22210         return true;
22211     },
22212     
22213     reset : function()
22214     {
22215         this.date = this.viewDate = '';
22216         
22217         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22218     }
22219    
22220 });
22221
22222 Roo.apply(Roo.bootstrap.DateField,  {
22223     
22224     head : {
22225         tag: 'thead',
22226         cn: [
22227         {
22228             tag: 'tr',
22229             cn: [
22230             {
22231                 tag: 'th',
22232                 cls: 'prev',
22233                 html: '<i class="fa fa-arrow-left"/>'
22234             },
22235             {
22236                 tag: 'th',
22237                 cls: 'switch',
22238                 colspan: '5'
22239             },
22240             {
22241                 tag: 'th',
22242                 cls: 'next',
22243                 html: '<i class="fa fa-arrow-right"/>'
22244             }
22245
22246             ]
22247         }
22248         ]
22249     },
22250     
22251     content : {
22252         tag: 'tbody',
22253         cn: [
22254         {
22255             tag: 'tr',
22256             cn: [
22257             {
22258                 tag: 'td',
22259                 colspan: '7'
22260             }
22261             ]
22262         }
22263         ]
22264     },
22265     
22266     footer : {
22267         tag: 'tfoot',
22268         cn: [
22269         {
22270             tag: 'tr',
22271             cn: [
22272             {
22273                 tag: 'th',
22274                 colspan: '7',
22275                 cls: 'today'
22276             }
22277                     
22278             ]
22279         }
22280         ]
22281     },
22282     
22283     dates:{
22284         en: {
22285             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22286             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22287             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22288             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22289             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22290             today: "Today"
22291         }
22292     },
22293     
22294     modes: [
22295     {
22296         clsName: 'days',
22297         navFnc: 'Month',
22298         navStep: 1
22299     },
22300     {
22301         clsName: 'months',
22302         navFnc: 'FullYear',
22303         navStep: 1
22304     },
22305     {
22306         clsName: 'years',
22307         navFnc: 'FullYear',
22308         navStep: 10
22309     }]
22310 });
22311
22312 Roo.apply(Roo.bootstrap.DateField,  {
22313   
22314     template : {
22315         tag: 'div',
22316         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22317         cn: [
22318         {
22319             tag: 'div',
22320             cls: 'datepicker-days',
22321             cn: [
22322             {
22323                 tag: 'table',
22324                 cls: 'table-condensed',
22325                 cn:[
22326                 Roo.bootstrap.DateField.head,
22327                 {
22328                     tag: 'tbody'
22329                 },
22330                 Roo.bootstrap.DateField.footer
22331                 ]
22332             }
22333             ]
22334         },
22335         {
22336             tag: 'div',
22337             cls: 'datepicker-months',
22338             cn: [
22339             {
22340                 tag: 'table',
22341                 cls: 'table-condensed',
22342                 cn:[
22343                 Roo.bootstrap.DateField.head,
22344                 Roo.bootstrap.DateField.content,
22345                 Roo.bootstrap.DateField.footer
22346                 ]
22347             }
22348             ]
22349         },
22350         {
22351             tag: 'div',
22352             cls: 'datepicker-years',
22353             cn: [
22354             {
22355                 tag: 'table',
22356                 cls: 'table-condensed',
22357                 cn:[
22358                 Roo.bootstrap.DateField.head,
22359                 Roo.bootstrap.DateField.content,
22360                 Roo.bootstrap.DateField.footer
22361                 ]
22362             }
22363             ]
22364         }
22365         ]
22366     }
22367 });
22368
22369  
22370
22371  /*
22372  * - LGPL
22373  *
22374  * TimeField
22375  * 
22376  */
22377
22378 /**
22379  * @class Roo.bootstrap.TimeField
22380  * @extends Roo.bootstrap.Input
22381  * Bootstrap DateField class
22382  * 
22383  * 
22384  * @constructor
22385  * Create a new TimeField
22386  * @param {Object} config The config object
22387  */
22388
22389 Roo.bootstrap.TimeField = function(config){
22390     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22391     this.addEvents({
22392             /**
22393              * @event show
22394              * Fires when this field show.
22395              * @param {Roo.bootstrap.DateField} thisthis
22396              * @param {Mixed} date The date value
22397              */
22398             show : true,
22399             /**
22400              * @event show
22401              * Fires when this field hide.
22402              * @param {Roo.bootstrap.DateField} this
22403              * @param {Mixed} date The date value
22404              */
22405             hide : true,
22406             /**
22407              * @event select
22408              * Fires when select a date.
22409              * @param {Roo.bootstrap.DateField} this
22410              * @param {Mixed} date The date value
22411              */
22412             select : true
22413         });
22414 };
22415
22416 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22417     
22418     /**
22419      * @cfg {String} format
22420      * The default time format string which can be overriden for localization support.  The format must be
22421      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22422      */
22423     format : "H:i",
22424
22425     getAutoCreate : function()
22426     {
22427         this.after = '<i class="fa far fa-clock"></i>';
22428         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22429         
22430          
22431     },
22432     onRender: function(ct, position)
22433     {
22434         
22435         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22436                 
22437         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22438         
22439         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22440         
22441         this.pop = this.picker().select('>.datepicker-time',true).first();
22442         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22443         
22444         this.picker().on('mousedown', this.onMousedown, this);
22445         this.picker().on('click', this.onClick, this);
22446         
22447         this.picker().addClass('datepicker-dropdown');
22448     
22449         this.fillTime();
22450         this.update();
22451             
22452         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22453         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22454         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22455         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22456         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22457         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22458
22459     },
22460     
22461     fireKey: function(e){
22462         if (!this.picker().isVisible()){
22463             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22464                 this.show();
22465             }
22466             return;
22467         }
22468
22469         e.preventDefault();
22470         
22471         switch(e.keyCode){
22472             case 27: // escape
22473                 this.hide();
22474                 break;
22475             case 37: // left
22476             case 39: // right
22477                 this.onTogglePeriod();
22478                 break;
22479             case 38: // up
22480                 this.onIncrementMinutes();
22481                 break;
22482             case 40: // down
22483                 this.onDecrementMinutes();
22484                 break;
22485             case 13: // enter
22486             case 9: // tab
22487                 this.setTime();
22488                 break;
22489         }
22490     },
22491     
22492     onClick: function(e) {
22493         e.stopPropagation();
22494         e.preventDefault();
22495     },
22496     
22497     picker : function()
22498     {
22499         return this.pickerEl;
22500     },
22501     
22502     fillTime: function()
22503     {    
22504         var time = this.pop.select('tbody', true).first();
22505         
22506         time.dom.innerHTML = '';
22507         
22508         time.createChild({
22509             tag: 'tr',
22510             cn: [
22511                 {
22512                     tag: 'td',
22513                     cn: [
22514                         {
22515                             tag: 'a',
22516                             href: '#',
22517                             cls: 'btn',
22518                             cn: [
22519                                 {
22520                                     tag: 'i',
22521                                     cls: 'hours-up fa fas fa-chevron-up'
22522                                 }
22523                             ]
22524                         } 
22525                     ]
22526                 },
22527                 {
22528                     tag: 'td',
22529                     cls: 'separator'
22530                 },
22531                 {
22532                     tag: 'td',
22533                     cn: [
22534                         {
22535                             tag: 'a',
22536                             href: '#',
22537                             cls: 'btn',
22538                             cn: [
22539                                 {
22540                                     tag: 'i',
22541                                     cls: 'minutes-up fa fas fa-chevron-up'
22542                                 }
22543                             ]
22544                         }
22545                     ]
22546                 },
22547                 {
22548                     tag: 'td',
22549                     cls: 'separator'
22550                 }
22551             ]
22552         });
22553         
22554         time.createChild({
22555             tag: 'tr',
22556             cn: [
22557                 {
22558                     tag: 'td',
22559                     cn: [
22560                         {
22561                             tag: 'span',
22562                             cls: 'timepicker-hour',
22563                             html: '00'
22564                         }  
22565                     ]
22566                 },
22567                 {
22568                     tag: 'td',
22569                     cls: 'separator',
22570                     html: ':'
22571                 },
22572                 {
22573                     tag: 'td',
22574                     cn: [
22575                         {
22576                             tag: 'span',
22577                             cls: 'timepicker-minute',
22578                             html: '00'
22579                         }  
22580                     ]
22581                 },
22582                 {
22583                     tag: 'td',
22584                     cls: 'separator'
22585                 },
22586                 {
22587                     tag: 'td',
22588                     cn: [
22589                         {
22590                             tag: 'button',
22591                             type: 'button',
22592                             cls: 'btn btn-primary period',
22593                             html: 'AM'
22594                             
22595                         }
22596                     ]
22597                 }
22598             ]
22599         });
22600         
22601         time.createChild({
22602             tag: 'tr',
22603             cn: [
22604                 {
22605                     tag: 'td',
22606                     cn: [
22607                         {
22608                             tag: 'a',
22609                             href: '#',
22610                             cls: 'btn',
22611                             cn: [
22612                                 {
22613                                     tag: 'span',
22614                                     cls: 'hours-down fa fas fa-chevron-down'
22615                                 }
22616                             ]
22617                         }
22618                     ]
22619                 },
22620                 {
22621                     tag: 'td',
22622                     cls: 'separator'
22623                 },
22624                 {
22625                     tag: 'td',
22626                     cn: [
22627                         {
22628                             tag: 'a',
22629                             href: '#',
22630                             cls: 'btn',
22631                             cn: [
22632                                 {
22633                                     tag: 'span',
22634                                     cls: 'minutes-down fa fas fa-chevron-down'
22635                                 }
22636                             ]
22637                         }
22638                     ]
22639                 },
22640                 {
22641                     tag: 'td',
22642                     cls: 'separator'
22643                 }
22644             ]
22645         });
22646         
22647     },
22648     
22649     update: function()
22650     {
22651         
22652         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22653         
22654         this.fill();
22655     },
22656     
22657     fill: function() 
22658     {
22659         var hours = this.time.getHours();
22660         var minutes = this.time.getMinutes();
22661         var period = 'AM';
22662         
22663         if(hours > 11){
22664             period = 'PM';
22665         }
22666         
22667         if(hours == 0){
22668             hours = 12;
22669         }
22670         
22671         
22672         if(hours > 12){
22673             hours = hours - 12;
22674         }
22675         
22676         if(hours < 10){
22677             hours = '0' + hours;
22678         }
22679         
22680         if(minutes < 10){
22681             minutes = '0' + minutes;
22682         }
22683         
22684         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22685         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22686         this.pop.select('button', true).first().dom.innerHTML = period;
22687         
22688     },
22689     
22690     place: function()
22691     {   
22692         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22693         
22694         var cls = ['bottom'];
22695         
22696         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22697             cls.pop();
22698             cls.push('top');
22699         }
22700         
22701         cls.push('right');
22702         
22703         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22704             cls.pop();
22705             cls.push('left');
22706         }
22707         //this.picker().setXY(20000,20000);
22708         this.picker().addClass(cls.join('-'));
22709         
22710         var _this = this;
22711         
22712         Roo.each(cls, function(c){
22713             if(c == 'bottom'){
22714                 (function() {
22715                  //  
22716                 }).defer(200);
22717                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22718                 //_this.picker().setTop(_this.inputEl().getHeight());
22719                 return;
22720             }
22721             if(c == 'top'){
22722                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22723                 
22724                 //_this.picker().setTop(0 - _this.picker().getHeight());
22725                 return;
22726             }
22727             /*
22728             if(c == 'left'){
22729                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22730                 return;
22731             }
22732             if(c == 'right'){
22733                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22734                 return;
22735             }
22736             */
22737         });
22738         
22739     },
22740   
22741     onFocus : function()
22742     {
22743         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22744         this.show();
22745     },
22746     
22747     onBlur : function()
22748     {
22749         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22750         this.hide();
22751     },
22752     
22753     show : function()
22754     {
22755         this.picker().show();
22756         this.pop.show();
22757         this.update();
22758         this.place();
22759         
22760         this.fireEvent('show', this, this.date);
22761     },
22762     
22763     hide : function()
22764     {
22765         this.picker().hide();
22766         this.pop.hide();
22767         
22768         this.fireEvent('hide', this, this.date);
22769     },
22770     
22771     setTime : function()
22772     {
22773         this.hide();
22774         this.setValue(this.time.format(this.format));
22775         
22776         this.fireEvent('select', this, this.date);
22777         
22778         
22779     },
22780     
22781     onMousedown: function(e){
22782         e.stopPropagation();
22783         e.preventDefault();
22784     },
22785     
22786     onIncrementHours: function()
22787     {
22788         Roo.log('onIncrementHours');
22789         this.time = this.time.add(Date.HOUR, 1);
22790         this.update();
22791         
22792     },
22793     
22794     onDecrementHours: function()
22795     {
22796         Roo.log('onDecrementHours');
22797         this.time = this.time.add(Date.HOUR, -1);
22798         this.update();
22799     },
22800     
22801     onIncrementMinutes: function()
22802     {
22803         Roo.log('onIncrementMinutes');
22804         this.time = this.time.add(Date.MINUTE, 1);
22805         this.update();
22806     },
22807     
22808     onDecrementMinutes: function()
22809     {
22810         Roo.log('onDecrementMinutes');
22811         this.time = this.time.add(Date.MINUTE, -1);
22812         this.update();
22813     },
22814     
22815     onTogglePeriod: function()
22816     {
22817         Roo.log('onTogglePeriod');
22818         this.time = this.time.add(Date.HOUR, 12);
22819         this.update();
22820     }
22821     
22822    
22823 });
22824  
22825
22826 Roo.apply(Roo.bootstrap.TimeField,  {
22827   
22828     template : {
22829         tag: 'div',
22830         cls: 'datepicker dropdown-menu',
22831         cn: [
22832             {
22833                 tag: 'div',
22834                 cls: 'datepicker-time',
22835                 cn: [
22836                 {
22837                     tag: 'table',
22838                     cls: 'table-condensed',
22839                     cn:[
22840                         {
22841                             tag: 'tbody',
22842                             cn: [
22843                                 {
22844                                     tag: 'tr',
22845                                     cn: [
22846                                     {
22847                                         tag: 'td',
22848                                         colspan: '7'
22849                                     }
22850                                     ]
22851                                 }
22852                             ]
22853                         },
22854                         {
22855                             tag: 'tfoot',
22856                             cn: [
22857                                 {
22858                                     tag: 'tr',
22859                                     cn: [
22860                                     {
22861                                         tag: 'th',
22862                                         colspan: '7',
22863                                         cls: '',
22864                                         cn: [
22865                                             {
22866                                                 tag: 'button',
22867                                                 cls: 'btn btn-info ok',
22868                                                 html: 'OK'
22869                                             }
22870                                         ]
22871                                     }
22872                     
22873                                     ]
22874                                 }
22875                             ]
22876                         }
22877                     ]
22878                 }
22879                 ]
22880             }
22881         ]
22882     }
22883 });
22884
22885  
22886
22887  /*
22888  * - LGPL
22889  *
22890  * MonthField
22891  * 
22892  */
22893
22894 /**
22895  * @class Roo.bootstrap.MonthField
22896  * @extends Roo.bootstrap.Input
22897  * Bootstrap MonthField class
22898  * 
22899  * @cfg {String} language default en
22900  * 
22901  * @constructor
22902  * Create a new MonthField
22903  * @param {Object} config The config object
22904  */
22905
22906 Roo.bootstrap.MonthField = function(config){
22907     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22908     
22909     this.addEvents({
22910         /**
22911          * @event show
22912          * Fires when this field show.
22913          * @param {Roo.bootstrap.MonthField} this
22914          * @param {Mixed} date The date value
22915          */
22916         show : true,
22917         /**
22918          * @event show
22919          * Fires when this field hide.
22920          * @param {Roo.bootstrap.MonthField} this
22921          * @param {Mixed} date The date value
22922          */
22923         hide : true,
22924         /**
22925          * @event select
22926          * Fires when select a date.
22927          * @param {Roo.bootstrap.MonthField} this
22928          * @param {String} oldvalue The old value
22929          * @param {String} newvalue The new value
22930          */
22931         select : true
22932     });
22933 };
22934
22935 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22936     
22937     onRender: function(ct, position)
22938     {
22939         
22940         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22941         
22942         this.language = this.language || 'en';
22943         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22944         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22945         
22946         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22947         this.isInline = false;
22948         this.isInput = true;
22949         this.component = this.el.select('.add-on', true).first() || false;
22950         this.component = (this.component && this.component.length === 0) ? false : this.component;
22951         this.hasInput = this.component && this.inputEL().length;
22952         
22953         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22954         
22955         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22956         
22957         this.picker().on('mousedown', this.onMousedown, this);
22958         this.picker().on('click', this.onClick, this);
22959         
22960         this.picker().addClass('datepicker-dropdown');
22961         
22962         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22963             v.setStyle('width', '189px');
22964         });
22965         
22966         this.fillMonths();
22967         
22968         this.update();
22969         
22970         if(this.isInline) {
22971             this.show();
22972         }
22973         
22974     },
22975     
22976     setValue: function(v, suppressEvent)
22977     {   
22978         var o = this.getValue();
22979         
22980         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22981         
22982         this.update();
22983
22984         if(suppressEvent !== true){
22985             this.fireEvent('select', this, o, v);
22986         }
22987         
22988     },
22989     
22990     getValue: function()
22991     {
22992         return this.value;
22993     },
22994     
22995     onClick: function(e) 
22996     {
22997         e.stopPropagation();
22998         e.preventDefault();
22999         
23000         var target = e.getTarget();
23001         
23002         if(target.nodeName.toLowerCase() === 'i'){
23003             target = Roo.get(target).dom.parentNode;
23004         }
23005         
23006         var nodeName = target.nodeName;
23007         var className = target.className;
23008         var html = target.innerHTML;
23009         
23010         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23011             return;
23012         }
23013         
23014         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23015         
23016         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23017         
23018         this.hide();
23019                         
23020     },
23021     
23022     picker : function()
23023     {
23024         return this.pickerEl;
23025     },
23026     
23027     fillMonths: function()
23028     {    
23029         var i = 0;
23030         var months = this.picker().select('>.datepicker-months td', true).first();
23031         
23032         months.dom.innerHTML = '';
23033         
23034         while (i < 12) {
23035             var month = {
23036                 tag: 'span',
23037                 cls: 'month',
23038                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23039             };
23040             
23041             months.createChild(month);
23042         }
23043         
23044     },
23045     
23046     update: function()
23047     {
23048         var _this = this;
23049         
23050         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23051             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23052         }
23053         
23054         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23055             e.removeClass('active');
23056             
23057             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23058                 e.addClass('active');
23059             }
23060         })
23061     },
23062     
23063     place: function()
23064     {
23065         if(this.isInline) {
23066             return;
23067         }
23068         
23069         this.picker().removeClass(['bottom', 'top']);
23070         
23071         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23072             /*
23073              * place to the top of element!
23074              *
23075              */
23076             
23077             this.picker().addClass('top');
23078             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23079             
23080             return;
23081         }
23082         
23083         this.picker().addClass('bottom');
23084         
23085         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23086     },
23087     
23088     onFocus : function()
23089     {
23090         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23091         this.show();
23092     },
23093     
23094     onBlur : function()
23095     {
23096         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23097         
23098         var d = this.inputEl().getValue();
23099         
23100         this.setValue(d);
23101                 
23102         this.hide();
23103     },
23104     
23105     show : function()
23106     {
23107         this.picker().show();
23108         this.picker().select('>.datepicker-months', true).first().show();
23109         this.update();
23110         this.place();
23111         
23112         this.fireEvent('show', this, this.date);
23113     },
23114     
23115     hide : function()
23116     {
23117         if(this.isInline) {
23118             return;
23119         }
23120         this.picker().hide();
23121         this.fireEvent('hide', this, this.date);
23122         
23123     },
23124     
23125     onMousedown: function(e)
23126     {
23127         e.stopPropagation();
23128         e.preventDefault();
23129     },
23130     
23131     keyup: function(e)
23132     {
23133         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23134         this.update();
23135     },
23136
23137     fireKey: function(e)
23138     {
23139         if (!this.picker().isVisible()){
23140             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23141                 this.show();
23142             }
23143             return;
23144         }
23145         
23146         var dir;
23147         
23148         switch(e.keyCode){
23149             case 27: // escape
23150                 this.hide();
23151                 e.preventDefault();
23152                 break;
23153             case 37: // left
23154             case 39: // right
23155                 dir = e.keyCode == 37 ? -1 : 1;
23156                 
23157                 this.vIndex = this.vIndex + dir;
23158                 
23159                 if(this.vIndex < 0){
23160                     this.vIndex = 0;
23161                 }
23162                 
23163                 if(this.vIndex > 11){
23164                     this.vIndex = 11;
23165                 }
23166                 
23167                 if(isNaN(this.vIndex)){
23168                     this.vIndex = 0;
23169                 }
23170                 
23171                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23172                 
23173                 break;
23174             case 38: // up
23175             case 40: // down
23176                 
23177                 dir = e.keyCode == 38 ? -1 : 1;
23178                 
23179                 this.vIndex = this.vIndex + dir * 4;
23180                 
23181                 if(this.vIndex < 0){
23182                     this.vIndex = 0;
23183                 }
23184                 
23185                 if(this.vIndex > 11){
23186                     this.vIndex = 11;
23187                 }
23188                 
23189                 if(isNaN(this.vIndex)){
23190                     this.vIndex = 0;
23191                 }
23192                 
23193                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23194                 break;
23195                 
23196             case 13: // enter
23197                 
23198                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23199                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23200                 }
23201                 
23202                 this.hide();
23203                 e.preventDefault();
23204                 break;
23205             case 9: // tab
23206                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23207                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23208                 }
23209                 this.hide();
23210                 break;
23211             case 16: // shift
23212             case 17: // ctrl
23213             case 18: // alt
23214                 break;
23215             default :
23216                 this.hide();
23217                 
23218         }
23219     },
23220     
23221     remove: function() 
23222     {
23223         this.picker().remove();
23224     }
23225    
23226 });
23227
23228 Roo.apply(Roo.bootstrap.MonthField,  {
23229     
23230     content : {
23231         tag: 'tbody',
23232         cn: [
23233         {
23234             tag: 'tr',
23235             cn: [
23236             {
23237                 tag: 'td',
23238                 colspan: '7'
23239             }
23240             ]
23241         }
23242         ]
23243     },
23244     
23245     dates:{
23246         en: {
23247             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23248             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23249         }
23250     }
23251 });
23252
23253 Roo.apply(Roo.bootstrap.MonthField,  {
23254   
23255     template : {
23256         tag: 'div',
23257         cls: 'datepicker dropdown-menu roo-dynamic',
23258         cn: [
23259             {
23260                 tag: 'div',
23261                 cls: 'datepicker-months',
23262                 cn: [
23263                 {
23264                     tag: 'table',
23265                     cls: 'table-condensed',
23266                     cn:[
23267                         Roo.bootstrap.DateField.content
23268                     ]
23269                 }
23270                 ]
23271             }
23272         ]
23273     }
23274 });
23275
23276  
23277
23278  
23279  /*
23280  * - LGPL
23281  *
23282  * CheckBox
23283  * 
23284  */
23285
23286 /**
23287  * @class Roo.bootstrap.CheckBox
23288  * @extends Roo.bootstrap.Input
23289  * Bootstrap CheckBox class
23290  * 
23291  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23292  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23293  * @cfg {String} boxLabel The text that appears beside the checkbox
23294  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23295  * @cfg {Boolean} checked initnal the element
23296  * @cfg {Boolean} inline inline the element (default false)
23297  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23298  * @cfg {String} tooltip label tooltip
23299  * 
23300  * @constructor
23301  * Create a new CheckBox
23302  * @param {Object} config The config object
23303  */
23304
23305 Roo.bootstrap.CheckBox = function(config){
23306     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23307    
23308     this.addEvents({
23309         /**
23310         * @event check
23311         * Fires when the element is checked or unchecked.
23312         * @param {Roo.bootstrap.CheckBox} this This input
23313         * @param {Boolean} checked The new checked value
23314         */
23315        check : true,
23316        /**
23317         * @event click
23318         * Fires when the element is click.
23319         * @param {Roo.bootstrap.CheckBox} this This input
23320         */
23321        click : true
23322     });
23323     
23324 };
23325
23326 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23327   
23328     inputType: 'checkbox',
23329     inputValue: 1,
23330     valueOff: 0,
23331     boxLabel: false,
23332     checked: false,
23333     weight : false,
23334     inline: false,
23335     tooltip : '',
23336     
23337     // checkbox success does not make any sense really.. 
23338     invalidClass : "",
23339     validClass : "",
23340     
23341     
23342     getAutoCreate : function()
23343     {
23344         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23345         
23346         var id = Roo.id();
23347         
23348         var cfg = {};
23349         
23350         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23351         
23352         if(this.inline){
23353             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23354         }
23355         
23356         var input =  {
23357             tag: 'input',
23358             id : id,
23359             type : this.inputType,
23360             value : this.inputValue,
23361             cls : 'roo-' + this.inputType, //'form-box',
23362             placeholder : this.placeholder || ''
23363             
23364         };
23365         
23366         if(this.inputType != 'radio'){
23367             var hidden =  {
23368                 tag: 'input',
23369                 type : 'hidden',
23370                 cls : 'roo-hidden-value',
23371                 value : this.checked ? this.inputValue : this.valueOff
23372             };
23373         }
23374         
23375             
23376         if (this.weight) { // Validity check?
23377             cfg.cls += " " + this.inputType + "-" + this.weight;
23378         }
23379         
23380         if (this.disabled) {
23381             input.disabled=true;
23382         }
23383         
23384         if(this.checked){
23385             input.checked = this.checked;
23386         }
23387         
23388         if (this.name) {
23389             
23390             input.name = this.name;
23391             
23392             if(this.inputType != 'radio'){
23393                 hidden.name = this.name;
23394                 input.name = '_hidden_' + this.name;
23395             }
23396         }
23397         
23398         if (this.size) {
23399             input.cls += ' input-' + this.size;
23400         }
23401         
23402         var settings=this;
23403         
23404         ['xs','sm','md','lg'].map(function(size){
23405             if (settings[size]) {
23406                 cfg.cls += ' col-' + size + '-' + settings[size];
23407             }
23408         });
23409         
23410         var inputblock = input;
23411          
23412         if (this.before || this.after) {
23413             
23414             inputblock = {
23415                 cls : 'input-group',
23416                 cn :  [] 
23417             };
23418             
23419             if (this.before) {
23420                 inputblock.cn.push({
23421                     tag :'span',
23422                     cls : 'input-group-addon',
23423                     html : this.before
23424                 });
23425             }
23426             
23427             inputblock.cn.push(input);
23428             
23429             if(this.inputType != 'radio'){
23430                 inputblock.cn.push(hidden);
23431             }
23432             
23433             if (this.after) {
23434                 inputblock.cn.push({
23435                     tag :'span',
23436                     cls : 'input-group-addon',
23437                     html : this.after
23438                 });
23439             }
23440             
23441         }
23442         var boxLabelCfg = false;
23443         
23444         if(this.boxLabel){
23445            
23446             boxLabelCfg = {
23447                 tag: 'label',
23448                 //'for': id, // box label is handled by onclick - so no for...
23449                 cls: 'box-label',
23450                 html: this.boxLabel
23451             };
23452             if(this.tooltip){
23453                 boxLabelCfg.tooltip = this.tooltip;
23454             }
23455              
23456         }
23457         
23458         
23459         if (align ==='left' && this.fieldLabel.length) {
23460 //                Roo.log("left and has label");
23461             cfg.cn = [
23462                 {
23463                     tag: 'label',
23464                     'for' :  id,
23465                     cls : 'control-label',
23466                     html : this.fieldLabel
23467                 },
23468                 {
23469                     cls : "", 
23470                     cn: [
23471                         inputblock
23472                     ]
23473                 }
23474             ];
23475             
23476             if (boxLabelCfg) {
23477                 cfg.cn[1].cn.push(boxLabelCfg);
23478             }
23479             
23480             if(this.labelWidth > 12){
23481                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23482             }
23483             
23484             if(this.labelWidth < 13 && this.labelmd == 0){
23485                 this.labelmd = this.labelWidth;
23486             }
23487             
23488             if(this.labellg > 0){
23489                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23490                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23491             }
23492             
23493             if(this.labelmd > 0){
23494                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23495                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23496             }
23497             
23498             if(this.labelsm > 0){
23499                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23500                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23501             }
23502             
23503             if(this.labelxs > 0){
23504                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23505                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23506             }
23507             
23508         } else if ( this.fieldLabel.length) {
23509 //                Roo.log(" label");
23510                 cfg.cn = [
23511                    
23512                     {
23513                         tag: this.boxLabel ? 'span' : 'label',
23514                         'for': id,
23515                         cls: 'control-label box-input-label',
23516                         //cls : 'input-group-addon',
23517                         html : this.fieldLabel
23518                     },
23519                     
23520                     inputblock
23521                     
23522                 ];
23523                 if (boxLabelCfg) {
23524                     cfg.cn.push(boxLabelCfg);
23525                 }
23526
23527         } else {
23528             
23529 //                Roo.log(" no label && no align");
23530                 cfg.cn = [  inputblock ] ;
23531                 if (boxLabelCfg) {
23532                     cfg.cn.push(boxLabelCfg);
23533                 }
23534
23535                 
23536         }
23537         
23538        
23539         
23540         if(this.inputType != 'radio'){
23541             cfg.cn.push(hidden);
23542         }
23543         
23544         return cfg;
23545         
23546     },
23547     
23548     /**
23549      * return the real input element.
23550      */
23551     inputEl: function ()
23552     {
23553         return this.el.select('input.roo-' + this.inputType,true).first();
23554     },
23555     hiddenEl: function ()
23556     {
23557         return this.el.select('input.roo-hidden-value',true).first();
23558     },
23559     
23560     labelEl: function()
23561     {
23562         return this.el.select('label.control-label',true).first();
23563     },
23564     /* depricated... */
23565     
23566     label: function()
23567     {
23568         return this.labelEl();
23569     },
23570     
23571     boxLabelEl: function()
23572     {
23573         return this.el.select('label.box-label',true).first();
23574     },
23575     
23576     initEvents : function()
23577     {
23578 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23579         
23580         this.inputEl().on('click', this.onClick,  this);
23581         
23582         if (this.boxLabel) { 
23583             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23584         }
23585         
23586         this.startValue = this.getValue();
23587         
23588         if(this.groupId){
23589             Roo.bootstrap.CheckBox.register(this);
23590         }
23591     },
23592     
23593     onClick : function(e)
23594     {   
23595         if(this.fireEvent('click', this, e) !== false){
23596             this.setChecked(!this.checked);
23597         }
23598         
23599     },
23600     
23601     setChecked : function(state,suppressEvent)
23602     {
23603         this.startValue = this.getValue();
23604
23605         if(this.inputType == 'radio'){
23606             
23607             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23608                 e.dom.checked = false;
23609             });
23610             
23611             this.inputEl().dom.checked = true;
23612             
23613             this.inputEl().dom.value = this.inputValue;
23614             
23615             if(suppressEvent !== true){
23616                 this.fireEvent('check', this, true);
23617             }
23618             
23619             this.validate();
23620             
23621             return;
23622         }
23623         
23624         this.checked = state;
23625         
23626         this.inputEl().dom.checked = state;
23627         
23628         
23629         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23630         
23631         if(suppressEvent !== true){
23632             this.fireEvent('check', this, state);
23633         }
23634         
23635         this.validate();
23636     },
23637     
23638     getValue : function()
23639     {
23640         if(this.inputType == 'radio'){
23641             return this.getGroupValue();
23642         }
23643         
23644         return this.hiddenEl().dom.value;
23645         
23646     },
23647     
23648     getGroupValue : function()
23649     {
23650         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23651             return '';
23652         }
23653         
23654         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23655     },
23656     
23657     setValue : function(v,suppressEvent)
23658     {
23659         if(this.inputType == 'radio'){
23660             this.setGroupValue(v, suppressEvent);
23661             return;
23662         }
23663         
23664         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23665         
23666         this.validate();
23667     },
23668     
23669     setGroupValue : function(v, suppressEvent)
23670     {
23671         this.startValue = this.getValue();
23672         
23673         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23674             e.dom.checked = false;
23675             
23676             if(e.dom.value == v){
23677                 e.dom.checked = true;
23678             }
23679         });
23680         
23681         if(suppressEvent !== true){
23682             this.fireEvent('check', this, true);
23683         }
23684
23685         this.validate();
23686         
23687         return;
23688     },
23689     
23690     validate : function()
23691     {
23692         if(this.getVisibilityEl().hasClass('hidden')){
23693             return true;
23694         }
23695         
23696         if(
23697                 this.disabled || 
23698                 (this.inputType == 'radio' && this.validateRadio()) ||
23699                 (this.inputType == 'checkbox' && this.validateCheckbox())
23700         ){
23701             this.markValid();
23702             return true;
23703         }
23704         
23705         this.markInvalid();
23706         return false;
23707     },
23708     
23709     validateRadio : function()
23710     {
23711         if(this.getVisibilityEl().hasClass('hidden')){
23712             return true;
23713         }
23714         
23715         if(this.allowBlank){
23716             return true;
23717         }
23718         
23719         var valid = false;
23720         
23721         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23722             if(!e.dom.checked){
23723                 return;
23724             }
23725             
23726             valid = true;
23727             
23728             return false;
23729         });
23730         
23731         return valid;
23732     },
23733     
23734     validateCheckbox : function()
23735     {
23736         if(!this.groupId){
23737             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23738             //return (this.getValue() == this.inputValue) ? true : false;
23739         }
23740         
23741         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23742         
23743         if(!group){
23744             return false;
23745         }
23746         
23747         var r = false;
23748         
23749         for(var i in group){
23750             if(group[i].el.isVisible(true)){
23751                 r = false;
23752                 break;
23753             }
23754             
23755             r = true;
23756         }
23757         
23758         for(var i in group){
23759             if(r){
23760                 break;
23761             }
23762             
23763             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23764         }
23765         
23766         return r;
23767     },
23768     
23769     /**
23770      * Mark this field as valid
23771      */
23772     markValid : function()
23773     {
23774         var _this = this;
23775         
23776         this.fireEvent('valid', this);
23777         
23778         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23779         
23780         if(this.groupId){
23781             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23782         }
23783         
23784         if(label){
23785             label.markValid();
23786         }
23787
23788         if(this.inputType == 'radio'){
23789             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23790                 var fg = e.findParent('.form-group', false, true);
23791                 if (Roo.bootstrap.version == 3) {
23792                     fg.removeClass([_this.invalidClass, _this.validClass]);
23793                     fg.addClass(_this.validClass);
23794                 } else {
23795                     fg.removeClass(['is-valid', 'is-invalid']);
23796                     fg.addClass('is-valid');
23797                 }
23798             });
23799             
23800             return;
23801         }
23802
23803         if(!this.groupId){
23804             var fg = this.el.findParent('.form-group', false, true);
23805             if (Roo.bootstrap.version == 3) {
23806                 fg.removeClass([this.invalidClass, this.validClass]);
23807                 fg.addClass(this.validClass);
23808             } else {
23809                 fg.removeClass(['is-valid', 'is-invalid']);
23810                 fg.addClass('is-valid');
23811             }
23812             return;
23813         }
23814         
23815         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23816         
23817         if(!group){
23818             return;
23819         }
23820         
23821         for(var i in group){
23822             var fg = group[i].el.findParent('.form-group', false, true);
23823             if (Roo.bootstrap.version == 3) {
23824                 fg.removeClass([this.invalidClass, this.validClass]);
23825                 fg.addClass(this.validClass);
23826             } else {
23827                 fg.removeClass(['is-valid', 'is-invalid']);
23828                 fg.addClass('is-valid');
23829             }
23830         }
23831     },
23832     
23833      /**
23834      * Mark this field as invalid
23835      * @param {String} msg The validation message
23836      */
23837     markInvalid : function(msg)
23838     {
23839         if(this.allowBlank){
23840             return;
23841         }
23842         
23843         var _this = this;
23844         
23845         this.fireEvent('invalid', this, msg);
23846         
23847         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23848         
23849         if(this.groupId){
23850             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23851         }
23852         
23853         if(label){
23854             label.markInvalid();
23855         }
23856             
23857         if(this.inputType == 'radio'){
23858             
23859             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23860                 var fg = e.findParent('.form-group', false, true);
23861                 if (Roo.bootstrap.version == 3) {
23862                     fg.removeClass([_this.invalidClass, _this.validClass]);
23863                     fg.addClass(_this.invalidClass);
23864                 } else {
23865                     fg.removeClass(['is-invalid', 'is-valid']);
23866                     fg.addClass('is-invalid');
23867                 }
23868             });
23869             
23870             return;
23871         }
23872         
23873         if(!this.groupId){
23874             var fg = this.el.findParent('.form-group', false, true);
23875             if (Roo.bootstrap.version == 3) {
23876                 fg.removeClass([_this.invalidClass, _this.validClass]);
23877                 fg.addClass(_this.invalidClass);
23878             } else {
23879                 fg.removeClass(['is-invalid', 'is-valid']);
23880                 fg.addClass('is-invalid');
23881             }
23882             return;
23883         }
23884         
23885         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23886         
23887         if(!group){
23888             return;
23889         }
23890         
23891         for(var i in group){
23892             var fg = group[i].el.findParent('.form-group', false, true);
23893             if (Roo.bootstrap.version == 3) {
23894                 fg.removeClass([_this.invalidClass, _this.validClass]);
23895                 fg.addClass(_this.invalidClass);
23896             } else {
23897                 fg.removeClass(['is-invalid', 'is-valid']);
23898                 fg.addClass('is-invalid');
23899             }
23900         }
23901         
23902     },
23903     
23904     clearInvalid : function()
23905     {
23906         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23907         
23908         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23909         
23910         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23911         
23912         if (label && label.iconEl) {
23913             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23914             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23915         }
23916     },
23917     
23918     disable : function()
23919     {
23920         if(this.inputType != 'radio'){
23921             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23922             return;
23923         }
23924         
23925         var _this = this;
23926         
23927         if(this.rendered){
23928             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23929                 _this.getActionEl().addClass(this.disabledClass);
23930                 e.dom.disabled = true;
23931             });
23932         }
23933         
23934         this.disabled = true;
23935         this.fireEvent("disable", this);
23936         return this;
23937     },
23938
23939     enable : function()
23940     {
23941         if(this.inputType != 'radio'){
23942             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23943             return;
23944         }
23945         
23946         var _this = this;
23947         
23948         if(this.rendered){
23949             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23950                 _this.getActionEl().removeClass(this.disabledClass);
23951                 e.dom.disabled = false;
23952             });
23953         }
23954         
23955         this.disabled = false;
23956         this.fireEvent("enable", this);
23957         return this;
23958     },
23959     
23960     setBoxLabel : function(v)
23961     {
23962         this.boxLabel = v;
23963         
23964         if(this.rendered){
23965             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23966         }
23967     }
23968
23969 });
23970
23971 Roo.apply(Roo.bootstrap.CheckBox, {
23972     
23973     groups: {},
23974     
23975      /**
23976     * register a CheckBox Group
23977     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23978     */
23979     register : function(checkbox)
23980     {
23981         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23982             this.groups[checkbox.groupId] = {};
23983         }
23984         
23985         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23986             return;
23987         }
23988         
23989         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23990         
23991     },
23992     /**
23993     * fetch a CheckBox Group based on the group ID
23994     * @param {string} the group ID
23995     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23996     */
23997     get: function(groupId) {
23998         if (typeof(this.groups[groupId]) == 'undefined') {
23999             return false;
24000         }
24001         
24002         return this.groups[groupId] ;
24003     }
24004     
24005     
24006 });
24007 /*
24008  * - LGPL
24009  *
24010  * RadioItem
24011  * 
24012  */
24013
24014 /**
24015  * @class Roo.bootstrap.Radio
24016  * @extends Roo.bootstrap.Component
24017  * Bootstrap Radio class
24018  * @cfg {String} boxLabel - the label associated
24019  * @cfg {String} value - the value of radio
24020  * 
24021  * @constructor
24022  * Create a new Radio
24023  * @param {Object} config The config object
24024  */
24025 Roo.bootstrap.Radio = function(config){
24026     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24027     
24028 };
24029
24030 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24031     
24032     boxLabel : '',
24033     
24034     value : '',
24035     
24036     getAutoCreate : function()
24037     {
24038         var cfg = {
24039             tag : 'div',
24040             cls : 'form-group radio',
24041             cn : [
24042                 {
24043                     tag : 'label',
24044                     cls : 'box-label',
24045                     html : this.boxLabel
24046                 }
24047             ]
24048         };
24049         
24050         return cfg;
24051     },
24052     
24053     initEvents : function() 
24054     {
24055         this.parent().register(this);
24056         
24057         this.el.on('click', this.onClick, this);
24058         
24059     },
24060     
24061     onClick : function(e)
24062     {
24063         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24064             this.setChecked(true);
24065         }
24066     },
24067     
24068     setChecked : function(state, suppressEvent)
24069     {
24070         this.parent().setValue(this.value, suppressEvent);
24071         
24072     },
24073     
24074     setBoxLabel : function(v)
24075     {
24076         this.boxLabel = v;
24077         
24078         if(this.rendered){
24079             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24080         }
24081     }
24082     
24083 });
24084  
24085
24086  /*
24087  * - LGPL
24088  *
24089  * Input
24090  * 
24091  */
24092
24093 /**
24094  * @class Roo.bootstrap.SecurePass
24095  * @extends Roo.bootstrap.Input
24096  * Bootstrap SecurePass class
24097  *
24098  * 
24099  * @constructor
24100  * Create a new SecurePass
24101  * @param {Object} config The config object
24102  */
24103  
24104 Roo.bootstrap.SecurePass = function (config) {
24105     // these go here, so the translation tool can replace them..
24106     this.errors = {
24107         PwdEmpty: "Please type a password, and then retype it to confirm.",
24108         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24109         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24110         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24111         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24112         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24113         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24114         TooWeak: "Your password is Too Weak."
24115     },
24116     this.meterLabel = "Password strength:";
24117     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24118     this.meterClass = [
24119         "roo-password-meter-tooweak", 
24120         "roo-password-meter-weak", 
24121         "roo-password-meter-medium", 
24122         "roo-password-meter-strong", 
24123         "roo-password-meter-grey"
24124     ];
24125     
24126     this.errors = {};
24127     
24128     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24129 }
24130
24131 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24132     /**
24133      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24134      * {
24135      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24136      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24137      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24138      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24139      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24140      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24141      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24142      * })
24143      */
24144     // private
24145     
24146     meterWidth: 300,
24147     errorMsg :'',    
24148     errors: false,
24149     imageRoot: '/',
24150     /**
24151      * @cfg {String/Object} Label for the strength meter (defaults to
24152      * 'Password strength:')
24153      */
24154     // private
24155     meterLabel: '',
24156     /**
24157      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24158      * ['Weak', 'Medium', 'Strong'])
24159      */
24160     // private    
24161     pwdStrengths: false,    
24162     // private
24163     strength: 0,
24164     // private
24165     _lastPwd: null,
24166     // private
24167     kCapitalLetter: 0,
24168     kSmallLetter: 1,
24169     kDigit: 2,
24170     kPunctuation: 3,
24171     
24172     insecure: false,
24173     // private
24174     initEvents: function ()
24175     {
24176         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24177
24178         if (this.el.is('input[type=password]') && Roo.isSafari) {
24179             this.el.on('keydown', this.SafariOnKeyDown, this);
24180         }
24181
24182         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24183     },
24184     // private
24185     onRender: function (ct, position)
24186     {
24187         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24188         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24189         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24190
24191         this.trigger.createChild({
24192                    cn: [
24193                     {
24194                     //id: 'PwdMeter',
24195                     tag: 'div',
24196                     cls: 'roo-password-meter-grey col-xs-12',
24197                     style: {
24198                         //width: 0,
24199                         //width: this.meterWidth + 'px'                                                
24200                         }
24201                     },
24202                     {                            
24203                          cls: 'roo-password-meter-text'                          
24204                     }
24205                 ]            
24206         });
24207
24208          
24209         if (this.hideTrigger) {
24210             this.trigger.setDisplayed(false);
24211         }
24212         this.setSize(this.width || '', this.height || '');
24213     },
24214     // private
24215     onDestroy: function ()
24216     {
24217         if (this.trigger) {
24218             this.trigger.removeAllListeners();
24219             this.trigger.remove();
24220         }
24221         if (this.wrap) {
24222             this.wrap.remove();
24223         }
24224         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24225     },
24226     // private
24227     checkStrength: function ()
24228     {
24229         var pwd = this.inputEl().getValue();
24230         if (pwd == this._lastPwd) {
24231             return;
24232         }
24233
24234         var strength;
24235         if (this.ClientSideStrongPassword(pwd)) {
24236             strength = 3;
24237         } else if (this.ClientSideMediumPassword(pwd)) {
24238             strength = 2;
24239         } else if (this.ClientSideWeakPassword(pwd)) {
24240             strength = 1;
24241         } else {
24242             strength = 0;
24243         }
24244         
24245         Roo.log('strength1: ' + strength);
24246         
24247         //var pm = this.trigger.child('div/div/div').dom;
24248         var pm = this.trigger.child('div/div');
24249         pm.removeClass(this.meterClass);
24250         pm.addClass(this.meterClass[strength]);
24251                 
24252         
24253         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24254                 
24255         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24256         
24257         this._lastPwd = pwd;
24258     },
24259     reset: function ()
24260     {
24261         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24262         
24263         this._lastPwd = '';
24264         
24265         var pm = this.trigger.child('div/div');
24266         pm.removeClass(this.meterClass);
24267         pm.addClass('roo-password-meter-grey');        
24268         
24269         
24270         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24271         
24272         pt.innerHTML = '';
24273         this.inputEl().dom.type='password';
24274     },
24275     // private
24276     validateValue: function (value)
24277     {
24278         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24279             return false;
24280         }
24281         if (value.length == 0) {
24282             if (this.allowBlank) {
24283                 this.clearInvalid();
24284                 return true;
24285             }
24286
24287             this.markInvalid(this.errors.PwdEmpty);
24288             this.errorMsg = this.errors.PwdEmpty;
24289             return false;
24290         }
24291         
24292         if(this.insecure){
24293             return true;
24294         }
24295         
24296         if (!value.match(/[\x21-\x7e]+/)) {
24297             this.markInvalid(this.errors.PwdBadChar);
24298             this.errorMsg = this.errors.PwdBadChar;
24299             return false;
24300         }
24301         if (value.length < 6) {
24302             this.markInvalid(this.errors.PwdShort);
24303             this.errorMsg = this.errors.PwdShort;
24304             return false;
24305         }
24306         if (value.length > 16) {
24307             this.markInvalid(this.errors.PwdLong);
24308             this.errorMsg = this.errors.PwdLong;
24309             return false;
24310         }
24311         var strength;
24312         if (this.ClientSideStrongPassword(value)) {
24313             strength = 3;
24314         } else if (this.ClientSideMediumPassword(value)) {
24315             strength = 2;
24316         } else if (this.ClientSideWeakPassword(value)) {
24317             strength = 1;
24318         } else {
24319             strength = 0;
24320         }
24321
24322         
24323         if (strength < 2) {
24324             //this.markInvalid(this.errors.TooWeak);
24325             this.errorMsg = this.errors.TooWeak;
24326             //return false;
24327         }
24328         
24329         
24330         console.log('strength2: ' + strength);
24331         
24332         //var pm = this.trigger.child('div/div/div').dom;
24333         
24334         var pm = this.trigger.child('div/div');
24335         pm.removeClass(this.meterClass);
24336         pm.addClass(this.meterClass[strength]);
24337                 
24338         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24339                 
24340         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24341         
24342         this.errorMsg = ''; 
24343         return true;
24344     },
24345     // private
24346     CharacterSetChecks: function (type)
24347     {
24348         this.type = type;
24349         this.fResult = false;
24350     },
24351     // private
24352     isctype: function (character, type)
24353     {
24354         switch (type) {  
24355             case this.kCapitalLetter:
24356                 if (character >= 'A' && character <= 'Z') {
24357                     return true;
24358                 }
24359                 break;
24360             
24361             case this.kSmallLetter:
24362                 if (character >= 'a' && character <= 'z') {
24363                     return true;
24364                 }
24365                 break;
24366             
24367             case this.kDigit:
24368                 if (character >= '0' && character <= '9') {
24369                     return true;
24370                 }
24371                 break;
24372             
24373             case this.kPunctuation:
24374                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24375                     return true;
24376                 }
24377                 break;
24378             
24379             default:
24380                 return false;
24381         }
24382
24383     },
24384     // private
24385     IsLongEnough: function (pwd, size)
24386     {
24387         return !(pwd == null || isNaN(size) || pwd.length < size);
24388     },
24389     // private
24390     SpansEnoughCharacterSets: function (word, nb)
24391     {
24392         if (!this.IsLongEnough(word, nb))
24393         {
24394             return false;
24395         }
24396
24397         var characterSetChecks = new Array(
24398             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24399             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24400         );
24401         
24402         for (var index = 0; index < word.length; ++index) {
24403             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24404                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24405                     characterSetChecks[nCharSet].fResult = true;
24406                     break;
24407                 }
24408             }
24409         }
24410
24411         var nCharSets = 0;
24412         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24413             if (characterSetChecks[nCharSet].fResult) {
24414                 ++nCharSets;
24415             }
24416         }
24417
24418         if (nCharSets < nb) {
24419             return false;
24420         }
24421         return true;
24422     },
24423     // private
24424     ClientSideStrongPassword: function (pwd)
24425     {
24426         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24427     },
24428     // private
24429     ClientSideMediumPassword: function (pwd)
24430     {
24431         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24432     },
24433     // private
24434     ClientSideWeakPassword: function (pwd)
24435     {
24436         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24437     }
24438           
24439 })//<script type="text/javascript">
24440
24441 /*
24442  * Based  Ext JS Library 1.1.1
24443  * Copyright(c) 2006-2007, Ext JS, LLC.
24444  * LGPL
24445  *
24446  */
24447  
24448 /**
24449  * @class Roo.HtmlEditorCore
24450  * @extends Roo.Component
24451  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24452  *
24453  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24454  */
24455
24456 Roo.HtmlEditorCore = function(config){
24457     
24458     
24459     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24460     
24461     
24462     this.addEvents({
24463         /**
24464          * @event initialize
24465          * Fires when the editor is fully initialized (including the iframe)
24466          * @param {Roo.HtmlEditorCore} this
24467          */
24468         initialize: true,
24469         /**
24470          * @event activate
24471          * Fires when the editor is first receives the focus. Any insertion must wait
24472          * until after this event.
24473          * @param {Roo.HtmlEditorCore} this
24474          */
24475         activate: true,
24476          /**
24477          * @event beforesync
24478          * Fires before the textarea is updated with content from the editor iframe. Return false
24479          * to cancel the sync.
24480          * @param {Roo.HtmlEditorCore} this
24481          * @param {String} html
24482          */
24483         beforesync: true,
24484          /**
24485          * @event beforepush
24486          * Fires before the iframe editor is updated with content from the textarea. Return false
24487          * to cancel the push.
24488          * @param {Roo.HtmlEditorCore} this
24489          * @param {String} html
24490          */
24491         beforepush: true,
24492          /**
24493          * @event sync
24494          * Fires when the textarea is updated with content from the editor iframe.
24495          * @param {Roo.HtmlEditorCore} this
24496          * @param {String} html
24497          */
24498         sync: true,
24499          /**
24500          * @event push
24501          * Fires when the iframe editor is updated with content from the textarea.
24502          * @param {Roo.HtmlEditorCore} this
24503          * @param {String} html
24504          */
24505         push: true,
24506         
24507         /**
24508          * @event editorevent
24509          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24510          * @param {Roo.HtmlEditorCore} this
24511          */
24512         editorevent: true
24513         
24514     });
24515     
24516     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24517     
24518     // defaults : white / black...
24519     this.applyBlacklists();
24520     
24521     
24522     
24523 };
24524
24525
24526 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24527
24528
24529      /**
24530      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24531      */
24532     
24533     owner : false,
24534     
24535      /**
24536      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24537      *                        Roo.resizable.
24538      */
24539     resizable : false,
24540      /**
24541      * @cfg {Number} height (in pixels)
24542      */   
24543     height: 300,
24544    /**
24545      * @cfg {Number} width (in pixels)
24546      */   
24547     width: 500,
24548     
24549     /**
24550      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24551      * 
24552      */
24553     stylesheets: false,
24554     
24555     // id of frame..
24556     frameId: false,
24557     
24558     // private properties
24559     validationEvent : false,
24560     deferHeight: true,
24561     initialized : false,
24562     activated : false,
24563     sourceEditMode : false,
24564     onFocus : Roo.emptyFn,
24565     iframePad:3,
24566     hideMode:'offsets',
24567     
24568     clearUp: true,
24569     
24570     // blacklist + whitelisted elements..
24571     black: false,
24572     white: false,
24573      
24574     bodyCls : '',
24575
24576     /**
24577      * Protected method that will not generally be called directly. It
24578      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24579      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24580      */
24581     getDocMarkup : function(){
24582         // body styles..
24583         var st = '';
24584         
24585         // inherit styels from page...?? 
24586         if (this.stylesheets === false) {
24587             
24588             Roo.get(document.head).select('style').each(function(node) {
24589                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24590             });
24591             
24592             Roo.get(document.head).select('link').each(function(node) { 
24593                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24594             });
24595             
24596         } else if (!this.stylesheets.length) {
24597                 // simple..
24598                 st = '<style type="text/css">' +
24599                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24600                    '</style>';
24601         } else {
24602             for (var i in this.stylesheets) { 
24603                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24604             }
24605             
24606         }
24607         
24608         st +=  '<style type="text/css">' +
24609             'IMG { cursor: pointer } ' +
24610         '</style>';
24611
24612         var cls = 'roo-htmleditor-body';
24613         
24614         if(this.bodyCls.length){
24615             cls += ' ' + this.bodyCls;
24616         }
24617         
24618         return '<html><head>' + st  +
24619             //<style type="text/css">' +
24620             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24621             //'</style>' +
24622             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24623     },
24624
24625     // private
24626     onRender : function(ct, position)
24627     {
24628         var _t = this;
24629         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24630         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24631         
24632         
24633         this.el.dom.style.border = '0 none';
24634         this.el.dom.setAttribute('tabIndex', -1);
24635         this.el.addClass('x-hidden hide');
24636         
24637         
24638         
24639         if(Roo.isIE){ // fix IE 1px bogus margin
24640             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24641         }
24642        
24643         
24644         this.frameId = Roo.id();
24645         
24646          
24647         
24648         var iframe = this.owner.wrap.createChild({
24649             tag: 'iframe',
24650             cls: 'form-control', // bootstrap..
24651             id: this.frameId,
24652             name: this.frameId,
24653             frameBorder : 'no',
24654             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24655         }, this.el
24656         );
24657         
24658         
24659         this.iframe = iframe.dom;
24660
24661          this.assignDocWin();
24662         
24663         this.doc.designMode = 'on';
24664        
24665         this.doc.open();
24666         this.doc.write(this.getDocMarkup());
24667         this.doc.close();
24668
24669         
24670         var task = { // must defer to wait for browser to be ready
24671             run : function(){
24672                 //console.log("run task?" + this.doc.readyState);
24673                 this.assignDocWin();
24674                 if(this.doc.body || this.doc.readyState == 'complete'){
24675                     try {
24676                         this.doc.designMode="on";
24677                     } catch (e) {
24678                         return;
24679                     }
24680                     Roo.TaskMgr.stop(task);
24681                     this.initEditor.defer(10, this);
24682                 }
24683             },
24684             interval : 10,
24685             duration: 10000,
24686             scope: this
24687         };
24688         Roo.TaskMgr.start(task);
24689
24690     },
24691
24692     // private
24693     onResize : function(w, h)
24694     {
24695          Roo.log('resize: ' +w + ',' + h );
24696         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24697         if(!this.iframe){
24698             return;
24699         }
24700         if(typeof w == 'number'){
24701             
24702             this.iframe.style.width = w + 'px';
24703         }
24704         if(typeof h == 'number'){
24705             
24706             this.iframe.style.height = h + 'px';
24707             if(this.doc){
24708                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24709             }
24710         }
24711         
24712     },
24713
24714     /**
24715      * Toggles the editor between standard and source edit mode.
24716      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24717      */
24718     toggleSourceEdit : function(sourceEditMode){
24719         
24720         this.sourceEditMode = sourceEditMode === true;
24721         
24722         if(this.sourceEditMode){
24723  
24724             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24725             
24726         }else{
24727             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24728             //this.iframe.className = '';
24729             this.deferFocus();
24730         }
24731         //this.setSize(this.owner.wrap.getSize());
24732         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24733     },
24734
24735     
24736   
24737
24738     /**
24739      * Protected method that will not generally be called directly. If you need/want
24740      * custom HTML cleanup, this is the method you should override.
24741      * @param {String} html The HTML to be cleaned
24742      * return {String} The cleaned HTML
24743      */
24744     cleanHtml : function(html){
24745         html = String(html);
24746         if(html.length > 5){
24747             if(Roo.isSafari){ // strip safari nonsense
24748                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24749             }
24750         }
24751         if(html == '&nbsp;'){
24752             html = '';
24753         }
24754         return html;
24755     },
24756
24757     /**
24758      * HTML Editor -> Textarea
24759      * Protected method that will not generally be called directly. Syncs the contents
24760      * of the editor iframe with the textarea.
24761      */
24762     syncValue : function(){
24763         if(this.initialized){
24764             var bd = (this.doc.body || this.doc.documentElement);
24765             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24766             var html = bd.innerHTML;
24767             if(Roo.isSafari){
24768                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24769                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24770                 if(m && m[1]){
24771                     html = '<div style="'+m[0]+'">' + html + '</div>';
24772                 }
24773             }
24774             html = this.cleanHtml(html);
24775             // fix up the special chars.. normaly like back quotes in word...
24776             // however we do not want to do this with chinese..
24777             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24778                 
24779                 var cc = match.charCodeAt();
24780
24781                 // Get the character value, handling surrogate pairs
24782                 if (match.length == 2) {
24783                     // It's a surrogate pair, calculate the Unicode code point
24784                     var high = match.charCodeAt(0) - 0xD800;
24785                     var low  = match.charCodeAt(1) - 0xDC00;
24786                     cc = (high * 0x400) + low + 0x10000;
24787                 }  else if (
24788                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24789                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24790                     (cc >= 0xf900 && cc < 0xfb00 )
24791                 ) {
24792                         return match;
24793                 }  
24794          
24795                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24796                 return "&#" + cc + ";";
24797                 
24798                 
24799             });
24800             
24801             
24802              
24803             if(this.owner.fireEvent('beforesync', this, html) !== false){
24804                 this.el.dom.value = html;
24805                 this.owner.fireEvent('sync', this, html);
24806             }
24807         }
24808     },
24809
24810     /**
24811      * Protected method that will not generally be called directly. Pushes the value of the textarea
24812      * into the iframe editor.
24813      */
24814     pushValue : function(){
24815         if(this.initialized){
24816             var v = this.el.dom.value.trim();
24817             
24818 //            if(v.length < 1){
24819 //                v = '&#160;';
24820 //            }
24821             
24822             if(this.owner.fireEvent('beforepush', this, v) !== false){
24823                 var d = (this.doc.body || this.doc.documentElement);
24824                 d.innerHTML = v;
24825                 this.cleanUpPaste();
24826                 this.el.dom.value = d.innerHTML;
24827                 this.owner.fireEvent('push', this, v);
24828             }
24829         }
24830     },
24831
24832     // private
24833     deferFocus : function(){
24834         this.focus.defer(10, this);
24835     },
24836
24837     // doc'ed in Field
24838     focus : function(){
24839         if(this.win && !this.sourceEditMode){
24840             this.win.focus();
24841         }else{
24842             this.el.focus();
24843         }
24844     },
24845     
24846     assignDocWin: function()
24847     {
24848         var iframe = this.iframe;
24849         
24850          if(Roo.isIE){
24851             this.doc = iframe.contentWindow.document;
24852             this.win = iframe.contentWindow;
24853         } else {
24854 //            if (!Roo.get(this.frameId)) {
24855 //                return;
24856 //            }
24857 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24858 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24859             
24860             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24861                 return;
24862             }
24863             
24864             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24865             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24866         }
24867     },
24868     
24869     // private
24870     initEditor : function(){
24871         //console.log("INIT EDITOR");
24872         this.assignDocWin();
24873         
24874         
24875         
24876         this.doc.designMode="on";
24877         this.doc.open();
24878         this.doc.write(this.getDocMarkup());
24879         this.doc.close();
24880         
24881         var dbody = (this.doc.body || this.doc.documentElement);
24882         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24883         // this copies styles from the containing element into thsi one..
24884         // not sure why we need all of this..
24885         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24886         
24887         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24888         //ss['background-attachment'] = 'fixed'; // w3c
24889         dbody.bgProperties = 'fixed'; // ie
24890         //Roo.DomHelper.applyStyles(dbody, ss);
24891         Roo.EventManager.on(this.doc, {
24892             //'mousedown': this.onEditorEvent,
24893             'mouseup': this.onEditorEvent,
24894             'dblclick': this.onEditorEvent,
24895             'click': this.onEditorEvent,
24896             'keyup': this.onEditorEvent,
24897             buffer:100,
24898             scope: this
24899         });
24900         if(Roo.isGecko){
24901             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24902         }
24903         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24904             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24905         }
24906         this.initialized = true;
24907
24908         this.owner.fireEvent('initialize', this);
24909         this.pushValue();
24910     },
24911
24912     // private
24913     onDestroy : function(){
24914         
24915         
24916         
24917         if(this.rendered){
24918             
24919             //for (var i =0; i < this.toolbars.length;i++) {
24920             //    // fixme - ask toolbars for heights?
24921             //    this.toolbars[i].onDestroy();
24922            // }
24923             
24924             //this.wrap.dom.innerHTML = '';
24925             //this.wrap.remove();
24926         }
24927     },
24928
24929     // private
24930     onFirstFocus : function(){
24931         
24932         this.assignDocWin();
24933         
24934         
24935         this.activated = true;
24936          
24937     
24938         if(Roo.isGecko){ // prevent silly gecko errors
24939             this.win.focus();
24940             var s = this.win.getSelection();
24941             if(!s.focusNode || s.focusNode.nodeType != 3){
24942                 var r = s.getRangeAt(0);
24943                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24944                 r.collapse(true);
24945                 this.deferFocus();
24946             }
24947             try{
24948                 this.execCmd('useCSS', true);
24949                 this.execCmd('styleWithCSS', false);
24950             }catch(e){}
24951         }
24952         this.owner.fireEvent('activate', this);
24953     },
24954
24955     // private
24956     adjustFont: function(btn){
24957         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24958         //if(Roo.isSafari){ // safari
24959         //    adjust *= 2;
24960        // }
24961         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24962         if(Roo.isSafari){ // safari
24963             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24964             v =  (v < 10) ? 10 : v;
24965             v =  (v > 48) ? 48 : v;
24966             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24967             
24968         }
24969         
24970         
24971         v = Math.max(1, v+adjust);
24972         
24973         this.execCmd('FontSize', v  );
24974     },
24975
24976     onEditorEvent : function(e)
24977     {
24978         this.owner.fireEvent('editorevent', this, e);
24979       //  this.updateToolbar();
24980         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24981     },
24982
24983     insertTag : function(tg)
24984     {
24985         // could be a bit smarter... -> wrap the current selected tRoo..
24986         if (tg.toLowerCase() == 'span' ||
24987             tg.toLowerCase() == 'code' ||
24988             tg.toLowerCase() == 'sup' ||
24989             tg.toLowerCase() == 'sub' 
24990             ) {
24991             
24992             range = this.createRange(this.getSelection());
24993             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24994             wrappingNode.appendChild(range.extractContents());
24995             range.insertNode(wrappingNode);
24996
24997             return;
24998             
24999             
25000             
25001         }
25002         this.execCmd("formatblock",   tg);
25003         
25004     },
25005     
25006     insertText : function(txt)
25007     {
25008         
25009         
25010         var range = this.createRange();
25011         range.deleteContents();
25012                //alert(Sender.getAttribute('label'));
25013                
25014         range.insertNode(this.doc.createTextNode(txt));
25015     } ,
25016     
25017      
25018
25019     /**
25020      * Executes a Midas editor command on the editor document and performs necessary focus and
25021      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25022      * @param {String} cmd The Midas command
25023      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25024      */
25025     relayCmd : function(cmd, value){
25026         this.win.focus();
25027         this.execCmd(cmd, value);
25028         this.owner.fireEvent('editorevent', this);
25029         //this.updateToolbar();
25030         this.owner.deferFocus();
25031     },
25032
25033     /**
25034      * Executes a Midas editor command directly on the editor document.
25035      * For visual commands, you should use {@link #relayCmd} instead.
25036      * <b>This should only be called after the editor is initialized.</b>
25037      * @param {String} cmd The Midas command
25038      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25039      */
25040     execCmd : function(cmd, value){
25041         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25042         this.syncValue();
25043     },
25044  
25045  
25046    
25047     /**
25048      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25049      * to insert tRoo.
25050      * @param {String} text | dom node.. 
25051      */
25052     insertAtCursor : function(text)
25053     {
25054         
25055         if(!this.activated){
25056             return;
25057         }
25058         /*
25059         if(Roo.isIE){
25060             this.win.focus();
25061             var r = this.doc.selection.createRange();
25062             if(r){
25063                 r.collapse(true);
25064                 r.pasteHTML(text);
25065                 this.syncValue();
25066                 this.deferFocus();
25067             
25068             }
25069             return;
25070         }
25071         */
25072         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25073             this.win.focus();
25074             
25075             
25076             // from jquery ui (MIT licenced)
25077             var range, node;
25078             var win = this.win;
25079             
25080             if (win.getSelection && win.getSelection().getRangeAt) {
25081                 range = win.getSelection().getRangeAt(0);
25082                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25083                 range.insertNode(node);
25084             } else if (win.document.selection && win.document.selection.createRange) {
25085                 // no firefox support
25086                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25087                 win.document.selection.createRange().pasteHTML(txt);
25088             } else {
25089                 // no firefox support
25090                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25091                 this.execCmd('InsertHTML', txt);
25092             } 
25093             
25094             this.syncValue();
25095             
25096             this.deferFocus();
25097         }
25098     },
25099  // private
25100     mozKeyPress : function(e){
25101         if(e.ctrlKey){
25102             var c = e.getCharCode(), cmd;
25103           
25104             if(c > 0){
25105                 c = String.fromCharCode(c).toLowerCase();
25106                 switch(c){
25107                     case 'b':
25108                         cmd = 'bold';
25109                         break;
25110                     case 'i':
25111                         cmd = 'italic';
25112                         break;
25113                     
25114                     case 'u':
25115                         cmd = 'underline';
25116                         break;
25117                     
25118                     case 'v':
25119                         this.cleanUpPaste.defer(100, this);
25120                         return;
25121                         
25122                 }
25123                 if(cmd){
25124                     this.win.focus();
25125                     this.execCmd(cmd);
25126                     this.deferFocus();
25127                     e.preventDefault();
25128                 }
25129                 
25130             }
25131         }
25132     },
25133
25134     // private
25135     fixKeys : function(){ // load time branching for fastest keydown performance
25136         if(Roo.isIE){
25137             return function(e){
25138                 var k = e.getKey(), r;
25139                 if(k == e.TAB){
25140                     e.stopEvent();
25141                     r = this.doc.selection.createRange();
25142                     if(r){
25143                         r.collapse(true);
25144                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25145                         this.deferFocus();
25146                     }
25147                     return;
25148                 }
25149                 
25150                 if(k == e.ENTER){
25151                     r = this.doc.selection.createRange();
25152                     if(r){
25153                         var target = r.parentElement();
25154                         if(!target || target.tagName.toLowerCase() != 'li'){
25155                             e.stopEvent();
25156                             r.pasteHTML('<br />');
25157                             r.collapse(false);
25158                             r.select();
25159                         }
25160                     }
25161                 }
25162                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25163                     this.cleanUpPaste.defer(100, this);
25164                     return;
25165                 }
25166                 
25167                 
25168             };
25169         }else if(Roo.isOpera){
25170             return function(e){
25171                 var k = e.getKey();
25172                 if(k == e.TAB){
25173                     e.stopEvent();
25174                     this.win.focus();
25175                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25176                     this.deferFocus();
25177                 }
25178                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25179                     this.cleanUpPaste.defer(100, this);
25180                     return;
25181                 }
25182                 
25183             };
25184         }else if(Roo.isSafari){
25185             return function(e){
25186                 var k = e.getKey();
25187                 
25188                 if(k == e.TAB){
25189                     e.stopEvent();
25190                     this.execCmd('InsertText','\t');
25191                     this.deferFocus();
25192                     return;
25193                 }
25194                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25195                     this.cleanUpPaste.defer(100, this);
25196                     return;
25197                 }
25198                 
25199              };
25200         }
25201     }(),
25202     
25203     getAllAncestors: function()
25204     {
25205         var p = this.getSelectedNode();
25206         var a = [];
25207         if (!p) {
25208             a.push(p); // push blank onto stack..
25209             p = this.getParentElement();
25210         }
25211         
25212         
25213         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25214             a.push(p);
25215             p = p.parentNode;
25216         }
25217         a.push(this.doc.body);
25218         return a;
25219     },
25220     lastSel : false,
25221     lastSelNode : false,
25222     
25223     
25224     getSelection : function() 
25225     {
25226         this.assignDocWin();
25227         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25228     },
25229     
25230     getSelectedNode: function() 
25231     {
25232         // this may only work on Gecko!!!
25233         
25234         // should we cache this!!!!
25235         
25236         
25237         
25238          
25239         var range = this.createRange(this.getSelection()).cloneRange();
25240         
25241         if (Roo.isIE) {
25242             var parent = range.parentElement();
25243             while (true) {
25244                 var testRange = range.duplicate();
25245                 testRange.moveToElementText(parent);
25246                 if (testRange.inRange(range)) {
25247                     break;
25248                 }
25249                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25250                     break;
25251                 }
25252                 parent = parent.parentElement;
25253             }
25254             return parent;
25255         }
25256         
25257         // is ancestor a text element.
25258         var ac =  range.commonAncestorContainer;
25259         if (ac.nodeType == 3) {
25260             ac = ac.parentNode;
25261         }
25262         
25263         var ar = ac.childNodes;
25264          
25265         var nodes = [];
25266         var other_nodes = [];
25267         var has_other_nodes = false;
25268         for (var i=0;i<ar.length;i++) {
25269             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25270                 continue;
25271             }
25272             // fullly contained node.
25273             
25274             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25275                 nodes.push(ar[i]);
25276                 continue;
25277             }
25278             
25279             // probably selected..
25280             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25281                 other_nodes.push(ar[i]);
25282                 continue;
25283             }
25284             // outer..
25285             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25286                 continue;
25287             }
25288             
25289             
25290             has_other_nodes = true;
25291         }
25292         if (!nodes.length && other_nodes.length) {
25293             nodes= other_nodes;
25294         }
25295         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25296             return false;
25297         }
25298         
25299         return nodes[0];
25300     },
25301     createRange: function(sel)
25302     {
25303         // this has strange effects when using with 
25304         // top toolbar - not sure if it's a great idea.
25305         //this.editor.contentWindow.focus();
25306         if (typeof sel != "undefined") {
25307             try {
25308                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25309             } catch(e) {
25310                 return this.doc.createRange();
25311             }
25312         } else {
25313             return this.doc.createRange();
25314         }
25315     },
25316     getParentElement: function()
25317     {
25318         
25319         this.assignDocWin();
25320         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25321         
25322         var range = this.createRange(sel);
25323          
25324         try {
25325             var p = range.commonAncestorContainer;
25326             while (p.nodeType == 3) { // text node
25327                 p = p.parentNode;
25328             }
25329             return p;
25330         } catch (e) {
25331             return null;
25332         }
25333     
25334     },
25335     /***
25336      *
25337      * Range intersection.. the hard stuff...
25338      *  '-1' = before
25339      *  '0' = hits..
25340      *  '1' = after.
25341      *         [ -- selected range --- ]
25342      *   [fail]                        [fail]
25343      *
25344      *    basically..
25345      *      if end is before start or  hits it. fail.
25346      *      if start is after end or hits it fail.
25347      *
25348      *   if either hits (but other is outside. - then it's not 
25349      *   
25350      *    
25351      **/
25352     
25353     
25354     // @see http://www.thismuchiknow.co.uk/?p=64.
25355     rangeIntersectsNode : function(range, node)
25356     {
25357         var nodeRange = node.ownerDocument.createRange();
25358         try {
25359             nodeRange.selectNode(node);
25360         } catch (e) {
25361             nodeRange.selectNodeContents(node);
25362         }
25363     
25364         var rangeStartRange = range.cloneRange();
25365         rangeStartRange.collapse(true);
25366     
25367         var rangeEndRange = range.cloneRange();
25368         rangeEndRange.collapse(false);
25369     
25370         var nodeStartRange = nodeRange.cloneRange();
25371         nodeStartRange.collapse(true);
25372     
25373         var nodeEndRange = nodeRange.cloneRange();
25374         nodeEndRange.collapse(false);
25375     
25376         return rangeStartRange.compareBoundaryPoints(
25377                  Range.START_TO_START, nodeEndRange) == -1 &&
25378                rangeEndRange.compareBoundaryPoints(
25379                  Range.START_TO_START, nodeStartRange) == 1;
25380         
25381          
25382     },
25383     rangeCompareNode : function(range, node)
25384     {
25385         var nodeRange = node.ownerDocument.createRange();
25386         try {
25387             nodeRange.selectNode(node);
25388         } catch (e) {
25389             nodeRange.selectNodeContents(node);
25390         }
25391         
25392         
25393         range.collapse(true);
25394     
25395         nodeRange.collapse(true);
25396      
25397         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25398         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25399          
25400         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25401         
25402         var nodeIsBefore   =  ss == 1;
25403         var nodeIsAfter    = ee == -1;
25404         
25405         if (nodeIsBefore && nodeIsAfter) {
25406             return 0; // outer
25407         }
25408         if (!nodeIsBefore && nodeIsAfter) {
25409             return 1; //right trailed.
25410         }
25411         
25412         if (nodeIsBefore && !nodeIsAfter) {
25413             return 2;  // left trailed.
25414         }
25415         // fully contined.
25416         return 3;
25417     },
25418
25419     // private? - in a new class?
25420     cleanUpPaste :  function()
25421     {
25422         // cleans up the whole document..
25423         Roo.log('cleanuppaste');
25424         
25425         this.cleanUpChildren(this.doc.body);
25426         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25427         if (clean != this.doc.body.innerHTML) {
25428             this.doc.body.innerHTML = clean;
25429         }
25430         
25431     },
25432     
25433     cleanWordChars : function(input) {// change the chars to hex code
25434         var he = Roo.HtmlEditorCore;
25435         
25436         var output = input;
25437         Roo.each(he.swapCodes, function(sw) { 
25438             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25439             
25440             output = output.replace(swapper, sw[1]);
25441         });
25442         
25443         return output;
25444     },
25445     
25446     
25447     cleanUpChildren : function (n)
25448     {
25449         if (!n.childNodes.length) {
25450             return;
25451         }
25452         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25453            this.cleanUpChild(n.childNodes[i]);
25454         }
25455     },
25456     
25457     
25458         
25459     
25460     cleanUpChild : function (node)
25461     {
25462         var ed = this;
25463         //console.log(node);
25464         if (node.nodeName == "#text") {
25465             // clean up silly Windows -- stuff?
25466             return; 
25467         }
25468         if (node.nodeName == "#comment") {
25469             node.parentNode.removeChild(node);
25470             // clean up silly Windows -- stuff?
25471             return; 
25472         }
25473         var lcname = node.tagName.toLowerCase();
25474         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25475         // whitelist of tags..
25476         
25477         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25478             // remove node.
25479             node.parentNode.removeChild(node);
25480             return;
25481             
25482         }
25483         
25484         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25485         
25486         // spans with no attributes - just remove them..
25487         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25488             remove_keep_children = true;
25489         }
25490         
25491         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25492         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25493         
25494         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25495         //    remove_keep_children = true;
25496         //}
25497         
25498         if (remove_keep_children) {
25499             this.cleanUpChildren(node);
25500             // inserts everything just before this node...
25501             while (node.childNodes.length) {
25502                 var cn = node.childNodes[0];
25503                 node.removeChild(cn);
25504                 node.parentNode.insertBefore(cn, node);
25505             }
25506             node.parentNode.removeChild(node);
25507             return;
25508         }
25509         
25510         if (!node.attributes || !node.attributes.length) {
25511             
25512           
25513             
25514             
25515             this.cleanUpChildren(node);
25516             return;
25517         }
25518         
25519         function cleanAttr(n,v)
25520         {
25521             
25522             if (v.match(/^\./) || v.match(/^\//)) {
25523                 return;
25524             }
25525             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25526                 return;
25527             }
25528             if (v.match(/^#/)) {
25529                 return;
25530             }
25531             if (v.match(/^\{/)) { // allow template editing.
25532                 return;
25533             }
25534 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25535             node.removeAttribute(n);
25536             
25537         }
25538         
25539         var cwhite = this.cwhite;
25540         var cblack = this.cblack;
25541             
25542         function cleanStyle(n,v)
25543         {
25544             if (v.match(/expression/)) { //XSS?? should we even bother..
25545                 node.removeAttribute(n);
25546                 return;
25547             }
25548             
25549             var parts = v.split(/;/);
25550             var clean = [];
25551             
25552             Roo.each(parts, function(p) {
25553                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25554                 if (!p.length) {
25555                     return true;
25556                 }
25557                 var l = p.split(':').shift().replace(/\s+/g,'');
25558                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25559                 
25560                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25561 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25562                     //node.removeAttribute(n);
25563                     return true;
25564                 }
25565                 //Roo.log()
25566                 // only allow 'c whitelisted system attributes'
25567                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25568 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25569                     //node.removeAttribute(n);
25570                     return true;
25571                 }
25572                 
25573                 
25574                  
25575                 
25576                 clean.push(p);
25577                 return true;
25578             });
25579             if (clean.length) { 
25580                 node.setAttribute(n, clean.join(';'));
25581             } else {
25582                 node.removeAttribute(n);
25583             }
25584             
25585         }
25586         
25587         
25588         for (var i = node.attributes.length-1; i > -1 ; i--) {
25589             var a = node.attributes[i];
25590             //console.log(a);
25591             
25592             if (a.name.toLowerCase().substr(0,2)=='on')  {
25593                 node.removeAttribute(a.name);
25594                 continue;
25595             }
25596             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25597                 node.removeAttribute(a.name);
25598                 continue;
25599             }
25600             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25601                 cleanAttr(a.name,a.value); // fixme..
25602                 continue;
25603             }
25604             if (a.name == 'style') {
25605                 cleanStyle(a.name,a.value);
25606                 continue;
25607             }
25608             /// clean up MS crap..
25609             // tecnically this should be a list of valid class'es..
25610             
25611             
25612             if (a.name == 'class') {
25613                 if (a.value.match(/^Mso/)) {
25614                     node.removeAttribute('class');
25615                 }
25616                 
25617                 if (a.value.match(/^body$/)) {
25618                     node.removeAttribute('class');
25619                 }
25620                 continue;
25621             }
25622             
25623             // style cleanup!?
25624             // class cleanup?
25625             
25626         }
25627         
25628         
25629         this.cleanUpChildren(node);
25630         
25631         
25632     },
25633     
25634     /**
25635      * Clean up MS wordisms...
25636      */
25637     cleanWord : function(node)
25638     {
25639         if (!node) {
25640             this.cleanWord(this.doc.body);
25641             return;
25642         }
25643         
25644         if(
25645                 node.nodeName == 'SPAN' &&
25646                 !node.hasAttributes() &&
25647                 node.childNodes.length == 1 &&
25648                 node.firstChild.nodeName == "#text"  
25649         ) {
25650             var textNode = node.firstChild;
25651             node.removeChild(textNode);
25652             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25653                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25654             }
25655             node.parentNode.insertBefore(textNode, node);
25656             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25657                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25658             }
25659             node.parentNode.removeChild(node);
25660         }
25661         
25662         if (node.nodeName == "#text") {
25663             // clean up silly Windows -- stuff?
25664             return; 
25665         }
25666         if (node.nodeName == "#comment") {
25667             node.parentNode.removeChild(node);
25668             // clean up silly Windows -- stuff?
25669             return; 
25670         }
25671         
25672         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25673             node.parentNode.removeChild(node);
25674             return;
25675         }
25676         //Roo.log(node.tagName);
25677         // remove - but keep children..
25678         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25679             //Roo.log('-- removed');
25680             while (node.childNodes.length) {
25681                 var cn = node.childNodes[0];
25682                 node.removeChild(cn);
25683                 node.parentNode.insertBefore(cn, node);
25684                 // move node to parent - and clean it..
25685                 this.cleanWord(cn);
25686             }
25687             node.parentNode.removeChild(node);
25688             /// no need to iterate chidlren = it's got none..
25689             //this.iterateChildren(node, this.cleanWord);
25690             return;
25691         }
25692         // clean styles
25693         if (node.className.length) {
25694             
25695             var cn = node.className.split(/\W+/);
25696             var cna = [];
25697             Roo.each(cn, function(cls) {
25698                 if (cls.match(/Mso[a-zA-Z]+/)) {
25699                     return;
25700                 }
25701                 cna.push(cls);
25702             });
25703             node.className = cna.length ? cna.join(' ') : '';
25704             if (!cna.length) {
25705                 node.removeAttribute("class");
25706             }
25707         }
25708         
25709         if (node.hasAttribute("lang")) {
25710             node.removeAttribute("lang");
25711         }
25712         
25713         if (node.hasAttribute("style")) {
25714             
25715             var styles = node.getAttribute("style").split(";");
25716             var nstyle = [];
25717             Roo.each(styles, function(s) {
25718                 if (!s.match(/:/)) {
25719                     return;
25720                 }
25721                 var kv = s.split(":");
25722                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25723                     return;
25724                 }
25725                 // what ever is left... we allow.
25726                 nstyle.push(s);
25727             });
25728             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25729             if (!nstyle.length) {
25730                 node.removeAttribute('style');
25731             }
25732         }
25733         this.iterateChildren(node, this.cleanWord);
25734         
25735         
25736         
25737     },
25738     /**
25739      * iterateChildren of a Node, calling fn each time, using this as the scole..
25740      * @param {DomNode} node node to iterate children of.
25741      * @param {Function} fn method of this class to call on each item.
25742      */
25743     iterateChildren : function(node, fn)
25744     {
25745         if (!node.childNodes.length) {
25746                 return;
25747         }
25748         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25749            fn.call(this, node.childNodes[i])
25750         }
25751     },
25752     
25753     
25754     /**
25755      * cleanTableWidths.
25756      *
25757      * Quite often pasting from word etc.. results in tables with column and widths.
25758      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25759      *
25760      */
25761     cleanTableWidths : function(node)
25762     {
25763          
25764          
25765         if (!node) {
25766             this.cleanTableWidths(this.doc.body);
25767             return;
25768         }
25769         
25770         // ignore list...
25771         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25772             return; 
25773         }
25774         Roo.log(node.tagName);
25775         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25776             this.iterateChildren(node, this.cleanTableWidths);
25777             return;
25778         }
25779         if (node.hasAttribute('width')) {
25780             node.removeAttribute('width');
25781         }
25782         
25783          
25784         if (node.hasAttribute("style")) {
25785             // pretty basic...
25786             
25787             var styles = node.getAttribute("style").split(";");
25788             var nstyle = [];
25789             Roo.each(styles, function(s) {
25790                 if (!s.match(/:/)) {
25791                     return;
25792                 }
25793                 var kv = s.split(":");
25794                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25795                     return;
25796                 }
25797                 // what ever is left... we allow.
25798                 nstyle.push(s);
25799             });
25800             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25801             if (!nstyle.length) {
25802                 node.removeAttribute('style');
25803             }
25804         }
25805         
25806         this.iterateChildren(node, this.cleanTableWidths);
25807         
25808         
25809     },
25810     
25811     
25812     
25813     
25814     domToHTML : function(currentElement, depth, nopadtext) {
25815         
25816         depth = depth || 0;
25817         nopadtext = nopadtext || false;
25818     
25819         if (!currentElement) {
25820             return this.domToHTML(this.doc.body);
25821         }
25822         
25823         //Roo.log(currentElement);
25824         var j;
25825         var allText = false;
25826         var nodeName = currentElement.nodeName;
25827         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25828         
25829         if  (nodeName == '#text') {
25830             
25831             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25832         }
25833         
25834         
25835         var ret = '';
25836         if (nodeName != 'BODY') {
25837              
25838             var i = 0;
25839             // Prints the node tagName, such as <A>, <IMG>, etc
25840             if (tagName) {
25841                 var attr = [];
25842                 for(i = 0; i < currentElement.attributes.length;i++) {
25843                     // quoting?
25844                     var aname = currentElement.attributes.item(i).name;
25845                     if (!currentElement.attributes.item(i).value.length) {
25846                         continue;
25847                     }
25848                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25849                 }
25850                 
25851                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25852             } 
25853             else {
25854                 
25855                 // eack
25856             }
25857         } else {
25858             tagName = false;
25859         }
25860         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25861             return ret;
25862         }
25863         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25864             nopadtext = true;
25865         }
25866         
25867         
25868         // Traverse the tree
25869         i = 0;
25870         var currentElementChild = currentElement.childNodes.item(i);
25871         var allText = true;
25872         var innerHTML  = '';
25873         lastnode = '';
25874         while (currentElementChild) {
25875             // Formatting code (indent the tree so it looks nice on the screen)
25876             var nopad = nopadtext;
25877             if (lastnode == 'SPAN') {
25878                 nopad  = true;
25879             }
25880             // text
25881             if  (currentElementChild.nodeName == '#text') {
25882                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25883                 toadd = nopadtext ? toadd : toadd.trim();
25884                 if (!nopad && toadd.length > 80) {
25885                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25886                 }
25887                 innerHTML  += toadd;
25888                 
25889                 i++;
25890                 currentElementChild = currentElement.childNodes.item(i);
25891                 lastNode = '';
25892                 continue;
25893             }
25894             allText = false;
25895             
25896             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25897                 
25898             // Recursively traverse the tree structure of the child node
25899             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25900             lastnode = currentElementChild.nodeName;
25901             i++;
25902             currentElementChild=currentElement.childNodes.item(i);
25903         }
25904         
25905         ret += innerHTML;
25906         
25907         if (!allText) {
25908                 // The remaining code is mostly for formatting the tree
25909             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25910         }
25911         
25912         
25913         if (tagName) {
25914             ret+= "</"+tagName+">";
25915         }
25916         return ret;
25917         
25918     },
25919         
25920     applyBlacklists : function()
25921     {
25922         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25923         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25924         
25925         this.white = [];
25926         this.black = [];
25927         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25928             if (b.indexOf(tag) > -1) {
25929                 return;
25930             }
25931             this.white.push(tag);
25932             
25933         }, this);
25934         
25935         Roo.each(w, function(tag) {
25936             if (b.indexOf(tag) > -1) {
25937                 return;
25938             }
25939             if (this.white.indexOf(tag) > -1) {
25940                 return;
25941             }
25942             this.white.push(tag);
25943             
25944         }, this);
25945         
25946         
25947         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25948             if (w.indexOf(tag) > -1) {
25949                 return;
25950             }
25951             this.black.push(tag);
25952             
25953         }, this);
25954         
25955         Roo.each(b, function(tag) {
25956             if (w.indexOf(tag) > -1) {
25957                 return;
25958             }
25959             if (this.black.indexOf(tag) > -1) {
25960                 return;
25961             }
25962             this.black.push(tag);
25963             
25964         }, this);
25965         
25966         
25967         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25968         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25969         
25970         this.cwhite = [];
25971         this.cblack = [];
25972         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25973             if (b.indexOf(tag) > -1) {
25974                 return;
25975             }
25976             this.cwhite.push(tag);
25977             
25978         }, this);
25979         
25980         Roo.each(w, function(tag) {
25981             if (b.indexOf(tag) > -1) {
25982                 return;
25983             }
25984             if (this.cwhite.indexOf(tag) > -1) {
25985                 return;
25986             }
25987             this.cwhite.push(tag);
25988             
25989         }, this);
25990         
25991         
25992         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25993             if (w.indexOf(tag) > -1) {
25994                 return;
25995             }
25996             this.cblack.push(tag);
25997             
25998         }, this);
25999         
26000         Roo.each(b, function(tag) {
26001             if (w.indexOf(tag) > -1) {
26002                 return;
26003             }
26004             if (this.cblack.indexOf(tag) > -1) {
26005                 return;
26006             }
26007             this.cblack.push(tag);
26008             
26009         }, this);
26010     },
26011     
26012     setStylesheets : function(stylesheets)
26013     {
26014         if(typeof(stylesheets) == 'string'){
26015             Roo.get(this.iframe.contentDocument.head).createChild({
26016                 tag : 'link',
26017                 rel : 'stylesheet',
26018                 type : 'text/css',
26019                 href : stylesheets
26020             });
26021             
26022             return;
26023         }
26024         var _this = this;
26025      
26026         Roo.each(stylesheets, function(s) {
26027             if(!s.length){
26028                 return;
26029             }
26030             
26031             Roo.get(_this.iframe.contentDocument.head).createChild({
26032                 tag : 'link',
26033                 rel : 'stylesheet',
26034                 type : 'text/css',
26035                 href : s
26036             });
26037         });
26038
26039         
26040     },
26041     
26042     removeStylesheets : function()
26043     {
26044         var _this = this;
26045         
26046         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26047             s.remove();
26048         });
26049     },
26050     
26051     setStyle : function(style)
26052     {
26053         Roo.get(this.iframe.contentDocument.head).createChild({
26054             tag : 'style',
26055             type : 'text/css',
26056             html : style
26057         });
26058
26059         return;
26060     }
26061     
26062     // hide stuff that is not compatible
26063     /**
26064      * @event blur
26065      * @hide
26066      */
26067     /**
26068      * @event change
26069      * @hide
26070      */
26071     /**
26072      * @event focus
26073      * @hide
26074      */
26075     /**
26076      * @event specialkey
26077      * @hide
26078      */
26079     /**
26080      * @cfg {String} fieldClass @hide
26081      */
26082     /**
26083      * @cfg {String} focusClass @hide
26084      */
26085     /**
26086      * @cfg {String} autoCreate @hide
26087      */
26088     /**
26089      * @cfg {String} inputType @hide
26090      */
26091     /**
26092      * @cfg {String} invalidClass @hide
26093      */
26094     /**
26095      * @cfg {String} invalidText @hide
26096      */
26097     /**
26098      * @cfg {String} msgFx @hide
26099      */
26100     /**
26101      * @cfg {String} validateOnBlur @hide
26102      */
26103 });
26104
26105 Roo.HtmlEditorCore.white = [
26106         'area', 'br', 'img', 'input', 'hr', 'wbr',
26107         
26108        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26109        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26110        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26111        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26112        'table',   'ul',         'xmp', 
26113        
26114        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26115       'thead',   'tr', 
26116      
26117       'dir', 'menu', 'ol', 'ul', 'dl',
26118        
26119       'embed',  'object'
26120 ];
26121
26122
26123 Roo.HtmlEditorCore.black = [
26124     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26125         'applet', // 
26126         'base',   'basefont', 'bgsound', 'blink',  'body', 
26127         'frame',  'frameset', 'head',    'html',   'ilayer', 
26128         'iframe', 'layer',  'link',     'meta',    'object',   
26129         'script', 'style' ,'title',  'xml' // clean later..
26130 ];
26131 Roo.HtmlEditorCore.clean = [
26132     'script', 'style', 'title', 'xml'
26133 ];
26134 Roo.HtmlEditorCore.remove = [
26135     'font'
26136 ];
26137 // attributes..
26138
26139 Roo.HtmlEditorCore.ablack = [
26140     'on'
26141 ];
26142     
26143 Roo.HtmlEditorCore.aclean = [ 
26144     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26145 ];
26146
26147 // protocols..
26148 Roo.HtmlEditorCore.pwhite= [
26149         'http',  'https',  'mailto'
26150 ];
26151
26152 // white listed style attributes.
26153 Roo.HtmlEditorCore.cwhite= [
26154       //  'text-align', /// default is to allow most things..
26155       
26156          
26157 //        'font-size'//??
26158 ];
26159
26160 // black listed style attributes.
26161 Roo.HtmlEditorCore.cblack= [
26162       //  'font-size' -- this can be set by the project 
26163 ];
26164
26165
26166 Roo.HtmlEditorCore.swapCodes   =[ 
26167     [    8211, "&#8211;" ], 
26168     [    8212, "&#8212;" ], 
26169     [    8216,  "'" ],  
26170     [    8217, "'" ],  
26171     [    8220, '"' ],  
26172     [    8221, '"' ],  
26173     [    8226, "*" ],  
26174     [    8230, "..." ]
26175 ]; 
26176
26177     /*
26178  * - LGPL
26179  *
26180  * HtmlEditor
26181  * 
26182  */
26183
26184 /**
26185  * @class Roo.bootstrap.HtmlEditor
26186  * @extends Roo.bootstrap.TextArea
26187  * Bootstrap HtmlEditor class
26188
26189  * @constructor
26190  * Create a new HtmlEditor
26191  * @param {Object} config The config object
26192  */
26193
26194 Roo.bootstrap.HtmlEditor = function(config){
26195     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26196     if (!this.toolbars) {
26197         this.toolbars = [];
26198     }
26199     
26200     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26201     this.addEvents({
26202             /**
26203              * @event initialize
26204              * Fires when the editor is fully initialized (including the iframe)
26205              * @param {HtmlEditor} this
26206              */
26207             initialize: true,
26208             /**
26209              * @event activate
26210              * Fires when the editor is first receives the focus. Any insertion must wait
26211              * until after this event.
26212              * @param {HtmlEditor} this
26213              */
26214             activate: true,
26215              /**
26216              * @event beforesync
26217              * Fires before the textarea is updated with content from the editor iframe. Return false
26218              * to cancel the sync.
26219              * @param {HtmlEditor} this
26220              * @param {String} html
26221              */
26222             beforesync: true,
26223              /**
26224              * @event beforepush
26225              * Fires before the iframe editor is updated with content from the textarea. Return false
26226              * to cancel the push.
26227              * @param {HtmlEditor} this
26228              * @param {String} html
26229              */
26230             beforepush: true,
26231              /**
26232              * @event sync
26233              * Fires when the textarea is updated with content from the editor iframe.
26234              * @param {HtmlEditor} this
26235              * @param {String} html
26236              */
26237             sync: true,
26238              /**
26239              * @event push
26240              * Fires when the iframe editor is updated with content from the textarea.
26241              * @param {HtmlEditor} this
26242              * @param {String} html
26243              */
26244             push: true,
26245              /**
26246              * @event editmodechange
26247              * Fires when the editor switches edit modes
26248              * @param {HtmlEditor} this
26249              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26250              */
26251             editmodechange: true,
26252             /**
26253              * @event editorevent
26254              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26255              * @param {HtmlEditor} this
26256              */
26257             editorevent: true,
26258             /**
26259              * @event firstfocus
26260              * Fires when on first focus - needed by toolbars..
26261              * @param {HtmlEditor} this
26262              */
26263             firstfocus: true,
26264             /**
26265              * @event autosave
26266              * Auto save the htmlEditor value as a file into Events
26267              * @param {HtmlEditor} this
26268              */
26269             autosave: true,
26270             /**
26271              * @event savedpreview
26272              * preview the saved version of htmlEditor
26273              * @param {HtmlEditor} this
26274              */
26275             savedpreview: true
26276         });
26277 };
26278
26279
26280 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26281     
26282     
26283       /**
26284      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26285      */
26286     toolbars : false,
26287     
26288      /**
26289     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26290     */
26291     btns : [],
26292    
26293      /**
26294      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26295      *                        Roo.resizable.
26296      */
26297     resizable : false,
26298      /**
26299      * @cfg {Number} height (in pixels)
26300      */   
26301     height: 300,
26302    /**
26303      * @cfg {Number} width (in pixels)
26304      */   
26305     width: false,
26306     
26307     /**
26308      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26309      * 
26310      */
26311     stylesheets: false,
26312     
26313     // id of frame..
26314     frameId: false,
26315     
26316     // private properties
26317     validationEvent : false,
26318     deferHeight: true,
26319     initialized : false,
26320     activated : false,
26321     
26322     onFocus : Roo.emptyFn,
26323     iframePad:3,
26324     hideMode:'offsets',
26325     
26326     tbContainer : false,
26327     
26328     bodyCls : '',
26329     
26330     toolbarContainer :function() {
26331         return this.wrap.select('.x-html-editor-tb',true).first();
26332     },
26333
26334     /**
26335      * Protected method that will not generally be called directly. It
26336      * is called when the editor creates its toolbar. Override this method if you need to
26337      * add custom toolbar buttons.
26338      * @param {HtmlEditor} editor
26339      */
26340     createToolbar : function(){
26341         Roo.log('renewing');
26342         Roo.log("create toolbars");
26343         
26344         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26345         this.toolbars[0].render(this.toolbarContainer());
26346         
26347         return;
26348         
26349 //        if (!editor.toolbars || !editor.toolbars.length) {
26350 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26351 //        }
26352 //        
26353 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26354 //            editor.toolbars[i] = Roo.factory(
26355 //                    typeof(editor.toolbars[i]) == 'string' ?
26356 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26357 //                Roo.bootstrap.HtmlEditor);
26358 //            editor.toolbars[i].init(editor);
26359 //        }
26360     },
26361
26362      
26363     // private
26364     onRender : function(ct, position)
26365     {
26366        // Roo.log("Call onRender: " + this.xtype);
26367         var _t = this;
26368         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26369       
26370         this.wrap = this.inputEl().wrap({
26371             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26372         });
26373         
26374         this.editorcore.onRender(ct, position);
26375          
26376         if (this.resizable) {
26377             this.resizeEl = new Roo.Resizable(this.wrap, {
26378                 pinned : true,
26379                 wrap: true,
26380                 dynamic : true,
26381                 minHeight : this.height,
26382                 height: this.height,
26383                 handles : this.resizable,
26384                 width: this.width,
26385                 listeners : {
26386                     resize : function(r, w, h) {
26387                         _t.onResize(w,h); // -something
26388                     }
26389                 }
26390             });
26391             
26392         }
26393         this.createToolbar(this);
26394        
26395         
26396         if(!this.width && this.resizable){
26397             this.setSize(this.wrap.getSize());
26398         }
26399         if (this.resizeEl) {
26400             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26401             // should trigger onReize..
26402         }
26403         
26404     },
26405
26406     // private
26407     onResize : function(w, h)
26408     {
26409         Roo.log('resize: ' +w + ',' + h );
26410         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26411         var ew = false;
26412         var eh = false;
26413         
26414         if(this.inputEl() ){
26415             if(typeof w == 'number'){
26416                 var aw = w - this.wrap.getFrameWidth('lr');
26417                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26418                 ew = aw;
26419             }
26420             if(typeof h == 'number'){
26421                  var tbh = -11;  // fixme it needs to tool bar size!
26422                 for (var i =0; i < this.toolbars.length;i++) {
26423                     // fixme - ask toolbars for heights?
26424                     tbh += this.toolbars[i].el.getHeight();
26425                     //if (this.toolbars[i].footer) {
26426                     //    tbh += this.toolbars[i].footer.el.getHeight();
26427                     //}
26428                 }
26429               
26430                 
26431                 
26432                 
26433                 
26434                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26435                 ah -= 5; // knock a few pixes off for look..
26436                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26437                 var eh = ah;
26438             }
26439         }
26440         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26441         this.editorcore.onResize(ew,eh);
26442         
26443     },
26444
26445     /**
26446      * Toggles the editor between standard and source edit mode.
26447      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26448      */
26449     toggleSourceEdit : function(sourceEditMode)
26450     {
26451         this.editorcore.toggleSourceEdit(sourceEditMode);
26452         
26453         if(this.editorcore.sourceEditMode){
26454             Roo.log('editor - showing textarea');
26455             
26456 //            Roo.log('in');
26457 //            Roo.log(this.syncValue());
26458             this.syncValue();
26459             this.inputEl().removeClass(['hide', 'x-hidden']);
26460             this.inputEl().dom.removeAttribute('tabIndex');
26461             this.inputEl().focus();
26462         }else{
26463             Roo.log('editor - hiding textarea');
26464 //            Roo.log('out')
26465 //            Roo.log(this.pushValue()); 
26466             this.pushValue();
26467             
26468             this.inputEl().addClass(['hide', 'x-hidden']);
26469             this.inputEl().dom.setAttribute('tabIndex', -1);
26470             //this.deferFocus();
26471         }
26472          
26473         if(this.resizable){
26474             this.setSize(this.wrap.getSize());
26475         }
26476         
26477         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26478     },
26479  
26480     // private (for BoxComponent)
26481     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26482
26483     // private (for BoxComponent)
26484     getResizeEl : function(){
26485         return this.wrap;
26486     },
26487
26488     // private (for BoxComponent)
26489     getPositionEl : function(){
26490         return this.wrap;
26491     },
26492
26493     // private
26494     initEvents : function(){
26495         this.originalValue = this.getValue();
26496     },
26497
26498 //    /**
26499 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26500 //     * @method
26501 //     */
26502 //    markInvalid : Roo.emptyFn,
26503 //    /**
26504 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26505 //     * @method
26506 //     */
26507 //    clearInvalid : Roo.emptyFn,
26508
26509     setValue : function(v){
26510         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26511         this.editorcore.pushValue();
26512     },
26513
26514      
26515     // private
26516     deferFocus : function(){
26517         this.focus.defer(10, this);
26518     },
26519
26520     // doc'ed in Field
26521     focus : function(){
26522         this.editorcore.focus();
26523         
26524     },
26525       
26526
26527     // private
26528     onDestroy : function(){
26529         
26530         
26531         
26532         if(this.rendered){
26533             
26534             for (var i =0; i < this.toolbars.length;i++) {
26535                 // fixme - ask toolbars for heights?
26536                 this.toolbars[i].onDestroy();
26537             }
26538             
26539             this.wrap.dom.innerHTML = '';
26540             this.wrap.remove();
26541         }
26542     },
26543
26544     // private
26545     onFirstFocus : function(){
26546         //Roo.log("onFirstFocus");
26547         this.editorcore.onFirstFocus();
26548          for (var i =0; i < this.toolbars.length;i++) {
26549             this.toolbars[i].onFirstFocus();
26550         }
26551         
26552     },
26553     
26554     // private
26555     syncValue : function()
26556     {   
26557         this.editorcore.syncValue();
26558     },
26559     
26560     pushValue : function()
26561     {   
26562         this.editorcore.pushValue();
26563     }
26564      
26565     
26566     // hide stuff that is not compatible
26567     /**
26568      * @event blur
26569      * @hide
26570      */
26571     /**
26572      * @event change
26573      * @hide
26574      */
26575     /**
26576      * @event focus
26577      * @hide
26578      */
26579     /**
26580      * @event specialkey
26581      * @hide
26582      */
26583     /**
26584      * @cfg {String} fieldClass @hide
26585      */
26586     /**
26587      * @cfg {String} focusClass @hide
26588      */
26589     /**
26590      * @cfg {String} autoCreate @hide
26591      */
26592     /**
26593      * @cfg {String} inputType @hide
26594      */
26595      
26596     /**
26597      * @cfg {String} invalidText @hide
26598      */
26599     /**
26600      * @cfg {String} msgFx @hide
26601      */
26602     /**
26603      * @cfg {String} validateOnBlur @hide
26604      */
26605 });
26606  
26607     
26608    
26609    
26610    
26611       
26612 Roo.namespace('Roo.bootstrap.htmleditor');
26613 /**
26614  * @class Roo.bootstrap.HtmlEditorToolbar1
26615  * Basic Toolbar
26616  * 
26617  * @example
26618  * Usage:
26619  *
26620  new Roo.bootstrap.HtmlEditor({
26621     ....
26622     toolbars : [
26623         new Roo.bootstrap.HtmlEditorToolbar1({
26624             disable : { fonts: 1 , format: 1, ..., ... , ...],
26625             btns : [ .... ]
26626         })
26627     }
26628      
26629  * 
26630  * @cfg {Object} disable List of elements to disable..
26631  * @cfg {Array} btns List of additional buttons.
26632  * 
26633  * 
26634  * NEEDS Extra CSS? 
26635  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26636  */
26637  
26638 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26639 {
26640     
26641     Roo.apply(this, config);
26642     
26643     // default disabled, based on 'good practice'..
26644     this.disable = this.disable || {};
26645     Roo.applyIf(this.disable, {
26646         fontSize : true,
26647         colors : true,
26648         specialElements : true
26649     });
26650     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26651     
26652     this.editor = config.editor;
26653     this.editorcore = config.editor.editorcore;
26654     
26655     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26656     
26657     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26658     // dont call parent... till later.
26659 }
26660 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26661      
26662     bar : true,
26663     
26664     editor : false,
26665     editorcore : false,
26666     
26667     
26668     formats : [
26669         "p" ,  
26670         "h1","h2","h3","h4","h5","h6", 
26671         "pre", "code", 
26672         "abbr", "acronym", "address", "cite", "samp", "var",
26673         'div','span'
26674     ],
26675     
26676     onRender : function(ct, position)
26677     {
26678        // Roo.log("Call onRender: " + this.xtype);
26679         
26680        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26681        Roo.log(this.el);
26682        this.el.dom.style.marginBottom = '0';
26683        var _this = this;
26684        var editorcore = this.editorcore;
26685        var editor= this.editor;
26686        
26687        var children = [];
26688        var btn = function(id,cmd , toggle, handler, html){
26689        
26690             var  event = toggle ? 'toggle' : 'click';
26691        
26692             var a = {
26693                 size : 'sm',
26694                 xtype: 'Button',
26695                 xns: Roo.bootstrap,
26696                 //glyphicon : id,
26697                 fa: id,
26698                 cmd : id || cmd,
26699                 enableToggle:toggle !== false,
26700                 html : html || '',
26701                 pressed : toggle ? false : null,
26702                 listeners : {}
26703             };
26704             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26705                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26706             };
26707             children.push(a);
26708             return a;
26709        }
26710        
26711     //    var cb_box = function...
26712         
26713         var style = {
26714                 xtype: 'Button',
26715                 size : 'sm',
26716                 xns: Roo.bootstrap,
26717                 fa : 'font',
26718                 //html : 'submit'
26719                 menu : {
26720                     xtype: 'Menu',
26721                     xns: Roo.bootstrap,
26722                     items:  []
26723                 }
26724         };
26725         Roo.each(this.formats, function(f) {
26726             style.menu.items.push({
26727                 xtype :'MenuItem',
26728                 xns: Roo.bootstrap,
26729                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26730                 tagname : f,
26731                 listeners : {
26732                     click : function()
26733                     {
26734                         editorcore.insertTag(this.tagname);
26735                         editor.focus();
26736                     }
26737                 }
26738                 
26739             });
26740         });
26741         children.push(style);   
26742         
26743         btn('bold',false,true);
26744         btn('italic',false,true);
26745         btn('align-left', 'justifyleft',true);
26746         btn('align-center', 'justifycenter',true);
26747         btn('align-right' , 'justifyright',true);
26748         btn('link', false, false, function(btn) {
26749             //Roo.log("create link?");
26750             var url = prompt(this.createLinkText, this.defaultLinkValue);
26751             if(url && url != 'http:/'+'/'){
26752                 this.editorcore.relayCmd('createlink', url);
26753             }
26754         }),
26755         btn('list','insertunorderedlist',true);
26756         btn('pencil', false,true, function(btn){
26757                 Roo.log(this);
26758                 this.toggleSourceEdit(btn.pressed);
26759         });
26760         
26761         if (this.editor.btns.length > 0) {
26762             for (var i = 0; i<this.editor.btns.length; i++) {
26763                 children.push(this.editor.btns[i]);
26764             }
26765         }
26766         
26767         /*
26768         var cog = {
26769                 xtype: 'Button',
26770                 size : 'sm',
26771                 xns: Roo.bootstrap,
26772                 glyphicon : 'cog',
26773                 //html : 'submit'
26774                 menu : {
26775                     xtype: 'Menu',
26776                     xns: Roo.bootstrap,
26777                     items:  []
26778                 }
26779         };
26780         
26781         cog.menu.items.push({
26782             xtype :'MenuItem',
26783             xns: Roo.bootstrap,
26784             html : Clean styles,
26785             tagname : f,
26786             listeners : {
26787                 click : function()
26788                 {
26789                     editorcore.insertTag(this.tagname);
26790                     editor.focus();
26791                 }
26792             }
26793             
26794         });
26795        */
26796         
26797          
26798        this.xtype = 'NavSimplebar';
26799         
26800         for(var i=0;i< children.length;i++) {
26801             
26802             this.buttons.add(this.addxtypeChild(children[i]));
26803             
26804         }
26805         
26806         editor.on('editorevent', this.updateToolbar, this);
26807     },
26808     onBtnClick : function(id)
26809     {
26810        this.editorcore.relayCmd(id);
26811        this.editorcore.focus();
26812     },
26813     
26814     /**
26815      * Protected method that will not generally be called directly. It triggers
26816      * a toolbar update by reading the markup state of the current selection in the editor.
26817      */
26818     updateToolbar: function(){
26819
26820         if(!this.editorcore.activated){
26821             this.editor.onFirstFocus(); // is this neeed?
26822             return;
26823         }
26824
26825         var btns = this.buttons; 
26826         var doc = this.editorcore.doc;
26827         btns.get('bold').setActive(doc.queryCommandState('bold'));
26828         btns.get('italic').setActive(doc.queryCommandState('italic'));
26829         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26830         
26831         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26832         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26833         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26834         
26835         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26836         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26837          /*
26838         
26839         var ans = this.editorcore.getAllAncestors();
26840         if (this.formatCombo) {
26841             
26842             
26843             var store = this.formatCombo.store;
26844             this.formatCombo.setValue("");
26845             for (var i =0; i < ans.length;i++) {
26846                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26847                     // select it..
26848                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26849                     break;
26850                 }
26851             }
26852         }
26853         
26854         
26855         
26856         // hides menus... - so this cant be on a menu...
26857         Roo.bootstrap.MenuMgr.hideAll();
26858         */
26859         Roo.bootstrap.MenuMgr.hideAll();
26860         //this.editorsyncValue();
26861     },
26862     onFirstFocus: function() {
26863         this.buttons.each(function(item){
26864            item.enable();
26865         });
26866     },
26867     toggleSourceEdit : function(sourceEditMode){
26868         
26869           
26870         if(sourceEditMode){
26871             Roo.log("disabling buttons");
26872            this.buttons.each( function(item){
26873                 if(item.cmd != 'pencil'){
26874                     item.disable();
26875                 }
26876             });
26877           
26878         }else{
26879             Roo.log("enabling buttons");
26880             if(this.editorcore.initialized){
26881                 this.buttons.each( function(item){
26882                     item.enable();
26883                 });
26884             }
26885             
26886         }
26887         Roo.log("calling toggole on editor");
26888         // tell the editor that it's been pressed..
26889         this.editor.toggleSourceEdit(sourceEditMode);
26890        
26891     }
26892 });
26893
26894
26895
26896
26897  
26898 /*
26899  * - LGPL
26900  */
26901
26902 /**
26903  * @class Roo.bootstrap.Markdown
26904  * @extends Roo.bootstrap.TextArea
26905  * Bootstrap Showdown editable area
26906  * @cfg {string} content
26907  * 
26908  * @constructor
26909  * Create a new Showdown
26910  */
26911
26912 Roo.bootstrap.Markdown = function(config){
26913     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26914    
26915 };
26916
26917 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26918     
26919     editing :false,
26920     
26921     initEvents : function()
26922     {
26923         
26924         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26925         this.markdownEl = this.el.createChild({
26926             cls : 'roo-markdown-area'
26927         });
26928         this.inputEl().addClass('d-none');
26929         if (this.getValue() == '') {
26930             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26931             
26932         } else {
26933             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26934         }
26935         this.markdownEl.on('click', this.toggleTextEdit, this);
26936         this.on('blur', this.toggleTextEdit, this);
26937         this.on('specialkey', this.resizeTextArea, this);
26938     },
26939     
26940     toggleTextEdit : function()
26941     {
26942         var sh = this.markdownEl.getHeight();
26943         this.inputEl().addClass('d-none');
26944         this.markdownEl.addClass('d-none');
26945         if (!this.editing) {
26946             // show editor?
26947             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26948             this.inputEl().removeClass('d-none');
26949             this.inputEl().focus();
26950             this.editing = true;
26951             return;
26952         }
26953         // show showdown...
26954         this.updateMarkdown();
26955         this.markdownEl.removeClass('d-none');
26956         this.editing = false;
26957         return;
26958     },
26959     updateMarkdown : function()
26960     {
26961         if (this.getValue() == '') {
26962             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26963             return;
26964         }
26965  
26966         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26967     },
26968     
26969     resizeTextArea: function () {
26970         
26971         var sh = 100;
26972         Roo.log([sh, this.getValue().split("\n").length * 30]);
26973         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26974     },
26975     setValue : function(val)
26976     {
26977         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26978         if (!this.editing) {
26979             this.updateMarkdown();
26980         }
26981         
26982     },
26983     focus : function()
26984     {
26985         if (!this.editing) {
26986             this.toggleTextEdit();
26987         }
26988         
26989     }
26990
26991
26992 });
26993 /**
26994  * @class Roo.bootstrap.Table.AbstractSelectionModel
26995  * @extends Roo.util.Observable
26996  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26997  * implemented by descendant classes.  This class should not be directly instantiated.
26998  * @constructor
26999  */
27000 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27001     this.locked = false;
27002     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27003 };
27004
27005
27006 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
27007     /** @ignore Called by the grid automatically. Do not call directly. */
27008     init : function(grid){
27009         this.grid = grid;
27010         this.initEvents();
27011     },
27012
27013     /**
27014      * Locks the selections.
27015      */
27016     lock : function(){
27017         this.locked = true;
27018     },
27019
27020     /**
27021      * Unlocks the selections.
27022      */
27023     unlock : function(){
27024         this.locked = false;
27025     },
27026
27027     /**
27028      * Returns true if the selections are locked.
27029      * @return {Boolean}
27030      */
27031     isLocked : function(){
27032         return this.locked;
27033     },
27034     
27035     
27036     initEvents : function ()
27037     {
27038         
27039     }
27040 });
27041 /**
27042  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27043  * @class Roo.bootstrap.Table.RowSelectionModel
27044  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27045  * It supports multiple selections and keyboard selection/navigation. 
27046  * @constructor
27047  * @param {Object} config
27048  */
27049
27050 Roo.bootstrap.Table.RowSelectionModel = function(config){
27051     Roo.apply(this, config);
27052     this.selections = new Roo.util.MixedCollection(false, function(o){
27053         return o.id;
27054     });
27055
27056     this.last = false;
27057     this.lastActive = false;
27058
27059     this.addEvents({
27060         /**
27061              * @event selectionchange
27062              * Fires when the selection changes
27063              * @param {SelectionModel} this
27064              */
27065             "selectionchange" : true,
27066         /**
27067              * @event afterselectionchange
27068              * Fires after the selection changes (eg. by key press or clicking)
27069              * @param {SelectionModel} this
27070              */
27071             "afterselectionchange" : true,
27072         /**
27073              * @event beforerowselect
27074              * Fires when a row is selected being selected, return false to cancel.
27075              * @param {SelectionModel} this
27076              * @param {Number} rowIndex The selected index
27077              * @param {Boolean} keepExisting False if other selections will be cleared
27078              */
27079             "beforerowselect" : true,
27080         /**
27081              * @event rowselect
27082              * Fires when a row is selected.
27083              * @param {SelectionModel} this
27084              * @param {Number} rowIndex The selected index
27085              * @param {Roo.data.Record} r The record
27086              */
27087             "rowselect" : true,
27088         /**
27089              * @event rowdeselect
27090              * Fires when a row is deselected.
27091              * @param {SelectionModel} this
27092              * @param {Number} rowIndex The selected index
27093              */
27094         "rowdeselect" : true
27095     });
27096     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27097     this.locked = false;
27098  };
27099
27100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27101     /**
27102      * @cfg {Boolean} singleSelect
27103      * True to allow selection of only one row at a time (defaults to false)
27104      */
27105     singleSelect : false,
27106
27107     // private
27108     initEvents : function()
27109     {
27110
27111         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27112         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27113         //}else{ // allow click to work like normal
27114          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27115         //}
27116         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27117         this.grid.on("rowclick", this.handleMouseDown, this);
27118         
27119         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27120             "up" : function(e){
27121                 if(!e.shiftKey){
27122                     this.selectPrevious(e.shiftKey);
27123                 }else if(this.last !== false && this.lastActive !== false){
27124                     var last = this.last;
27125                     this.selectRange(this.last,  this.lastActive-1);
27126                     this.grid.getView().focusRow(this.lastActive);
27127                     if(last !== false){
27128                         this.last = last;
27129                     }
27130                 }else{
27131                     this.selectFirstRow();
27132                 }
27133                 this.fireEvent("afterselectionchange", this);
27134             },
27135             "down" : function(e){
27136                 if(!e.shiftKey){
27137                     this.selectNext(e.shiftKey);
27138                 }else if(this.last !== false && this.lastActive !== false){
27139                     var last = this.last;
27140                     this.selectRange(this.last,  this.lastActive+1);
27141                     this.grid.getView().focusRow(this.lastActive);
27142                     if(last !== false){
27143                         this.last = last;
27144                     }
27145                 }else{
27146                     this.selectFirstRow();
27147                 }
27148                 this.fireEvent("afterselectionchange", this);
27149             },
27150             scope: this
27151         });
27152         this.grid.store.on('load', function(){
27153             this.selections.clear();
27154         },this);
27155         /*
27156         var view = this.grid.view;
27157         view.on("refresh", this.onRefresh, this);
27158         view.on("rowupdated", this.onRowUpdated, this);
27159         view.on("rowremoved", this.onRemove, this);
27160         */
27161     },
27162
27163     // private
27164     onRefresh : function()
27165     {
27166         var ds = this.grid.store, i, v = this.grid.view;
27167         var s = this.selections;
27168         s.each(function(r){
27169             if((i = ds.indexOfId(r.id)) != -1){
27170                 v.onRowSelect(i);
27171             }else{
27172                 s.remove(r);
27173             }
27174         });
27175     },
27176
27177     // private
27178     onRemove : function(v, index, r){
27179         this.selections.remove(r);
27180     },
27181
27182     // private
27183     onRowUpdated : function(v, index, r){
27184         if(this.isSelected(r)){
27185             v.onRowSelect(index);
27186         }
27187     },
27188
27189     /**
27190      * Select records.
27191      * @param {Array} records The records to select
27192      * @param {Boolean} keepExisting (optional) True to keep existing selections
27193      */
27194     selectRecords : function(records, keepExisting)
27195     {
27196         if(!keepExisting){
27197             this.clearSelections();
27198         }
27199             var ds = this.grid.store;
27200         for(var i = 0, len = records.length; i < len; i++){
27201             this.selectRow(ds.indexOf(records[i]), true);
27202         }
27203     },
27204
27205     /**
27206      * Gets the number of selected rows.
27207      * @return {Number}
27208      */
27209     getCount : function(){
27210         return this.selections.length;
27211     },
27212
27213     /**
27214      * Selects the first row in the grid.
27215      */
27216     selectFirstRow : function(){
27217         this.selectRow(0);
27218     },
27219
27220     /**
27221      * Select the last row.
27222      * @param {Boolean} keepExisting (optional) True to keep existing selections
27223      */
27224     selectLastRow : function(keepExisting){
27225         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27226         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27227     },
27228
27229     /**
27230      * Selects the row immediately following the last selected row.
27231      * @param {Boolean} keepExisting (optional) True to keep existing selections
27232      */
27233     selectNext : function(keepExisting)
27234     {
27235             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27236             this.selectRow(this.last+1, keepExisting);
27237             this.grid.getView().focusRow(this.last);
27238         }
27239     },
27240
27241     /**
27242      * Selects the row that precedes the last selected row.
27243      * @param {Boolean} keepExisting (optional) True to keep existing selections
27244      */
27245     selectPrevious : function(keepExisting){
27246         if(this.last){
27247             this.selectRow(this.last-1, keepExisting);
27248             this.grid.getView().focusRow(this.last);
27249         }
27250     },
27251
27252     /**
27253      * Returns the selected records
27254      * @return {Array} Array of selected records
27255      */
27256     getSelections : function(){
27257         return [].concat(this.selections.items);
27258     },
27259
27260     /**
27261      * Returns the first selected record.
27262      * @return {Record}
27263      */
27264     getSelected : function(){
27265         return this.selections.itemAt(0);
27266     },
27267
27268
27269     /**
27270      * Clears all selections.
27271      */
27272     clearSelections : function(fast)
27273     {
27274         if(this.locked) {
27275             return;
27276         }
27277         if(fast !== true){
27278                 var ds = this.grid.store;
27279             var s = this.selections;
27280             s.each(function(r){
27281                 this.deselectRow(ds.indexOfId(r.id));
27282             }, this);
27283             s.clear();
27284         }else{
27285             this.selections.clear();
27286         }
27287         this.last = false;
27288     },
27289
27290
27291     /**
27292      * Selects all rows.
27293      */
27294     selectAll : function(){
27295         if(this.locked) {
27296             return;
27297         }
27298         this.selections.clear();
27299         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27300             this.selectRow(i, true);
27301         }
27302     },
27303
27304     /**
27305      * Returns True if there is a selection.
27306      * @return {Boolean}
27307      */
27308     hasSelection : function(){
27309         return this.selections.length > 0;
27310     },
27311
27312     /**
27313      * Returns True if the specified row is selected.
27314      * @param {Number/Record} record The record or index of the record to check
27315      * @return {Boolean}
27316      */
27317     isSelected : function(index){
27318             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27319         return (r && this.selections.key(r.id) ? true : false);
27320     },
27321
27322     /**
27323      * Returns True if the specified record id is selected.
27324      * @param {String} id The id of record to check
27325      * @return {Boolean}
27326      */
27327     isIdSelected : function(id){
27328         return (this.selections.key(id) ? true : false);
27329     },
27330
27331
27332     // private
27333     handleMouseDBClick : function(e, t){
27334         
27335     },
27336     // private
27337     handleMouseDown : function(e, t)
27338     {
27339             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27340         if(this.isLocked() || rowIndex < 0 ){
27341             return;
27342         };
27343         if(e.shiftKey && this.last !== false){
27344             var last = this.last;
27345             this.selectRange(last, rowIndex, e.ctrlKey);
27346             this.last = last; // reset the last
27347             t.focus();
27348     
27349         }else{
27350             var isSelected = this.isSelected(rowIndex);
27351             //Roo.log("select row:" + rowIndex);
27352             if(isSelected){
27353                 this.deselectRow(rowIndex);
27354             } else {
27355                         this.selectRow(rowIndex, true);
27356             }
27357     
27358             /*
27359                 if(e.button !== 0 && isSelected){
27360                 alert('rowIndex 2: ' + rowIndex);
27361                     view.focusRow(rowIndex);
27362                 }else if(e.ctrlKey && isSelected){
27363                     this.deselectRow(rowIndex);
27364                 }else if(!isSelected){
27365                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27366                     view.focusRow(rowIndex);
27367                 }
27368             */
27369         }
27370         this.fireEvent("afterselectionchange", this);
27371     },
27372     // private
27373     handleDragableRowClick :  function(grid, rowIndex, e) 
27374     {
27375         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27376             this.selectRow(rowIndex, false);
27377             grid.view.focusRow(rowIndex);
27378              this.fireEvent("afterselectionchange", this);
27379         }
27380     },
27381     
27382     /**
27383      * Selects multiple rows.
27384      * @param {Array} rows Array of the indexes of the row to select
27385      * @param {Boolean} keepExisting (optional) True to keep existing selections
27386      */
27387     selectRows : function(rows, keepExisting){
27388         if(!keepExisting){
27389             this.clearSelections();
27390         }
27391         for(var i = 0, len = rows.length; i < len; i++){
27392             this.selectRow(rows[i], true);
27393         }
27394     },
27395
27396     /**
27397      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27398      * @param {Number} startRow The index of the first row in the range
27399      * @param {Number} endRow The index of the last row in the range
27400      * @param {Boolean} keepExisting (optional) True to retain existing selections
27401      */
27402     selectRange : function(startRow, endRow, keepExisting){
27403         if(this.locked) {
27404             return;
27405         }
27406         if(!keepExisting){
27407             this.clearSelections();
27408         }
27409         if(startRow <= endRow){
27410             for(var i = startRow; i <= endRow; i++){
27411                 this.selectRow(i, true);
27412             }
27413         }else{
27414             for(var i = startRow; i >= endRow; i--){
27415                 this.selectRow(i, true);
27416             }
27417         }
27418     },
27419
27420     /**
27421      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27422      * @param {Number} startRow The index of the first row in the range
27423      * @param {Number} endRow The index of the last row in the range
27424      */
27425     deselectRange : function(startRow, endRow, preventViewNotify){
27426         if(this.locked) {
27427             return;
27428         }
27429         for(var i = startRow; i <= endRow; i++){
27430             this.deselectRow(i, preventViewNotify);
27431         }
27432     },
27433
27434     /**
27435      * Selects a row.
27436      * @param {Number} row The index of the row to select
27437      * @param {Boolean} keepExisting (optional) True to keep existing selections
27438      */
27439     selectRow : function(index, keepExisting, preventViewNotify)
27440     {
27441             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27442             return;
27443         }
27444         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27445             if(!keepExisting || this.singleSelect){
27446                 this.clearSelections();
27447             }
27448             
27449             var r = this.grid.store.getAt(index);
27450             //console.log('selectRow - record id :' + r.id);
27451             
27452             this.selections.add(r);
27453             this.last = this.lastActive = index;
27454             if(!preventViewNotify){
27455                 var proxy = new Roo.Element(
27456                                 this.grid.getRowDom(index)
27457                 );
27458                 proxy.addClass('bg-info info');
27459             }
27460             this.fireEvent("rowselect", this, index, r);
27461             this.fireEvent("selectionchange", this);
27462         }
27463     },
27464
27465     /**
27466      * Deselects a row.
27467      * @param {Number} row The index of the row to deselect
27468      */
27469     deselectRow : function(index, preventViewNotify)
27470     {
27471         if(this.locked) {
27472             return;
27473         }
27474         if(this.last == index){
27475             this.last = false;
27476         }
27477         if(this.lastActive == index){
27478             this.lastActive = false;
27479         }
27480         
27481         var r = this.grid.store.getAt(index);
27482         if (!r) {
27483             return;
27484         }
27485         
27486         this.selections.remove(r);
27487         //.console.log('deselectRow - record id :' + r.id);
27488         if(!preventViewNotify){
27489         
27490             var proxy = new Roo.Element(
27491                 this.grid.getRowDom(index)
27492             );
27493             proxy.removeClass('bg-info info');
27494         }
27495         this.fireEvent("rowdeselect", this, index);
27496         this.fireEvent("selectionchange", this);
27497     },
27498
27499     // private
27500     restoreLast : function(){
27501         if(this._last){
27502             this.last = this._last;
27503         }
27504     },
27505
27506     // private
27507     acceptsNav : function(row, col, cm){
27508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27509     },
27510
27511     // private
27512     onEditorKey : function(field, e){
27513         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27514         if(k == e.TAB){
27515             e.stopEvent();
27516             ed.completeEdit();
27517             if(e.shiftKey){
27518                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27519             }else{
27520                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27521             }
27522         }else if(k == e.ENTER && !e.ctrlKey){
27523             e.stopEvent();
27524             ed.completeEdit();
27525             if(e.shiftKey){
27526                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27527             }else{
27528                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27529             }
27530         }else if(k == e.ESC){
27531             ed.cancelEdit();
27532         }
27533         if(newCell){
27534             g.startEditing(newCell[0], newCell[1]);
27535         }
27536     }
27537 });
27538 /*
27539  * Based on:
27540  * Ext JS Library 1.1.1
27541  * Copyright(c) 2006-2007, Ext JS, LLC.
27542  *
27543  * Originally Released Under LGPL - original licence link has changed is not relivant.
27544  *
27545  * Fork - LGPL
27546  * <script type="text/javascript">
27547  */
27548  
27549 /**
27550  * @class Roo.bootstrap.PagingToolbar
27551  * @extends Roo.bootstrap.NavSimplebar
27552  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27553  * @constructor
27554  * Create a new PagingToolbar
27555  * @param {Object} config The config object
27556  * @param {Roo.data.Store} store
27557  */
27558 Roo.bootstrap.PagingToolbar = function(config)
27559 {
27560     // old args format still supported... - xtype is prefered..
27561         // created from xtype...
27562     
27563     this.ds = config.dataSource;
27564     
27565     if (config.store && !this.ds) {
27566         this.store= Roo.factory(config.store, Roo.data);
27567         this.ds = this.store;
27568         this.ds.xmodule = this.xmodule || false;
27569     }
27570     
27571     this.toolbarItems = [];
27572     if (config.items) {
27573         this.toolbarItems = config.items;
27574     }
27575     
27576     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27577     
27578     this.cursor = 0;
27579     
27580     if (this.ds) { 
27581         this.bind(this.ds);
27582     }
27583     
27584     if (Roo.bootstrap.version == 4) {
27585         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27586     } else {
27587         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27588     }
27589     
27590 };
27591
27592 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27593     /**
27594      * @cfg {Roo.data.Store} dataSource
27595      * The underlying data store providing the paged data
27596      */
27597     /**
27598      * @cfg {String/HTMLElement/Element} container
27599      * container The id or element that will contain the toolbar
27600      */
27601     /**
27602      * @cfg {Boolean} displayInfo
27603      * True to display the displayMsg (defaults to false)
27604      */
27605     /**
27606      * @cfg {Number} pageSize
27607      * The number of records to display per page (defaults to 20)
27608      */
27609     pageSize: 20,
27610     /**
27611      * @cfg {String} displayMsg
27612      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27613      */
27614     displayMsg : 'Displaying {0} - {1} of {2}',
27615     /**
27616      * @cfg {String} emptyMsg
27617      * The message to display when no records are found (defaults to "No data to display")
27618      */
27619     emptyMsg : 'No data to display',
27620     /**
27621      * Customizable piece of the default paging text (defaults to "Page")
27622      * @type String
27623      */
27624     beforePageText : "Page",
27625     /**
27626      * Customizable piece of the default paging text (defaults to "of %0")
27627      * @type String
27628      */
27629     afterPageText : "of {0}",
27630     /**
27631      * Customizable piece of the default paging text (defaults to "First Page")
27632      * @type String
27633      */
27634     firstText : "First Page",
27635     /**
27636      * Customizable piece of the default paging text (defaults to "Previous Page")
27637      * @type String
27638      */
27639     prevText : "Previous Page",
27640     /**
27641      * Customizable piece of the default paging text (defaults to "Next Page")
27642      * @type String
27643      */
27644     nextText : "Next Page",
27645     /**
27646      * Customizable piece of the default paging text (defaults to "Last Page")
27647      * @type String
27648      */
27649     lastText : "Last Page",
27650     /**
27651      * Customizable piece of the default paging text (defaults to "Refresh")
27652      * @type String
27653      */
27654     refreshText : "Refresh",
27655
27656     buttons : false,
27657     // private
27658     onRender : function(ct, position) 
27659     {
27660         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27661         this.navgroup.parentId = this.id;
27662         this.navgroup.onRender(this.el, null);
27663         // add the buttons to the navgroup
27664         
27665         if(this.displayInfo){
27666             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27667             this.displayEl = this.el.select('.x-paging-info', true).first();
27668 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27669 //            this.displayEl = navel.el.select('span',true).first();
27670         }
27671         
27672         var _this = this;
27673         
27674         if(this.buttons){
27675             Roo.each(_this.buttons, function(e){ // this might need to use render????
27676                Roo.factory(e).render(_this.el);
27677             });
27678         }
27679             
27680         Roo.each(_this.toolbarItems, function(e) {
27681             _this.navgroup.addItem(e);
27682         });
27683         
27684         
27685         this.first = this.navgroup.addItem({
27686             tooltip: this.firstText,
27687             cls: "prev btn-outline-secondary",
27688             html : ' <i class="fa fa-step-backward"></i>',
27689             disabled: true,
27690             preventDefault: true,
27691             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27692         });
27693         
27694         this.prev =  this.navgroup.addItem({
27695             tooltip: this.prevText,
27696             cls: "prev btn-outline-secondary",
27697             html : ' <i class="fa fa-backward"></i>',
27698             disabled: true,
27699             preventDefault: true,
27700             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27701         });
27702     //this.addSeparator();
27703         
27704         
27705         var field = this.navgroup.addItem( {
27706             tagtype : 'span',
27707             cls : 'x-paging-position  btn-outline-secondary',
27708              disabled: true,
27709             html : this.beforePageText  +
27710                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27711                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27712          } ); //?? escaped?
27713         
27714         this.field = field.el.select('input', true).first();
27715         this.field.on("keydown", this.onPagingKeydown, this);
27716         this.field.on("focus", function(){this.dom.select();});
27717     
27718     
27719         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27720         //this.field.setHeight(18);
27721         //this.addSeparator();
27722         this.next = this.navgroup.addItem({
27723             tooltip: this.nextText,
27724             cls: "next btn-outline-secondary",
27725             html : ' <i class="fa fa-forward"></i>',
27726             disabled: true,
27727             preventDefault: true,
27728             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27729         });
27730         this.last = this.navgroup.addItem({
27731             tooltip: this.lastText,
27732             html : ' <i class="fa fa-step-forward"></i>',
27733             cls: "next btn-outline-secondary",
27734             disabled: true,
27735             preventDefault: true,
27736             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27737         });
27738     //this.addSeparator();
27739         this.loading = this.navgroup.addItem({
27740             tooltip: this.refreshText,
27741             cls: "btn-outline-secondary",
27742             html : ' <i class="fa fa-refresh"></i>',
27743             preventDefault: true,
27744             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27745         });
27746         
27747     },
27748
27749     // private
27750     updateInfo : function(){
27751         if(this.displayEl){
27752             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27753             var msg = count == 0 ?
27754                 this.emptyMsg :
27755                 String.format(
27756                     this.displayMsg,
27757                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27758                 );
27759             this.displayEl.update(msg);
27760         }
27761     },
27762
27763     // private
27764     onLoad : function(ds, r, o)
27765     {
27766         this.cursor = o.params && o.params.start ? o.params.start : 0;
27767         
27768         var d = this.getPageData(),
27769             ap = d.activePage,
27770             ps = d.pages;
27771         
27772         
27773         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27774         this.field.dom.value = ap;
27775         this.first.setDisabled(ap == 1);
27776         this.prev.setDisabled(ap == 1);
27777         this.next.setDisabled(ap == ps);
27778         this.last.setDisabled(ap == ps);
27779         this.loading.enable();
27780         this.updateInfo();
27781     },
27782
27783     // private
27784     getPageData : function(){
27785         var total = this.ds.getTotalCount();
27786         return {
27787             total : total,
27788             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27789             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27790         };
27791     },
27792
27793     // private
27794     onLoadError : function(){
27795         this.loading.enable();
27796     },
27797
27798     // private
27799     onPagingKeydown : function(e){
27800         var k = e.getKey();
27801         var d = this.getPageData();
27802         if(k == e.RETURN){
27803             var v = this.field.dom.value, pageNum;
27804             if(!v || isNaN(pageNum = parseInt(v, 10))){
27805                 this.field.dom.value = d.activePage;
27806                 return;
27807             }
27808             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27809             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27810             e.stopEvent();
27811         }
27812         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))
27813         {
27814           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27815           this.field.dom.value = pageNum;
27816           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27817           e.stopEvent();
27818         }
27819         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27820         {
27821           var v = this.field.dom.value, pageNum; 
27822           var increment = (e.shiftKey) ? 10 : 1;
27823           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27824                 increment *= -1;
27825           }
27826           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27827             this.field.dom.value = d.activePage;
27828             return;
27829           }
27830           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27831           {
27832             this.field.dom.value = parseInt(v, 10) + increment;
27833             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27834             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27835           }
27836           e.stopEvent();
27837         }
27838     },
27839
27840     // private
27841     beforeLoad : function(){
27842         if(this.loading){
27843             this.loading.disable();
27844         }
27845     },
27846
27847     // private
27848     onClick : function(which){
27849         
27850         var ds = this.ds;
27851         if (!ds) {
27852             return;
27853         }
27854         
27855         switch(which){
27856             case "first":
27857                 ds.load({params:{start: 0, limit: this.pageSize}});
27858             break;
27859             case "prev":
27860                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27861             break;
27862             case "next":
27863                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27864             break;
27865             case "last":
27866                 var total = ds.getTotalCount();
27867                 var extra = total % this.pageSize;
27868                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27869                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27870             break;
27871             case "refresh":
27872                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27873             break;
27874         }
27875     },
27876
27877     /**
27878      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27879      * @param {Roo.data.Store} store The data store to unbind
27880      */
27881     unbind : function(ds){
27882         ds.un("beforeload", this.beforeLoad, this);
27883         ds.un("load", this.onLoad, this);
27884         ds.un("loadexception", this.onLoadError, this);
27885         ds.un("remove", this.updateInfo, this);
27886         ds.un("add", this.updateInfo, this);
27887         this.ds = undefined;
27888     },
27889
27890     /**
27891      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27892      * @param {Roo.data.Store} store The data store to bind
27893      */
27894     bind : function(ds){
27895         ds.on("beforeload", this.beforeLoad, this);
27896         ds.on("load", this.onLoad, this);
27897         ds.on("loadexception", this.onLoadError, this);
27898         ds.on("remove", this.updateInfo, this);
27899         ds.on("add", this.updateInfo, this);
27900         this.ds = ds;
27901     }
27902 });/*
27903  * - LGPL
27904  *
27905  * element
27906  * 
27907  */
27908
27909 /**
27910  * @class Roo.bootstrap.MessageBar
27911  * @extends Roo.bootstrap.Component
27912  * Bootstrap MessageBar class
27913  * @cfg {String} html contents of the MessageBar
27914  * @cfg {String} weight (info | success | warning | danger) default info
27915  * @cfg {String} beforeClass insert the bar before the given class
27916  * @cfg {Boolean} closable (true | false) default false
27917  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27918  * 
27919  * @constructor
27920  * Create a new Element
27921  * @param {Object} config The config object
27922  */
27923
27924 Roo.bootstrap.MessageBar = function(config){
27925     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27926 };
27927
27928 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27929     
27930     html: '',
27931     weight: 'info',
27932     closable: false,
27933     fixed: false,
27934     beforeClass: 'bootstrap-sticky-wrap',
27935     
27936     getAutoCreate : function(){
27937         
27938         var cfg = {
27939             tag: 'div',
27940             cls: 'alert alert-dismissable alert-' + this.weight,
27941             cn: [
27942                 {
27943                     tag: 'span',
27944                     cls: 'message',
27945                     html: this.html || ''
27946                 }
27947             ]
27948         };
27949         
27950         if(this.fixed){
27951             cfg.cls += ' alert-messages-fixed';
27952         }
27953         
27954         if(this.closable){
27955             cfg.cn.push({
27956                 tag: 'button',
27957                 cls: 'close',
27958                 html: 'x'
27959             });
27960         }
27961         
27962         return cfg;
27963     },
27964     
27965     onRender : function(ct, position)
27966     {
27967         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27968         
27969         if(!this.el){
27970             var cfg = Roo.apply({},  this.getAutoCreate());
27971             cfg.id = Roo.id();
27972             
27973             if (this.cls) {
27974                 cfg.cls += ' ' + this.cls;
27975             }
27976             if (this.style) {
27977                 cfg.style = this.style;
27978             }
27979             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27980             
27981             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27982         }
27983         
27984         this.el.select('>button.close').on('click', this.hide, this);
27985         
27986     },
27987     
27988     show : function()
27989     {
27990         if (!this.rendered) {
27991             this.render();
27992         }
27993         
27994         this.el.show();
27995         
27996         this.fireEvent('show', this);
27997         
27998     },
27999     
28000     hide : function()
28001     {
28002         if (!this.rendered) {
28003             this.render();
28004         }
28005         
28006         this.el.hide();
28007         
28008         this.fireEvent('hide', this);
28009     },
28010     
28011     update : function()
28012     {
28013 //        var e = this.el.dom.firstChild;
28014 //        
28015 //        if(this.closable){
28016 //            e = e.nextSibling;
28017 //        }
28018 //        
28019 //        e.data = this.html || '';
28020
28021         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28022     }
28023    
28024 });
28025
28026  
28027
28028      /*
28029  * - LGPL
28030  *
28031  * Graph
28032  * 
28033  */
28034
28035
28036 /**
28037  * @class Roo.bootstrap.Graph
28038  * @extends Roo.bootstrap.Component
28039  * Bootstrap Graph class
28040 > Prameters
28041  -sm {number} sm 4
28042  -md {number} md 5
28043  @cfg {String} graphtype  bar | vbar | pie
28044  @cfg {number} g_x coodinator | centre x (pie)
28045  @cfg {number} g_y coodinator | centre y (pie)
28046  @cfg {number} g_r radius (pie)
28047  @cfg {number} g_height height of the chart (respected by all elements in the set)
28048  @cfg {number} g_width width of the chart (respected by all elements in the set)
28049  @cfg {Object} title The title of the chart
28050     
28051  -{Array}  values
28052  -opts (object) options for the chart 
28053      o {
28054      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28055      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28056      o vgutter (number)
28057      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.
28058      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28059      o to
28060      o stretch (boolean)
28061      o }
28062  -opts (object) options for the pie
28063      o{
28064      o cut
28065      o startAngle (number)
28066      o endAngle (number)
28067      } 
28068  *
28069  * @constructor
28070  * Create a new Input
28071  * @param {Object} config The config object
28072  */
28073
28074 Roo.bootstrap.Graph = function(config){
28075     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28076     
28077     this.addEvents({
28078         // img events
28079         /**
28080          * @event click
28081          * The img click event for the img.
28082          * @param {Roo.EventObject} e
28083          */
28084         "click" : true
28085     });
28086 };
28087
28088 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28089     
28090     sm: 4,
28091     md: 5,
28092     graphtype: 'bar',
28093     g_height: 250,
28094     g_width: 400,
28095     g_x: 50,
28096     g_y: 50,
28097     g_r: 30,
28098     opts:{
28099         //g_colors: this.colors,
28100         g_type: 'soft',
28101         g_gutter: '20%'
28102
28103     },
28104     title : false,
28105
28106     getAutoCreate : function(){
28107         
28108         var cfg = {
28109             tag: 'div',
28110             html : null
28111         };
28112         
28113         
28114         return  cfg;
28115     },
28116
28117     onRender : function(ct,position){
28118         
28119         
28120         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28121         
28122         if (typeof(Raphael) == 'undefined') {
28123             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28124             return;
28125         }
28126         
28127         this.raphael = Raphael(this.el.dom);
28128         
28129                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28130                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28131                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28132                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28133                 /*
28134                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28135                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28136                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28137                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28138                 
28139                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28140                 r.barchart(330, 10, 300, 220, data1);
28141                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28142                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28143                 */
28144                 
28145                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28146                 // r.barchart(30, 30, 560, 250,  xdata, {
28147                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28148                 //     axis : "0 0 1 1",
28149                 //     axisxlabels :  xdata
28150                 //     //yvalues : cols,
28151                    
28152                 // });
28153 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28154 //        
28155 //        this.load(null,xdata,{
28156 //                axis : "0 0 1 1",
28157 //                axisxlabels :  xdata
28158 //                });
28159
28160     },
28161
28162     load : function(graphtype,xdata,opts)
28163     {
28164         this.raphael.clear();
28165         if(!graphtype) {
28166             graphtype = this.graphtype;
28167         }
28168         if(!opts){
28169             opts = this.opts;
28170         }
28171         var r = this.raphael,
28172             fin = function () {
28173                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28174             },
28175             fout = function () {
28176                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28177             },
28178             pfin = function() {
28179                 this.sector.stop();
28180                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28181
28182                 if (this.label) {
28183                     this.label[0].stop();
28184                     this.label[0].attr({ r: 7.5 });
28185                     this.label[1].attr({ "font-weight": 800 });
28186                 }
28187             },
28188             pfout = function() {
28189                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28190
28191                 if (this.label) {
28192                     this.label[0].animate({ r: 5 }, 500, "bounce");
28193                     this.label[1].attr({ "font-weight": 400 });
28194                 }
28195             };
28196
28197         switch(graphtype){
28198             case 'bar':
28199                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28200                 break;
28201             case 'hbar':
28202                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28203                 break;
28204             case 'pie':
28205 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28206 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28207 //            
28208                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28209                 
28210                 break;
28211
28212         }
28213         
28214         if(this.title){
28215             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28216         }
28217         
28218     },
28219     
28220     setTitle: function(o)
28221     {
28222         this.title = o;
28223     },
28224     
28225     initEvents: function() {
28226         
28227         if(!this.href){
28228             this.el.on('click', this.onClick, this);
28229         }
28230     },
28231     
28232     onClick : function(e)
28233     {
28234         Roo.log('img onclick');
28235         this.fireEvent('click', this, e);
28236     }
28237    
28238 });
28239
28240  
28241 /*
28242  * - LGPL
28243  *
28244  * numberBox
28245  * 
28246  */
28247 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28248
28249 /**
28250  * @class Roo.bootstrap.dash.NumberBox
28251  * @extends Roo.bootstrap.Component
28252  * Bootstrap NumberBox class
28253  * @cfg {String} headline Box headline
28254  * @cfg {String} content Box content
28255  * @cfg {String} icon Box icon
28256  * @cfg {String} footer Footer text
28257  * @cfg {String} fhref Footer href
28258  * 
28259  * @constructor
28260  * Create a new NumberBox
28261  * @param {Object} config The config object
28262  */
28263
28264
28265 Roo.bootstrap.dash.NumberBox = function(config){
28266     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28267     
28268 };
28269
28270 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28271     
28272     headline : '',
28273     content : '',
28274     icon : '',
28275     footer : '',
28276     fhref : '',
28277     ficon : '',
28278     
28279     getAutoCreate : function(){
28280         
28281         var cfg = {
28282             tag : 'div',
28283             cls : 'small-box ',
28284             cn : [
28285                 {
28286                     tag : 'div',
28287                     cls : 'inner',
28288                     cn :[
28289                         {
28290                             tag : 'h3',
28291                             cls : 'roo-headline',
28292                             html : this.headline
28293                         },
28294                         {
28295                             tag : 'p',
28296                             cls : 'roo-content',
28297                             html : this.content
28298                         }
28299                     ]
28300                 }
28301             ]
28302         };
28303         
28304         if(this.icon){
28305             cfg.cn.push({
28306                 tag : 'div',
28307                 cls : 'icon',
28308                 cn :[
28309                     {
28310                         tag : 'i',
28311                         cls : 'ion ' + this.icon
28312                     }
28313                 ]
28314             });
28315         }
28316         
28317         if(this.footer){
28318             var footer = {
28319                 tag : 'a',
28320                 cls : 'small-box-footer',
28321                 href : this.fhref || '#',
28322                 html : this.footer
28323             };
28324             
28325             cfg.cn.push(footer);
28326             
28327         }
28328         
28329         return  cfg;
28330     },
28331
28332     onRender : function(ct,position){
28333         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28334
28335
28336        
28337                 
28338     },
28339
28340     setHeadline: function (value)
28341     {
28342         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28343     },
28344     
28345     setFooter: function (value, href)
28346     {
28347         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28348         
28349         if(href){
28350             this.el.select('a.small-box-footer',true).first().attr('href', href);
28351         }
28352         
28353     },
28354
28355     setContent: function (value)
28356     {
28357         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28358     },
28359
28360     initEvents: function() 
28361     {   
28362         
28363     }
28364     
28365 });
28366
28367  
28368 /*
28369  * - LGPL
28370  *
28371  * TabBox
28372  * 
28373  */
28374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28375
28376 /**
28377  * @class Roo.bootstrap.dash.TabBox
28378  * @extends Roo.bootstrap.Component
28379  * Bootstrap TabBox class
28380  * @cfg {String} title Title of the TabBox
28381  * @cfg {String} icon Icon of the TabBox
28382  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28383  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28384  * 
28385  * @constructor
28386  * Create a new TabBox
28387  * @param {Object} config The config object
28388  */
28389
28390
28391 Roo.bootstrap.dash.TabBox = function(config){
28392     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28393     this.addEvents({
28394         // raw events
28395         /**
28396          * @event addpane
28397          * When a pane is added
28398          * @param {Roo.bootstrap.dash.TabPane} pane
28399          */
28400         "addpane" : true,
28401         /**
28402          * @event activatepane
28403          * When a pane is activated
28404          * @param {Roo.bootstrap.dash.TabPane} pane
28405          */
28406         "activatepane" : true
28407         
28408          
28409     });
28410     
28411     this.panes = [];
28412 };
28413
28414 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28415
28416     title : '',
28417     icon : false,
28418     showtabs : true,
28419     tabScrollable : false,
28420     
28421     getChildContainer : function()
28422     {
28423         return this.el.select('.tab-content', true).first();
28424     },
28425     
28426     getAutoCreate : function(){
28427         
28428         var header = {
28429             tag: 'li',
28430             cls: 'pull-left header',
28431             html: this.title,
28432             cn : []
28433         };
28434         
28435         if(this.icon){
28436             header.cn.push({
28437                 tag: 'i',
28438                 cls: 'fa ' + this.icon
28439             });
28440         }
28441         
28442         var h = {
28443             tag: 'ul',
28444             cls: 'nav nav-tabs pull-right',
28445             cn: [
28446                 header
28447             ]
28448         };
28449         
28450         if(this.tabScrollable){
28451             h = {
28452                 tag: 'div',
28453                 cls: 'tab-header',
28454                 cn: [
28455                     {
28456                         tag: 'ul',
28457                         cls: 'nav nav-tabs pull-right',
28458                         cn: [
28459                             header
28460                         ]
28461                     }
28462                 ]
28463             };
28464         }
28465         
28466         var cfg = {
28467             tag: 'div',
28468             cls: 'nav-tabs-custom',
28469             cn: [
28470                 h,
28471                 {
28472                     tag: 'div',
28473                     cls: 'tab-content no-padding',
28474                     cn: []
28475                 }
28476             ]
28477         };
28478
28479         return  cfg;
28480     },
28481     initEvents : function()
28482     {
28483         //Roo.log('add add pane handler');
28484         this.on('addpane', this.onAddPane, this);
28485     },
28486      /**
28487      * Updates the box title
28488      * @param {String} html to set the title to.
28489      */
28490     setTitle : function(value)
28491     {
28492         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28493     },
28494     onAddPane : function(pane)
28495     {
28496         this.panes.push(pane);
28497         //Roo.log('addpane');
28498         //Roo.log(pane);
28499         // tabs are rendere left to right..
28500         if(!this.showtabs){
28501             return;
28502         }
28503         
28504         var ctr = this.el.select('.nav-tabs', true).first();
28505          
28506          
28507         var existing = ctr.select('.nav-tab',true);
28508         var qty = existing.getCount();;
28509         
28510         
28511         var tab = ctr.createChild({
28512             tag : 'li',
28513             cls : 'nav-tab' + (qty ? '' : ' active'),
28514             cn : [
28515                 {
28516                     tag : 'a',
28517                     href:'#',
28518                     html : pane.title
28519                 }
28520             ]
28521         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28522         pane.tab = tab;
28523         
28524         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28525         if (!qty) {
28526             pane.el.addClass('active');
28527         }
28528         
28529                 
28530     },
28531     onTabClick : function(ev,un,ob,pane)
28532     {
28533         //Roo.log('tab - prev default');
28534         ev.preventDefault();
28535         
28536         
28537         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28538         pane.tab.addClass('active');
28539         //Roo.log(pane.title);
28540         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28541         // technically we should have a deactivate event.. but maybe add later.
28542         // and it should not de-activate the selected tab...
28543         this.fireEvent('activatepane', pane);
28544         pane.el.addClass('active');
28545         pane.fireEvent('activate');
28546         
28547         
28548     },
28549     
28550     getActivePane : function()
28551     {
28552         var r = false;
28553         Roo.each(this.panes, function(p) {
28554             if(p.el.hasClass('active')){
28555                 r = p;
28556                 return false;
28557             }
28558             
28559             return;
28560         });
28561         
28562         return r;
28563     }
28564     
28565     
28566 });
28567
28568  
28569 /*
28570  * - LGPL
28571  *
28572  * Tab pane
28573  * 
28574  */
28575 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28576 /**
28577  * @class Roo.bootstrap.TabPane
28578  * @extends Roo.bootstrap.Component
28579  * Bootstrap TabPane class
28580  * @cfg {Boolean} active (false | true) Default false
28581  * @cfg {String} title title of panel
28582
28583  * 
28584  * @constructor
28585  * Create a new TabPane
28586  * @param {Object} config The config object
28587  */
28588
28589 Roo.bootstrap.dash.TabPane = function(config){
28590     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28591     
28592     this.addEvents({
28593         // raw events
28594         /**
28595          * @event activate
28596          * When a pane is activated
28597          * @param {Roo.bootstrap.dash.TabPane} pane
28598          */
28599         "activate" : true
28600          
28601     });
28602 };
28603
28604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28605     
28606     active : false,
28607     title : '',
28608     
28609     // the tabBox that this is attached to.
28610     tab : false,
28611      
28612     getAutoCreate : function() 
28613     {
28614         var cfg = {
28615             tag: 'div',
28616             cls: 'tab-pane'
28617         };
28618         
28619         if(this.active){
28620             cfg.cls += ' active';
28621         }
28622         
28623         return cfg;
28624     },
28625     initEvents  : function()
28626     {
28627         //Roo.log('trigger add pane handler');
28628         this.parent().fireEvent('addpane', this)
28629     },
28630     
28631      /**
28632      * Updates the tab title 
28633      * @param {String} html to set the title to.
28634      */
28635     setTitle: function(str)
28636     {
28637         if (!this.tab) {
28638             return;
28639         }
28640         this.title = str;
28641         this.tab.select('a', true).first().dom.innerHTML = str;
28642         
28643     }
28644     
28645     
28646     
28647 });
28648
28649  
28650
28651
28652  /*
28653  * - LGPL
28654  *
28655  * menu
28656  * 
28657  */
28658 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28659
28660 /**
28661  * @class Roo.bootstrap.menu.Menu
28662  * @extends Roo.bootstrap.Component
28663  * Bootstrap Menu class - container for Menu
28664  * @cfg {String} html Text of the menu
28665  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28666  * @cfg {String} icon Font awesome icon
28667  * @cfg {String} pos Menu align to (top | bottom) default bottom
28668  * 
28669  * 
28670  * @constructor
28671  * Create a new Menu
28672  * @param {Object} config The config object
28673  */
28674
28675
28676 Roo.bootstrap.menu.Menu = function(config){
28677     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28678     
28679     this.addEvents({
28680         /**
28681          * @event beforeshow
28682          * Fires before this menu is displayed
28683          * @param {Roo.bootstrap.menu.Menu} this
28684          */
28685         beforeshow : true,
28686         /**
28687          * @event beforehide
28688          * Fires before this menu is hidden
28689          * @param {Roo.bootstrap.menu.Menu} this
28690          */
28691         beforehide : true,
28692         /**
28693          * @event show
28694          * Fires after this menu is displayed
28695          * @param {Roo.bootstrap.menu.Menu} this
28696          */
28697         show : true,
28698         /**
28699          * @event hide
28700          * Fires after this menu is hidden
28701          * @param {Roo.bootstrap.menu.Menu} this
28702          */
28703         hide : true,
28704         /**
28705          * @event click
28706          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28707          * @param {Roo.bootstrap.menu.Menu} this
28708          * @param {Roo.EventObject} e
28709          */
28710         click : true
28711     });
28712     
28713 };
28714
28715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28716     
28717     submenu : false,
28718     html : '',
28719     weight : 'default',
28720     icon : false,
28721     pos : 'bottom',
28722     
28723     
28724     getChildContainer : function() {
28725         if(this.isSubMenu){
28726             return this.el;
28727         }
28728         
28729         return this.el.select('ul.dropdown-menu', true).first();  
28730     },
28731     
28732     getAutoCreate : function()
28733     {
28734         var text = [
28735             {
28736                 tag : 'span',
28737                 cls : 'roo-menu-text',
28738                 html : this.html
28739             }
28740         ];
28741         
28742         if(this.icon){
28743             text.unshift({
28744                 tag : 'i',
28745                 cls : 'fa ' + this.icon
28746             })
28747         }
28748         
28749         
28750         var cfg = {
28751             tag : 'div',
28752             cls : 'btn-group',
28753             cn : [
28754                 {
28755                     tag : 'button',
28756                     cls : 'dropdown-button btn btn-' + this.weight,
28757                     cn : text
28758                 },
28759                 {
28760                     tag : 'button',
28761                     cls : 'dropdown-toggle btn btn-' + this.weight,
28762                     cn : [
28763                         {
28764                             tag : 'span',
28765                             cls : 'caret'
28766                         }
28767                     ]
28768                 },
28769                 {
28770                     tag : 'ul',
28771                     cls : 'dropdown-menu'
28772                 }
28773             ]
28774             
28775         };
28776         
28777         if(this.pos == 'top'){
28778             cfg.cls += ' dropup';
28779         }
28780         
28781         if(this.isSubMenu){
28782             cfg = {
28783                 tag : 'ul',
28784                 cls : 'dropdown-menu'
28785             }
28786         }
28787         
28788         return cfg;
28789     },
28790     
28791     onRender : function(ct, position)
28792     {
28793         this.isSubMenu = ct.hasClass('dropdown-submenu');
28794         
28795         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28796     },
28797     
28798     initEvents : function() 
28799     {
28800         if(this.isSubMenu){
28801             return;
28802         }
28803         
28804         this.hidden = true;
28805         
28806         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28807         this.triggerEl.on('click', this.onTriggerPress, this);
28808         
28809         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28810         this.buttonEl.on('click', this.onClick, this);
28811         
28812     },
28813     
28814     list : function()
28815     {
28816         if(this.isSubMenu){
28817             return this.el;
28818         }
28819         
28820         return this.el.select('ul.dropdown-menu', true).first();
28821     },
28822     
28823     onClick : function(e)
28824     {
28825         this.fireEvent("click", this, e);
28826     },
28827     
28828     onTriggerPress  : function(e)
28829     {   
28830         if (this.isVisible()) {
28831             this.hide();
28832         } else {
28833             this.show();
28834         }
28835     },
28836     
28837     isVisible : function(){
28838         return !this.hidden;
28839     },
28840     
28841     show : function()
28842     {
28843         this.fireEvent("beforeshow", this);
28844         
28845         this.hidden = false;
28846         this.el.addClass('open');
28847         
28848         Roo.get(document).on("mouseup", this.onMouseUp, this);
28849         
28850         this.fireEvent("show", this);
28851         
28852         
28853     },
28854     
28855     hide : function()
28856     {
28857         this.fireEvent("beforehide", this);
28858         
28859         this.hidden = true;
28860         this.el.removeClass('open');
28861         
28862         Roo.get(document).un("mouseup", this.onMouseUp);
28863         
28864         this.fireEvent("hide", this);
28865     },
28866     
28867     onMouseUp : function()
28868     {
28869         this.hide();
28870     }
28871     
28872 });
28873
28874  
28875  /*
28876  * - LGPL
28877  *
28878  * menu item
28879  * 
28880  */
28881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28882
28883 /**
28884  * @class Roo.bootstrap.menu.Item
28885  * @extends Roo.bootstrap.Component
28886  * Bootstrap MenuItem class
28887  * @cfg {Boolean} submenu (true | false) default false
28888  * @cfg {String} html text of the item
28889  * @cfg {String} href the link
28890  * @cfg {Boolean} disable (true | false) default false
28891  * @cfg {Boolean} preventDefault (true | false) default true
28892  * @cfg {String} icon Font awesome icon
28893  * @cfg {String} pos Submenu align to (left | right) default right 
28894  * 
28895  * 
28896  * @constructor
28897  * Create a new Item
28898  * @param {Object} config The config object
28899  */
28900
28901
28902 Roo.bootstrap.menu.Item = function(config){
28903     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28904     this.addEvents({
28905         /**
28906          * @event mouseover
28907          * Fires when the mouse is hovering over this menu
28908          * @param {Roo.bootstrap.menu.Item} this
28909          * @param {Roo.EventObject} e
28910          */
28911         mouseover : true,
28912         /**
28913          * @event mouseout
28914          * Fires when the mouse exits this menu
28915          * @param {Roo.bootstrap.menu.Item} this
28916          * @param {Roo.EventObject} e
28917          */
28918         mouseout : true,
28919         // raw events
28920         /**
28921          * @event click
28922          * The raw click event for the entire grid.
28923          * @param {Roo.EventObject} e
28924          */
28925         click : true
28926     });
28927 };
28928
28929 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28930     
28931     submenu : false,
28932     href : '',
28933     html : '',
28934     preventDefault: true,
28935     disable : false,
28936     icon : false,
28937     pos : 'right',
28938     
28939     getAutoCreate : function()
28940     {
28941         var text = [
28942             {
28943                 tag : 'span',
28944                 cls : 'roo-menu-item-text',
28945                 html : this.html
28946             }
28947         ];
28948         
28949         if(this.icon){
28950             text.unshift({
28951                 tag : 'i',
28952                 cls : 'fa ' + this.icon
28953             })
28954         }
28955         
28956         var cfg = {
28957             tag : 'li',
28958             cn : [
28959                 {
28960                     tag : 'a',
28961                     href : this.href || '#',
28962                     cn : text
28963                 }
28964             ]
28965         };
28966         
28967         if(this.disable){
28968             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28969         }
28970         
28971         if(this.submenu){
28972             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28973             
28974             if(this.pos == 'left'){
28975                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28976             }
28977         }
28978         
28979         return cfg;
28980     },
28981     
28982     initEvents : function() 
28983     {
28984         this.el.on('mouseover', this.onMouseOver, this);
28985         this.el.on('mouseout', this.onMouseOut, this);
28986         
28987         this.el.select('a', true).first().on('click', this.onClick, this);
28988         
28989     },
28990     
28991     onClick : function(e)
28992     {
28993         if(this.preventDefault){
28994             e.preventDefault();
28995         }
28996         
28997         this.fireEvent("click", this, e);
28998     },
28999     
29000     onMouseOver : function(e)
29001     {
29002         if(this.submenu && this.pos == 'left'){
29003             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29004         }
29005         
29006         this.fireEvent("mouseover", this, e);
29007     },
29008     
29009     onMouseOut : function(e)
29010     {
29011         this.fireEvent("mouseout", this, e);
29012     }
29013 });
29014
29015  
29016
29017  /*
29018  * - LGPL
29019  *
29020  * menu separator
29021  * 
29022  */
29023 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29024
29025 /**
29026  * @class Roo.bootstrap.menu.Separator
29027  * @extends Roo.bootstrap.Component
29028  * Bootstrap Separator class
29029  * 
29030  * @constructor
29031  * Create a new Separator
29032  * @param {Object} config The config object
29033  */
29034
29035
29036 Roo.bootstrap.menu.Separator = function(config){
29037     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29038 };
29039
29040 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29041     
29042     getAutoCreate : function(){
29043         var cfg = {
29044             tag : 'li',
29045             cls: 'dropdown-divider divider'
29046         };
29047         
29048         return cfg;
29049     }
29050    
29051 });
29052
29053  
29054
29055  /*
29056  * - LGPL
29057  *
29058  * Tooltip
29059  * 
29060  */
29061
29062 /**
29063  * @class Roo.bootstrap.Tooltip
29064  * Bootstrap Tooltip class
29065  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29066  * to determine which dom element triggers the tooltip.
29067  * 
29068  * It needs to add support for additional attributes like tooltip-position
29069  * 
29070  * @constructor
29071  * Create a new Toolti
29072  * @param {Object} config The config object
29073  */
29074
29075 Roo.bootstrap.Tooltip = function(config){
29076     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29077     
29078     this.alignment = Roo.bootstrap.Tooltip.alignment;
29079     
29080     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29081         this.alignment = config.alignment;
29082     }
29083     
29084 };
29085
29086 Roo.apply(Roo.bootstrap.Tooltip, {
29087     /**
29088      * @function init initialize tooltip monitoring.
29089      * @static
29090      */
29091     currentEl : false,
29092     currentTip : false,
29093     currentRegion : false,
29094     
29095     //  init : delay?
29096     
29097     init : function()
29098     {
29099         Roo.get(document).on('mouseover', this.enter ,this);
29100         Roo.get(document).on('mouseout', this.leave, this);
29101          
29102         
29103         this.currentTip = new Roo.bootstrap.Tooltip();
29104     },
29105     
29106     enter : function(ev)
29107     {
29108         var dom = ev.getTarget();
29109         
29110         //Roo.log(['enter',dom]);
29111         var el = Roo.fly(dom);
29112         if (this.currentEl) {
29113             //Roo.log(dom);
29114             //Roo.log(this.currentEl);
29115             //Roo.log(this.currentEl.contains(dom));
29116             if (this.currentEl == el) {
29117                 return;
29118             }
29119             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29120                 return;
29121             }
29122
29123         }
29124         
29125         if (this.currentTip.el) {
29126             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29127         }    
29128         //Roo.log(ev);
29129         
29130         if(!el || el.dom == document){
29131             return;
29132         }
29133         
29134         var bindEl = el; 
29135         var pel = false;
29136         if (!el.attr('tooltip')) {
29137             pel = el.findParent("[tooltip]");
29138             if (pel) {
29139                 bindEl = Roo.get(pel);
29140             }
29141         }
29142         
29143        
29144         
29145         // you can not look for children, as if el is the body.. then everythign is the child..
29146         if (!pel && !el.attr('tooltip')) { //
29147             if (!el.select("[tooltip]").elements.length) {
29148                 return;
29149             }
29150             // is the mouse over this child...?
29151             bindEl = el.select("[tooltip]").first();
29152             var xy = ev.getXY();
29153             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29154                 //Roo.log("not in region.");
29155                 return;
29156             }
29157             //Roo.log("child element over..");
29158             
29159         }
29160         this.currentEl = el;
29161         this.currentTip.bind(bindEl);
29162         this.currentRegion = Roo.lib.Region.getRegion(dom);
29163         this.currentTip.enter();
29164         
29165     },
29166     leave : function(ev)
29167     {
29168         var dom = ev.getTarget();
29169         //Roo.log(['leave',dom]);
29170         if (!this.currentEl) {
29171             return;
29172         }
29173         
29174         
29175         if (dom != this.currentEl.dom) {
29176             return;
29177         }
29178         var xy = ev.getXY();
29179         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29180             return;
29181         }
29182         // only activate leave if mouse cursor is outside... bounding box..
29183         
29184         
29185         
29186         
29187         if (this.currentTip) {
29188             this.currentTip.leave();
29189         }
29190         //Roo.log('clear currentEl');
29191         this.currentEl = false;
29192         
29193         
29194     },
29195     alignment : {
29196         'left' : ['r-l', [-2,0], 'right'],
29197         'right' : ['l-r', [2,0], 'left'],
29198         'bottom' : ['t-b', [0,2], 'top'],
29199         'top' : [ 'b-t', [0,-2], 'bottom']
29200     }
29201     
29202 });
29203
29204
29205 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29206     
29207     
29208     bindEl : false,
29209     
29210     delay : null, // can be { show : 300 , hide: 500}
29211     
29212     timeout : null,
29213     
29214     hoverState : null, //???
29215     
29216     placement : 'bottom', 
29217     
29218     alignment : false,
29219     
29220     getAutoCreate : function(){
29221     
29222         var cfg = {
29223            cls : 'tooltip',   
29224            role : 'tooltip',
29225            cn : [
29226                 {
29227                     cls : 'tooltip-arrow arrow'
29228                 },
29229                 {
29230                     cls : 'tooltip-inner'
29231                 }
29232            ]
29233         };
29234         
29235         return cfg;
29236     },
29237     bind : function(el)
29238     {
29239         this.bindEl = el;
29240     },
29241     
29242     initEvents : function()
29243     {
29244         this.arrowEl = this.el.select('.arrow', true).first();
29245         this.innerEl = this.el.select('.tooltip-inner', true).first();
29246     },
29247     
29248     enter : function () {
29249        
29250         if (this.timeout != null) {
29251             clearTimeout(this.timeout);
29252         }
29253         
29254         this.hoverState = 'in';
29255          //Roo.log("enter - show");
29256         if (!this.delay || !this.delay.show) {
29257             this.show();
29258             return;
29259         }
29260         var _t = this;
29261         this.timeout = setTimeout(function () {
29262             if (_t.hoverState == 'in') {
29263                 _t.show();
29264             }
29265         }, this.delay.show);
29266     },
29267     leave : function()
29268     {
29269         clearTimeout(this.timeout);
29270     
29271         this.hoverState = 'out';
29272          if (!this.delay || !this.delay.hide) {
29273             this.hide();
29274             return;
29275         }
29276        
29277         var _t = this;
29278         this.timeout = setTimeout(function () {
29279             //Roo.log("leave - timeout");
29280             
29281             if (_t.hoverState == 'out') {
29282                 _t.hide();
29283                 Roo.bootstrap.Tooltip.currentEl = false;
29284             }
29285         }, delay);
29286     },
29287     
29288     show : function (msg)
29289     {
29290         if (!this.el) {
29291             this.render(document.body);
29292         }
29293         // set content.
29294         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29295         
29296         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29297         
29298         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29299         
29300         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29301                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29302         
29303         var placement = typeof this.placement == 'function' ?
29304             this.placement.call(this, this.el, on_el) :
29305             this.placement;
29306             
29307         var autoToken = /\s?auto?\s?/i;
29308         var autoPlace = autoToken.test(placement);
29309         if (autoPlace) {
29310             placement = placement.replace(autoToken, '') || 'top';
29311         }
29312         
29313         //this.el.detach()
29314         //this.el.setXY([0,0]);
29315         this.el.show();
29316         //this.el.dom.style.display='block';
29317         
29318         //this.el.appendTo(on_el);
29319         
29320         var p = this.getPosition();
29321         var box = this.el.getBox();
29322         
29323         if (autoPlace) {
29324             // fixme..
29325         }
29326         
29327         var align = this.alignment[placement];
29328         
29329         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29330         
29331         if(placement == 'top' || placement == 'bottom'){
29332             if(xy[0] < 0){
29333                 placement = 'right';
29334             }
29335             
29336             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29337                 placement = 'left';
29338             }
29339             
29340             var scroll = Roo.select('body', true).first().getScroll();
29341             
29342             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29343                 placement = 'top';
29344             }
29345             
29346             align = this.alignment[placement];
29347             
29348             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29349             
29350         }
29351         
29352         var elems = document.getElementsByTagName('div');
29353         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29354         for (var i = 0; i < elems.length; i++) {
29355           var zindex = Number.parseInt(
29356                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29357                 10
29358           );
29359           if (zindex > highest) {
29360             highest = zindex;
29361           }
29362         }
29363         
29364         
29365         
29366         this.el.dom.style.zIndex = highest;
29367         
29368         this.el.alignTo(this.bindEl, align[0],align[1]);
29369         //var arrow = this.el.select('.arrow',true).first();
29370         //arrow.set(align[2], 
29371         
29372         this.el.addClass(placement);
29373         this.el.addClass("bs-tooltip-"+ placement);
29374         
29375         this.el.addClass('in fade show');
29376         
29377         this.hoverState = null;
29378         
29379         if (this.el.hasClass('fade')) {
29380             // fade it?
29381         }
29382         
29383         
29384         
29385         
29386         
29387     },
29388     hide : function()
29389     {
29390          
29391         if (!this.el) {
29392             return;
29393         }
29394         //this.el.setXY([0,0]);
29395         this.el.removeClass(['show', 'in']);
29396         //this.el.hide();
29397         
29398     }
29399     
29400 });
29401  
29402
29403  /*
29404  * - LGPL
29405  *
29406  * Location Picker
29407  * 
29408  */
29409
29410 /**
29411  * @class Roo.bootstrap.LocationPicker
29412  * @extends Roo.bootstrap.Component
29413  * Bootstrap LocationPicker class
29414  * @cfg {Number} latitude Position when init default 0
29415  * @cfg {Number} longitude Position when init default 0
29416  * @cfg {Number} zoom default 15
29417  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29418  * @cfg {Boolean} mapTypeControl default false
29419  * @cfg {Boolean} disableDoubleClickZoom default false
29420  * @cfg {Boolean} scrollwheel default true
29421  * @cfg {Boolean} streetViewControl default false
29422  * @cfg {Number} radius default 0
29423  * @cfg {String} locationName
29424  * @cfg {Boolean} draggable default true
29425  * @cfg {Boolean} enableAutocomplete default false
29426  * @cfg {Boolean} enableReverseGeocode default true
29427  * @cfg {String} markerTitle
29428  * 
29429  * @constructor
29430  * Create a new LocationPicker
29431  * @param {Object} config The config object
29432  */
29433
29434
29435 Roo.bootstrap.LocationPicker = function(config){
29436     
29437     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29438     
29439     this.addEvents({
29440         /**
29441          * @event initial
29442          * Fires when the picker initialized.
29443          * @param {Roo.bootstrap.LocationPicker} this
29444          * @param {Google Location} location
29445          */
29446         initial : true,
29447         /**
29448          * @event positionchanged
29449          * Fires when the picker position changed.
29450          * @param {Roo.bootstrap.LocationPicker} this
29451          * @param {Google Location} location
29452          */
29453         positionchanged : true,
29454         /**
29455          * @event resize
29456          * Fires when the map resize.
29457          * @param {Roo.bootstrap.LocationPicker} this
29458          */
29459         resize : true,
29460         /**
29461          * @event show
29462          * Fires when the map show.
29463          * @param {Roo.bootstrap.LocationPicker} this
29464          */
29465         show : true,
29466         /**
29467          * @event hide
29468          * Fires when the map hide.
29469          * @param {Roo.bootstrap.LocationPicker} this
29470          */
29471         hide : true,
29472         /**
29473          * @event mapClick
29474          * Fires when click the map.
29475          * @param {Roo.bootstrap.LocationPicker} this
29476          * @param {Map event} e
29477          */
29478         mapClick : true,
29479         /**
29480          * @event mapRightClick
29481          * Fires when right click the map.
29482          * @param {Roo.bootstrap.LocationPicker} this
29483          * @param {Map event} e
29484          */
29485         mapRightClick : true,
29486         /**
29487          * @event markerClick
29488          * Fires when click the marker.
29489          * @param {Roo.bootstrap.LocationPicker} this
29490          * @param {Map event} e
29491          */
29492         markerClick : true,
29493         /**
29494          * @event markerRightClick
29495          * Fires when right click the marker.
29496          * @param {Roo.bootstrap.LocationPicker} this
29497          * @param {Map event} e
29498          */
29499         markerRightClick : true,
29500         /**
29501          * @event OverlayViewDraw
29502          * Fires when OverlayView Draw
29503          * @param {Roo.bootstrap.LocationPicker} this
29504          */
29505         OverlayViewDraw : true,
29506         /**
29507          * @event OverlayViewOnAdd
29508          * Fires when OverlayView Draw
29509          * @param {Roo.bootstrap.LocationPicker} this
29510          */
29511         OverlayViewOnAdd : true,
29512         /**
29513          * @event OverlayViewOnRemove
29514          * Fires when OverlayView Draw
29515          * @param {Roo.bootstrap.LocationPicker} this
29516          */
29517         OverlayViewOnRemove : true,
29518         /**
29519          * @event OverlayViewShow
29520          * Fires when OverlayView Draw
29521          * @param {Roo.bootstrap.LocationPicker} this
29522          * @param {Pixel} cpx
29523          */
29524         OverlayViewShow : true,
29525         /**
29526          * @event OverlayViewHide
29527          * Fires when OverlayView Draw
29528          * @param {Roo.bootstrap.LocationPicker} this
29529          */
29530         OverlayViewHide : true,
29531         /**
29532          * @event loadexception
29533          * Fires when load google lib failed.
29534          * @param {Roo.bootstrap.LocationPicker} this
29535          */
29536         loadexception : true
29537     });
29538         
29539 };
29540
29541 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29542     
29543     gMapContext: false,
29544     
29545     latitude: 0,
29546     longitude: 0,
29547     zoom: 15,
29548     mapTypeId: false,
29549     mapTypeControl: false,
29550     disableDoubleClickZoom: false,
29551     scrollwheel: true,
29552     streetViewControl: false,
29553     radius: 0,
29554     locationName: '',
29555     draggable: true,
29556     enableAutocomplete: false,
29557     enableReverseGeocode: true,
29558     markerTitle: '',
29559     
29560     getAutoCreate: function()
29561     {
29562
29563         var cfg = {
29564             tag: 'div',
29565             cls: 'roo-location-picker'
29566         };
29567         
29568         return cfg
29569     },
29570     
29571     initEvents: function(ct, position)
29572     {       
29573         if(!this.el.getWidth() || this.isApplied()){
29574             return;
29575         }
29576         
29577         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29578         
29579         this.initial();
29580     },
29581     
29582     initial: function()
29583     {
29584         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29585             this.fireEvent('loadexception', this);
29586             return;
29587         }
29588         
29589         if(!this.mapTypeId){
29590             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29591         }
29592         
29593         this.gMapContext = this.GMapContext();
29594         
29595         this.initOverlayView();
29596         
29597         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29598         
29599         var _this = this;
29600                 
29601         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29602             _this.setPosition(_this.gMapContext.marker.position);
29603         });
29604         
29605         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29606             _this.fireEvent('mapClick', this, event);
29607             
29608         });
29609
29610         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29611             _this.fireEvent('mapRightClick', this, event);
29612             
29613         });
29614         
29615         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29616             _this.fireEvent('markerClick', this, event);
29617             
29618         });
29619
29620         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29621             _this.fireEvent('markerRightClick', this, event);
29622             
29623         });
29624         
29625         this.setPosition(this.gMapContext.location);
29626         
29627         this.fireEvent('initial', this, this.gMapContext.location);
29628     },
29629     
29630     initOverlayView: function()
29631     {
29632         var _this = this;
29633         
29634         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29635             
29636             draw: function()
29637             {
29638                 _this.fireEvent('OverlayViewDraw', _this);
29639             },
29640             
29641             onAdd: function()
29642             {
29643                 _this.fireEvent('OverlayViewOnAdd', _this);
29644             },
29645             
29646             onRemove: function()
29647             {
29648                 _this.fireEvent('OverlayViewOnRemove', _this);
29649             },
29650             
29651             show: function(cpx)
29652             {
29653                 _this.fireEvent('OverlayViewShow', _this, cpx);
29654             },
29655             
29656             hide: function()
29657             {
29658                 _this.fireEvent('OverlayViewHide', _this);
29659             }
29660             
29661         });
29662     },
29663     
29664     fromLatLngToContainerPixel: function(event)
29665     {
29666         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29667     },
29668     
29669     isApplied: function() 
29670     {
29671         return this.getGmapContext() == false ? false : true;
29672     },
29673     
29674     getGmapContext: function() 
29675     {
29676         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29677     },
29678     
29679     GMapContext: function() 
29680     {
29681         var position = new google.maps.LatLng(this.latitude, this.longitude);
29682         
29683         var _map = new google.maps.Map(this.el.dom, {
29684             center: position,
29685             zoom: this.zoom,
29686             mapTypeId: this.mapTypeId,
29687             mapTypeControl: this.mapTypeControl,
29688             disableDoubleClickZoom: this.disableDoubleClickZoom,
29689             scrollwheel: this.scrollwheel,
29690             streetViewControl: this.streetViewControl,
29691             locationName: this.locationName,
29692             draggable: this.draggable,
29693             enableAutocomplete: this.enableAutocomplete,
29694             enableReverseGeocode: this.enableReverseGeocode
29695         });
29696         
29697         var _marker = new google.maps.Marker({
29698             position: position,
29699             map: _map,
29700             title: this.markerTitle,
29701             draggable: this.draggable
29702         });
29703         
29704         return {
29705             map: _map,
29706             marker: _marker,
29707             circle: null,
29708             location: position,
29709             radius: this.radius,
29710             locationName: this.locationName,
29711             addressComponents: {
29712                 formatted_address: null,
29713                 addressLine1: null,
29714                 addressLine2: null,
29715                 streetName: null,
29716                 streetNumber: null,
29717                 city: null,
29718                 district: null,
29719                 state: null,
29720                 stateOrProvince: null
29721             },
29722             settings: this,
29723             domContainer: this.el.dom,
29724             geodecoder: new google.maps.Geocoder()
29725         };
29726     },
29727     
29728     drawCircle: function(center, radius, options) 
29729     {
29730         if (this.gMapContext.circle != null) {
29731             this.gMapContext.circle.setMap(null);
29732         }
29733         if (radius > 0) {
29734             radius *= 1;
29735             options = Roo.apply({}, options, {
29736                 strokeColor: "#0000FF",
29737                 strokeOpacity: .35,
29738                 strokeWeight: 2,
29739                 fillColor: "#0000FF",
29740                 fillOpacity: .2
29741             });
29742             
29743             options.map = this.gMapContext.map;
29744             options.radius = radius;
29745             options.center = center;
29746             this.gMapContext.circle = new google.maps.Circle(options);
29747             return this.gMapContext.circle;
29748         }
29749         
29750         return null;
29751     },
29752     
29753     setPosition: function(location) 
29754     {
29755         this.gMapContext.location = location;
29756         this.gMapContext.marker.setPosition(location);
29757         this.gMapContext.map.panTo(location);
29758         this.drawCircle(location, this.gMapContext.radius, {});
29759         
29760         var _this = this;
29761         
29762         if (this.gMapContext.settings.enableReverseGeocode) {
29763             this.gMapContext.geodecoder.geocode({
29764                 latLng: this.gMapContext.location
29765             }, function(results, status) {
29766                 
29767                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29768                     _this.gMapContext.locationName = results[0].formatted_address;
29769                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29770                     
29771                     _this.fireEvent('positionchanged', this, location);
29772                 }
29773             });
29774             
29775             return;
29776         }
29777         
29778         this.fireEvent('positionchanged', this, location);
29779     },
29780     
29781     resize: function()
29782     {
29783         google.maps.event.trigger(this.gMapContext.map, "resize");
29784         
29785         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29786         
29787         this.fireEvent('resize', this);
29788     },
29789     
29790     setPositionByLatLng: function(latitude, longitude)
29791     {
29792         this.setPosition(new google.maps.LatLng(latitude, longitude));
29793     },
29794     
29795     getCurrentPosition: function() 
29796     {
29797         return {
29798             latitude: this.gMapContext.location.lat(),
29799             longitude: this.gMapContext.location.lng()
29800         };
29801     },
29802     
29803     getAddressName: function() 
29804     {
29805         return this.gMapContext.locationName;
29806     },
29807     
29808     getAddressComponents: function() 
29809     {
29810         return this.gMapContext.addressComponents;
29811     },
29812     
29813     address_component_from_google_geocode: function(address_components) 
29814     {
29815         var result = {};
29816         
29817         for (var i = 0; i < address_components.length; i++) {
29818             var component = address_components[i];
29819             if (component.types.indexOf("postal_code") >= 0) {
29820                 result.postalCode = component.short_name;
29821             } else if (component.types.indexOf("street_number") >= 0) {
29822                 result.streetNumber = component.short_name;
29823             } else if (component.types.indexOf("route") >= 0) {
29824                 result.streetName = component.short_name;
29825             } else if (component.types.indexOf("neighborhood") >= 0) {
29826                 result.city = component.short_name;
29827             } else if (component.types.indexOf("locality") >= 0) {
29828                 result.city = component.short_name;
29829             } else if (component.types.indexOf("sublocality") >= 0) {
29830                 result.district = component.short_name;
29831             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29832                 result.stateOrProvince = component.short_name;
29833             } else if (component.types.indexOf("country") >= 0) {
29834                 result.country = component.short_name;
29835             }
29836         }
29837         
29838         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29839         result.addressLine2 = "";
29840         return result;
29841     },
29842     
29843     setZoomLevel: function(zoom)
29844     {
29845         this.gMapContext.map.setZoom(zoom);
29846     },
29847     
29848     show: function()
29849     {
29850         if(!this.el){
29851             return;
29852         }
29853         
29854         this.el.show();
29855         
29856         this.resize();
29857         
29858         this.fireEvent('show', this);
29859     },
29860     
29861     hide: function()
29862     {
29863         if(!this.el){
29864             return;
29865         }
29866         
29867         this.el.hide();
29868         
29869         this.fireEvent('hide', this);
29870     }
29871     
29872 });
29873
29874 Roo.apply(Roo.bootstrap.LocationPicker, {
29875     
29876     OverlayView : function(map, options)
29877     {
29878         options = options || {};
29879         
29880         this.setMap(map);
29881     }
29882     
29883     
29884 });/**
29885  * @class Roo.bootstrap.Alert
29886  * @extends Roo.bootstrap.Component
29887  * Bootstrap Alert class - shows an alert area box
29888  * eg
29889  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29890   Enter a valid email address
29891 </div>
29892  * @licence LGPL
29893  * @cfg {String} title The title of alert
29894  * @cfg {String} html The content of alert
29895  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29896  * @cfg {String} fa font-awesomeicon
29897  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29898  * @cfg {Boolean} close true to show a x closer
29899  * 
29900  * 
29901  * @constructor
29902  * Create a new alert
29903  * @param {Object} config The config object
29904  */
29905
29906
29907 Roo.bootstrap.Alert = function(config){
29908     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29909     
29910 };
29911
29912 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29913     
29914     title: '',
29915     html: '',
29916     weight: false,
29917     fa: false,
29918     faicon: false, // BC
29919     close : false,
29920     
29921     
29922     getAutoCreate : function()
29923     {
29924         
29925         var cfg = {
29926             tag : 'div',
29927             cls : 'alert',
29928             cn : [
29929                 {
29930                     tag: 'button',
29931                     type :  "button",
29932                     cls: "close",
29933                     html : '×',
29934                     style : this.close ? '' : 'display:none'
29935                 },
29936                 {
29937                     tag : 'i',
29938                     cls : 'roo-alert-icon'
29939                     
29940                 },
29941                 {
29942                     tag : 'b',
29943                     cls : 'roo-alert-title',
29944                     html : this.title
29945                 },
29946                 {
29947                     tag : 'span',
29948                     cls : 'roo-alert-text',
29949                     html : this.html
29950                 }
29951             ]
29952         };
29953         
29954         if(this.faicon){
29955             cfg.cn[0].cls += ' fa ' + this.faicon;
29956         }
29957         if(this.fa){
29958             cfg.cn[0].cls += ' fa ' + this.fa;
29959         }
29960         
29961         if(this.weight){
29962             cfg.cls += ' alert-' + this.weight;
29963         }
29964         
29965         return cfg;
29966     },
29967     
29968     initEvents: function() 
29969     {
29970         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29971         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29972         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29973         this.htmlEl = this.el.select('.roo-alert-text',true).first();
29974         if (this.seconds > 0) {
29975             this.hide.defer(this.seconds, this);
29976         }
29977     },
29978     /**
29979      * Set the Title Message HTML
29980      * @param {String} html
29981      */
29982     setTitle : function(str)
29983     {
29984         this.titleEl.dom.innerHTML = str;
29985     },
29986      
29987      /**
29988      * Set the Body Message HTML
29989      * @param {String} html
29990      */
29991     setHtml : function(str)
29992     {
29993         this.htmlEl.dom.innerHTML = str;
29994     },
29995     /**
29996      * Set the Weight of the alert
29997      * @param {String} (success|info|warning|danger) weight
29998      */
29999     
30000     setWeight : function(weight)
30001     {
30002         if(this.weight){
30003             this.el.removeClass('alert-' + this.weight);
30004         }
30005         
30006         this.weight = weight;
30007         
30008         this.el.addClass('alert-' + this.weight);
30009     },
30010       /**
30011      * Set the Icon of the alert
30012      * @param {String} see fontawsome names (name without the 'fa-' bit)
30013      */
30014     setIcon : function(icon)
30015     {
30016         if(this.faicon){
30017             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30018         }
30019         
30020         this.faicon = icon;
30021         
30022         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30023     },
30024     /**
30025      * Hide the Alert
30026      */
30027     hide: function() 
30028     {
30029         this.el.hide();   
30030     },
30031     /**
30032      * Show the Alert
30033      */
30034     show: function() 
30035     {  
30036         this.el.show();   
30037     }
30038     
30039 });
30040
30041  
30042 /*
30043 * Licence: LGPL
30044 */
30045
30046 /**
30047  * @class Roo.bootstrap.UploadCropbox
30048  * @extends Roo.bootstrap.Component
30049  * Bootstrap UploadCropbox class
30050  * @cfg {String} emptyText show when image has been loaded
30051  * @cfg {String} rotateNotify show when image too small to rotate
30052  * @cfg {Number} errorTimeout default 3000
30053  * @cfg {Number} minWidth default 300
30054  * @cfg {Number} minHeight default 300
30055  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30056  * @cfg {Boolean} isDocument (true|false) default false
30057  * @cfg {String} url action url
30058  * @cfg {String} paramName default 'imageUpload'
30059  * @cfg {String} method default POST
30060  * @cfg {Boolean} loadMask (true|false) default true
30061  * @cfg {Boolean} loadingText default 'Loading...'
30062  * 
30063  * @constructor
30064  * Create a new UploadCropbox
30065  * @param {Object} config The config object
30066  */
30067
30068 Roo.bootstrap.UploadCropbox = function(config){
30069     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30070     
30071     this.addEvents({
30072         /**
30073          * @event beforeselectfile
30074          * Fire before select file
30075          * @param {Roo.bootstrap.UploadCropbox} this
30076          */
30077         "beforeselectfile" : true,
30078         /**
30079          * @event initial
30080          * Fire after initEvent
30081          * @param {Roo.bootstrap.UploadCropbox} this
30082          */
30083         "initial" : true,
30084         /**
30085          * @event crop
30086          * Fire after initEvent
30087          * @param {Roo.bootstrap.UploadCropbox} this
30088          * @param {String} data
30089          */
30090         "crop" : true,
30091         /**
30092          * @event prepare
30093          * Fire when preparing the file data
30094          * @param {Roo.bootstrap.UploadCropbox} this
30095          * @param {Object} file
30096          */
30097         "prepare" : true,
30098         /**
30099          * @event exception
30100          * Fire when get exception
30101          * @param {Roo.bootstrap.UploadCropbox} this
30102          * @param {XMLHttpRequest} xhr
30103          */
30104         "exception" : true,
30105         /**
30106          * @event beforeloadcanvas
30107          * Fire before load the canvas
30108          * @param {Roo.bootstrap.UploadCropbox} this
30109          * @param {String} src
30110          */
30111         "beforeloadcanvas" : true,
30112         /**
30113          * @event trash
30114          * Fire when trash image
30115          * @param {Roo.bootstrap.UploadCropbox} this
30116          */
30117         "trash" : true,
30118         /**
30119          * @event download
30120          * Fire when download the image
30121          * @param {Roo.bootstrap.UploadCropbox} this
30122          */
30123         "download" : true,
30124         /**
30125          * @event footerbuttonclick
30126          * Fire when footerbuttonclick
30127          * @param {Roo.bootstrap.UploadCropbox} this
30128          * @param {String} type
30129          */
30130         "footerbuttonclick" : true,
30131         /**
30132          * @event resize
30133          * Fire when resize
30134          * @param {Roo.bootstrap.UploadCropbox} this
30135          */
30136         "resize" : true,
30137         /**
30138          * @event rotate
30139          * Fire when rotate the image
30140          * @param {Roo.bootstrap.UploadCropbox} this
30141          * @param {String} pos
30142          */
30143         "rotate" : true,
30144         /**
30145          * @event inspect
30146          * Fire when inspect the file
30147          * @param {Roo.bootstrap.UploadCropbox} this
30148          * @param {Object} file
30149          */
30150         "inspect" : true,
30151         /**
30152          * @event upload
30153          * Fire when xhr upload the file
30154          * @param {Roo.bootstrap.UploadCropbox} this
30155          * @param {Object} data
30156          */
30157         "upload" : true,
30158         /**
30159          * @event arrange
30160          * Fire when arrange the file data
30161          * @param {Roo.bootstrap.UploadCropbox} this
30162          * @param {Object} formData
30163          */
30164         "arrange" : true
30165     });
30166     
30167     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30168 };
30169
30170 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30171     
30172     emptyText : 'Click to upload image',
30173     rotateNotify : 'Image is too small to rotate',
30174     errorTimeout : 3000,
30175     scale : 0,
30176     baseScale : 1,
30177     rotate : 0,
30178     dragable : false,
30179     pinching : false,
30180     mouseX : 0,
30181     mouseY : 0,
30182     cropData : false,
30183     minWidth : 300,
30184     minHeight : 300,
30185     file : false,
30186     exif : {},
30187     baseRotate : 1,
30188     cropType : 'image/jpeg',
30189     buttons : false,
30190     canvasLoaded : false,
30191     isDocument : false,
30192     method : 'POST',
30193     paramName : 'imageUpload',
30194     loadMask : true,
30195     loadingText : 'Loading...',
30196     maskEl : false,
30197     
30198     getAutoCreate : function()
30199     {
30200         var cfg = {
30201             tag : 'div',
30202             cls : 'roo-upload-cropbox',
30203             cn : [
30204                 {
30205                     tag : 'input',
30206                     cls : 'roo-upload-cropbox-selector',
30207                     type : 'file'
30208                 },
30209                 {
30210                     tag : 'div',
30211                     cls : 'roo-upload-cropbox-body',
30212                     style : 'cursor:pointer',
30213                     cn : [
30214                         {
30215                             tag : 'div',
30216                             cls : 'roo-upload-cropbox-preview'
30217                         },
30218                         {
30219                             tag : 'div',
30220                             cls : 'roo-upload-cropbox-thumb'
30221                         },
30222                         {
30223                             tag : 'div',
30224                             cls : 'roo-upload-cropbox-empty-notify',
30225                             html : this.emptyText
30226                         },
30227                         {
30228                             tag : 'div',
30229                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30230                             html : this.rotateNotify
30231                         }
30232                     ]
30233                 },
30234                 {
30235                     tag : 'div',
30236                     cls : 'roo-upload-cropbox-footer',
30237                     cn : {
30238                         tag : 'div',
30239                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30240                         cn : []
30241                     }
30242                 }
30243             ]
30244         };
30245         
30246         return cfg;
30247     },
30248     
30249     onRender : function(ct, position)
30250     {
30251         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30252         
30253         if (this.buttons.length) {
30254             
30255             Roo.each(this.buttons, function(bb) {
30256                 
30257                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30258                 
30259                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30260                 
30261             }, this);
30262         }
30263         
30264         if(this.loadMask){
30265             this.maskEl = this.el;
30266         }
30267     },
30268     
30269     initEvents : function()
30270     {
30271         this.urlAPI = (window.createObjectURL && window) || 
30272                                 (window.URL && URL.revokeObjectURL && URL) || 
30273                                 (window.webkitURL && webkitURL);
30274                         
30275         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30276         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30277         
30278         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30279         this.selectorEl.hide();
30280         
30281         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30282         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30283         
30284         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30285         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30286         this.thumbEl.hide();
30287         
30288         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30289         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30290         
30291         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30292         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30293         this.errorEl.hide();
30294         
30295         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30296         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30297         this.footerEl.hide();
30298         
30299         this.setThumbBoxSize();
30300         
30301         this.bind();
30302         
30303         this.resize();
30304         
30305         this.fireEvent('initial', this);
30306     },
30307
30308     bind : function()
30309     {
30310         var _this = this;
30311         
30312         window.addEventListener("resize", function() { _this.resize(); } );
30313         
30314         this.bodyEl.on('click', this.beforeSelectFile, this);
30315         
30316         if(Roo.isTouch){
30317             this.bodyEl.on('touchstart', this.onTouchStart, this);
30318             this.bodyEl.on('touchmove', this.onTouchMove, this);
30319             this.bodyEl.on('touchend', this.onTouchEnd, this);
30320         }
30321         
30322         if(!Roo.isTouch){
30323             this.bodyEl.on('mousedown', this.onMouseDown, this);
30324             this.bodyEl.on('mousemove', this.onMouseMove, this);
30325             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30326             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30327             Roo.get(document).on('mouseup', this.onMouseUp, this);
30328         }
30329         
30330         this.selectorEl.on('change', this.onFileSelected, this);
30331     },
30332     
30333     reset : function()
30334     {    
30335         this.scale = 0;
30336         this.baseScale = 1;
30337         this.rotate = 0;
30338         this.baseRotate = 1;
30339         this.dragable = false;
30340         this.pinching = false;
30341         this.mouseX = 0;
30342         this.mouseY = 0;
30343         this.cropData = false;
30344         this.notifyEl.dom.innerHTML = this.emptyText;
30345         
30346         this.selectorEl.dom.value = '';
30347         
30348     },
30349     
30350     resize : function()
30351     {
30352         if(this.fireEvent('resize', this) != false){
30353             this.setThumbBoxPosition();
30354             this.setCanvasPosition();
30355         }
30356     },
30357     
30358     onFooterButtonClick : function(e, el, o, type)
30359     {
30360         switch (type) {
30361             case 'rotate-left' :
30362                 this.onRotateLeft(e);
30363                 break;
30364             case 'rotate-right' :
30365                 this.onRotateRight(e);
30366                 break;
30367             case 'picture' :
30368                 this.beforeSelectFile(e);
30369                 break;
30370             case 'trash' :
30371                 this.trash(e);
30372                 break;
30373             case 'crop' :
30374                 this.crop(e);
30375                 break;
30376             case 'download' :
30377                 this.download(e);
30378                 break;
30379             default :
30380                 break;
30381         }
30382         
30383         this.fireEvent('footerbuttonclick', this, type);
30384     },
30385     
30386     beforeSelectFile : function(e)
30387     {
30388         e.preventDefault();
30389         
30390         if(this.fireEvent('beforeselectfile', this) != false){
30391             this.selectorEl.dom.click();
30392         }
30393     },
30394     
30395     onFileSelected : function(e)
30396     {
30397         e.preventDefault();
30398         
30399         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30400             return;
30401         }
30402         
30403         var file = this.selectorEl.dom.files[0];
30404         
30405         if(this.fireEvent('inspect', this, file) != false){
30406             this.prepare(file);
30407         }
30408         
30409     },
30410     
30411     trash : function(e)
30412     {
30413         this.fireEvent('trash', this);
30414     },
30415     
30416     download : function(e)
30417     {
30418         this.fireEvent('download', this);
30419     },
30420     
30421     loadCanvas : function(src)
30422     {   
30423         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30424             
30425             this.reset();
30426             
30427             this.imageEl = document.createElement('img');
30428             
30429             var _this = this;
30430             
30431             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30432             
30433             this.imageEl.src = src;
30434         }
30435     },
30436     
30437     onLoadCanvas : function()
30438     {   
30439         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30440         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30441         
30442         this.bodyEl.un('click', this.beforeSelectFile, this);
30443         
30444         this.notifyEl.hide();
30445         this.thumbEl.show();
30446         this.footerEl.show();
30447         
30448         this.baseRotateLevel();
30449         
30450         if(this.isDocument){
30451             this.setThumbBoxSize();
30452         }
30453         
30454         this.setThumbBoxPosition();
30455         
30456         this.baseScaleLevel();
30457         
30458         this.draw();
30459         
30460         this.resize();
30461         
30462         this.canvasLoaded = true;
30463         
30464         if(this.loadMask){
30465             this.maskEl.unmask();
30466         }
30467         
30468     },
30469     
30470     setCanvasPosition : function()
30471     {   
30472         if(!this.canvasEl){
30473             return;
30474         }
30475         
30476         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30477         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30478         
30479         this.previewEl.setLeft(pw);
30480         this.previewEl.setTop(ph);
30481         
30482     },
30483     
30484     onMouseDown : function(e)
30485     {   
30486         e.stopEvent();
30487         
30488         this.dragable = true;
30489         this.pinching = false;
30490         
30491         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30492             this.dragable = false;
30493             return;
30494         }
30495         
30496         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30497         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30498         
30499     },
30500     
30501     onMouseMove : function(e)
30502     {   
30503         e.stopEvent();
30504         
30505         if(!this.canvasLoaded){
30506             return;
30507         }
30508         
30509         if (!this.dragable){
30510             return;
30511         }
30512         
30513         var minX = Math.ceil(this.thumbEl.getLeft(true));
30514         var minY = Math.ceil(this.thumbEl.getTop(true));
30515         
30516         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30517         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30518         
30519         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30520         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30521         
30522         x = x - this.mouseX;
30523         y = y - this.mouseY;
30524         
30525         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30526         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30527         
30528         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30529         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30530         
30531         this.previewEl.setLeft(bgX);
30532         this.previewEl.setTop(bgY);
30533         
30534         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30535         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30536     },
30537     
30538     onMouseUp : function(e)
30539     {   
30540         e.stopEvent();
30541         
30542         this.dragable = false;
30543     },
30544     
30545     onMouseWheel : function(e)
30546     {   
30547         e.stopEvent();
30548         
30549         this.startScale = this.scale;
30550         
30551         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30552         
30553         if(!this.zoomable()){
30554             this.scale = this.startScale;
30555             return;
30556         }
30557         
30558         this.draw();
30559         
30560         return;
30561     },
30562     
30563     zoomable : function()
30564     {
30565         var minScale = this.thumbEl.getWidth() / this.minWidth;
30566         
30567         if(this.minWidth < this.minHeight){
30568             minScale = this.thumbEl.getHeight() / this.minHeight;
30569         }
30570         
30571         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30572         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30573         
30574         if(
30575                 this.isDocument &&
30576                 (this.rotate == 0 || this.rotate == 180) && 
30577                 (
30578                     width > this.imageEl.OriginWidth || 
30579                     height > this.imageEl.OriginHeight ||
30580                     (width < this.minWidth && height < this.minHeight)
30581                 )
30582         ){
30583             return false;
30584         }
30585         
30586         if(
30587                 this.isDocument &&
30588                 (this.rotate == 90 || this.rotate == 270) && 
30589                 (
30590                     width > this.imageEl.OriginWidth || 
30591                     height > this.imageEl.OriginHeight ||
30592                     (width < this.minHeight && height < this.minWidth)
30593                 )
30594         ){
30595             return false;
30596         }
30597         
30598         if(
30599                 !this.isDocument &&
30600                 (this.rotate == 0 || this.rotate == 180) && 
30601                 (
30602                     width < this.minWidth || 
30603                     width > this.imageEl.OriginWidth || 
30604                     height < this.minHeight || 
30605                     height > this.imageEl.OriginHeight
30606                 )
30607         ){
30608             return false;
30609         }
30610         
30611         if(
30612                 !this.isDocument &&
30613                 (this.rotate == 90 || this.rotate == 270) && 
30614                 (
30615                     width < this.minHeight || 
30616                     width > this.imageEl.OriginWidth || 
30617                     height < this.minWidth || 
30618                     height > this.imageEl.OriginHeight
30619                 )
30620         ){
30621             return false;
30622         }
30623         
30624         return true;
30625         
30626     },
30627     
30628     onRotateLeft : function(e)
30629     {   
30630         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30631             
30632             var minScale = this.thumbEl.getWidth() / this.minWidth;
30633             
30634             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30635             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30636             
30637             this.startScale = this.scale;
30638             
30639             while (this.getScaleLevel() < minScale){
30640             
30641                 this.scale = this.scale + 1;
30642                 
30643                 if(!this.zoomable()){
30644                     break;
30645                 }
30646                 
30647                 if(
30648                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30649                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30650                 ){
30651                     continue;
30652                 }
30653                 
30654                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30655
30656                 this.draw();
30657                 
30658                 return;
30659             }
30660             
30661             this.scale = this.startScale;
30662             
30663             this.onRotateFail();
30664             
30665             return false;
30666         }
30667         
30668         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30669
30670         if(this.isDocument){
30671             this.setThumbBoxSize();
30672             this.setThumbBoxPosition();
30673             this.setCanvasPosition();
30674         }
30675         
30676         this.draw();
30677         
30678         this.fireEvent('rotate', this, 'left');
30679         
30680     },
30681     
30682     onRotateRight : function(e)
30683     {
30684         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30685             
30686             var minScale = this.thumbEl.getWidth() / this.minWidth;
30687         
30688             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30689             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30690             
30691             this.startScale = this.scale;
30692             
30693             while (this.getScaleLevel() < minScale){
30694             
30695                 this.scale = this.scale + 1;
30696                 
30697                 if(!this.zoomable()){
30698                     break;
30699                 }
30700                 
30701                 if(
30702                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30703                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30704                 ){
30705                     continue;
30706                 }
30707                 
30708                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30709
30710                 this.draw();
30711                 
30712                 return;
30713             }
30714             
30715             this.scale = this.startScale;
30716             
30717             this.onRotateFail();
30718             
30719             return false;
30720         }
30721         
30722         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30723
30724         if(this.isDocument){
30725             this.setThumbBoxSize();
30726             this.setThumbBoxPosition();
30727             this.setCanvasPosition();
30728         }
30729         
30730         this.draw();
30731         
30732         this.fireEvent('rotate', this, 'right');
30733     },
30734     
30735     onRotateFail : function()
30736     {
30737         this.errorEl.show(true);
30738         
30739         var _this = this;
30740         
30741         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30742     },
30743     
30744     draw : function()
30745     {
30746         this.previewEl.dom.innerHTML = '';
30747         
30748         var canvasEl = document.createElement("canvas");
30749         
30750         var contextEl = canvasEl.getContext("2d");
30751         
30752         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30753         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30754         var center = this.imageEl.OriginWidth / 2;
30755         
30756         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30757             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30758             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30759             center = this.imageEl.OriginHeight / 2;
30760         }
30761         
30762         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30763         
30764         contextEl.translate(center, center);
30765         contextEl.rotate(this.rotate * Math.PI / 180);
30766
30767         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30768         
30769         this.canvasEl = document.createElement("canvas");
30770         
30771         this.contextEl = this.canvasEl.getContext("2d");
30772         
30773         switch (this.rotate) {
30774             case 0 :
30775                 
30776                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30777                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30778                 
30779                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30780                 
30781                 break;
30782             case 90 : 
30783                 
30784                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30785                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30786                 
30787                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30788                     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);
30789                     break;
30790                 }
30791                 
30792                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30793                 
30794                 break;
30795             case 180 :
30796                 
30797                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30798                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30799                 
30800                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30801                     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);
30802                     break;
30803                 }
30804                 
30805                 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);
30806                 
30807                 break;
30808             case 270 :
30809                 
30810                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30811                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30812         
30813                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30814                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30815                     break;
30816                 }
30817                 
30818                 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);
30819                 
30820                 break;
30821             default : 
30822                 break;
30823         }
30824         
30825         this.previewEl.appendChild(this.canvasEl);
30826         
30827         this.setCanvasPosition();
30828     },
30829     
30830     crop : function()
30831     {
30832         if(!this.canvasLoaded){
30833             return;
30834         }
30835         
30836         var imageCanvas = document.createElement("canvas");
30837         
30838         var imageContext = imageCanvas.getContext("2d");
30839         
30840         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30841         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30842         
30843         var center = imageCanvas.width / 2;
30844         
30845         imageContext.translate(center, center);
30846         
30847         imageContext.rotate(this.rotate * Math.PI / 180);
30848         
30849         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30850         
30851         var canvas = document.createElement("canvas");
30852         
30853         var context = canvas.getContext("2d");
30854                 
30855         canvas.width = this.minWidth;
30856         canvas.height = this.minHeight;
30857
30858         switch (this.rotate) {
30859             case 0 :
30860                 
30861                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30862                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30863                 
30864                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30865                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30866                 
30867                 var targetWidth = this.minWidth - 2 * x;
30868                 var targetHeight = this.minHeight - 2 * y;
30869                 
30870                 var scale = 1;
30871                 
30872                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30873                     scale = targetWidth / width;
30874                 }
30875                 
30876                 if(x > 0 && y == 0){
30877                     scale = targetHeight / height;
30878                 }
30879                 
30880                 if(x > 0 && y > 0){
30881                     scale = targetWidth / width;
30882                     
30883                     if(width < height){
30884                         scale = targetHeight / height;
30885                     }
30886                 }
30887                 
30888                 context.scale(scale, scale);
30889                 
30890                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30891                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30892
30893                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30894                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30895
30896                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30897                 
30898                 break;
30899             case 90 : 
30900                 
30901                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30902                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30903                 
30904                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30905                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30906                 
30907                 var targetWidth = this.minWidth - 2 * x;
30908                 var targetHeight = this.minHeight - 2 * y;
30909                 
30910                 var scale = 1;
30911                 
30912                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30913                     scale = targetWidth / width;
30914                 }
30915                 
30916                 if(x > 0 && y == 0){
30917                     scale = targetHeight / height;
30918                 }
30919                 
30920                 if(x > 0 && y > 0){
30921                     scale = targetWidth / width;
30922                     
30923                     if(width < height){
30924                         scale = targetHeight / height;
30925                     }
30926                 }
30927                 
30928                 context.scale(scale, scale);
30929                 
30930                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30931                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30932
30933                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30934                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30935                 
30936                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30937                 
30938                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30939                 
30940                 break;
30941             case 180 :
30942                 
30943                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30944                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30945                 
30946                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30947                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30948                 
30949                 var targetWidth = this.minWidth - 2 * x;
30950                 var targetHeight = this.minHeight - 2 * y;
30951                 
30952                 var scale = 1;
30953                 
30954                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30955                     scale = targetWidth / width;
30956                 }
30957                 
30958                 if(x > 0 && y == 0){
30959                     scale = targetHeight / height;
30960                 }
30961                 
30962                 if(x > 0 && y > 0){
30963                     scale = targetWidth / width;
30964                     
30965                     if(width < height){
30966                         scale = targetHeight / height;
30967                     }
30968                 }
30969                 
30970                 context.scale(scale, scale);
30971                 
30972                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30973                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30974
30975                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30976                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30977
30978                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30979                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30980                 
30981                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30982                 
30983                 break;
30984             case 270 :
30985                 
30986                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30987                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30988                 
30989                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30990                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30991                 
30992                 var targetWidth = this.minWidth - 2 * x;
30993                 var targetHeight = this.minHeight - 2 * y;
30994                 
30995                 var scale = 1;
30996                 
30997                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30998                     scale = targetWidth / width;
30999                 }
31000                 
31001                 if(x > 0 && y == 0){
31002                     scale = targetHeight / height;
31003                 }
31004                 
31005                 if(x > 0 && y > 0){
31006                     scale = targetWidth / width;
31007                     
31008                     if(width < height){
31009                         scale = targetHeight / height;
31010                     }
31011                 }
31012                 
31013                 context.scale(scale, scale);
31014                 
31015                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31016                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31017
31018                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31019                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31020                 
31021                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31022                 
31023                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31024                 
31025                 break;
31026             default : 
31027                 break;
31028         }
31029         
31030         this.cropData = canvas.toDataURL(this.cropType);
31031         
31032         if(this.fireEvent('crop', this, this.cropData) !== false){
31033             this.process(this.file, this.cropData);
31034         }
31035         
31036         return;
31037         
31038     },
31039     
31040     setThumbBoxSize : function()
31041     {
31042         var width, height;
31043         
31044         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31045             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31046             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31047             
31048             this.minWidth = width;
31049             this.minHeight = height;
31050             
31051             if(this.rotate == 90 || this.rotate == 270){
31052                 this.minWidth = height;
31053                 this.minHeight = width;
31054             }
31055         }
31056         
31057         height = 300;
31058         width = Math.ceil(this.minWidth * height / this.minHeight);
31059         
31060         if(this.minWidth > this.minHeight){
31061             width = 300;
31062             height = Math.ceil(this.minHeight * width / this.minWidth);
31063         }
31064         
31065         this.thumbEl.setStyle({
31066             width : width + 'px',
31067             height : height + 'px'
31068         });
31069
31070         return;
31071             
31072     },
31073     
31074     setThumbBoxPosition : function()
31075     {
31076         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31077         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31078         
31079         this.thumbEl.setLeft(x);
31080         this.thumbEl.setTop(y);
31081         
31082     },
31083     
31084     baseRotateLevel : function()
31085     {
31086         this.baseRotate = 1;
31087         
31088         if(
31089                 typeof(this.exif) != 'undefined' &&
31090                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31091                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31092         ){
31093             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31094         }
31095         
31096         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31097         
31098     },
31099     
31100     baseScaleLevel : function()
31101     {
31102         var width, height;
31103         
31104         if(this.isDocument){
31105             
31106             if(this.baseRotate == 6 || this.baseRotate == 8){
31107             
31108                 height = this.thumbEl.getHeight();
31109                 this.baseScale = height / this.imageEl.OriginWidth;
31110
31111                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31112                     width = this.thumbEl.getWidth();
31113                     this.baseScale = width / this.imageEl.OriginHeight;
31114                 }
31115
31116                 return;
31117             }
31118
31119             height = this.thumbEl.getHeight();
31120             this.baseScale = height / this.imageEl.OriginHeight;
31121
31122             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31123                 width = this.thumbEl.getWidth();
31124                 this.baseScale = width / this.imageEl.OriginWidth;
31125             }
31126
31127             return;
31128         }
31129         
31130         if(this.baseRotate == 6 || this.baseRotate == 8){
31131             
31132             width = this.thumbEl.getHeight();
31133             this.baseScale = width / this.imageEl.OriginHeight;
31134             
31135             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31136                 height = this.thumbEl.getWidth();
31137                 this.baseScale = height / this.imageEl.OriginHeight;
31138             }
31139             
31140             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31141                 height = this.thumbEl.getWidth();
31142                 this.baseScale = height / this.imageEl.OriginHeight;
31143                 
31144                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31145                     width = this.thumbEl.getHeight();
31146                     this.baseScale = width / this.imageEl.OriginWidth;
31147                 }
31148             }
31149             
31150             return;
31151         }
31152         
31153         width = this.thumbEl.getWidth();
31154         this.baseScale = width / this.imageEl.OriginWidth;
31155         
31156         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31157             height = this.thumbEl.getHeight();
31158             this.baseScale = height / this.imageEl.OriginHeight;
31159         }
31160         
31161         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31162             
31163             height = this.thumbEl.getHeight();
31164             this.baseScale = height / this.imageEl.OriginHeight;
31165             
31166             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31167                 width = this.thumbEl.getWidth();
31168                 this.baseScale = width / this.imageEl.OriginWidth;
31169             }
31170             
31171         }
31172         
31173         return;
31174     },
31175     
31176     getScaleLevel : function()
31177     {
31178         return this.baseScale * Math.pow(1.1, this.scale);
31179     },
31180     
31181     onTouchStart : function(e)
31182     {
31183         if(!this.canvasLoaded){
31184             this.beforeSelectFile(e);
31185             return;
31186         }
31187         
31188         var touches = e.browserEvent.touches;
31189         
31190         if(!touches){
31191             return;
31192         }
31193         
31194         if(touches.length == 1){
31195             this.onMouseDown(e);
31196             return;
31197         }
31198         
31199         if(touches.length != 2){
31200             return;
31201         }
31202         
31203         var coords = [];
31204         
31205         for(var i = 0, finger; finger = touches[i]; i++){
31206             coords.push(finger.pageX, finger.pageY);
31207         }
31208         
31209         var x = Math.pow(coords[0] - coords[2], 2);
31210         var y = Math.pow(coords[1] - coords[3], 2);
31211         
31212         this.startDistance = Math.sqrt(x + y);
31213         
31214         this.startScale = this.scale;
31215         
31216         this.pinching = true;
31217         this.dragable = false;
31218         
31219     },
31220     
31221     onTouchMove : function(e)
31222     {
31223         if(!this.pinching && !this.dragable){
31224             return;
31225         }
31226         
31227         var touches = e.browserEvent.touches;
31228         
31229         if(!touches){
31230             return;
31231         }
31232         
31233         if(this.dragable){
31234             this.onMouseMove(e);
31235             return;
31236         }
31237         
31238         var coords = [];
31239         
31240         for(var i = 0, finger; finger = touches[i]; i++){
31241             coords.push(finger.pageX, finger.pageY);
31242         }
31243         
31244         var x = Math.pow(coords[0] - coords[2], 2);
31245         var y = Math.pow(coords[1] - coords[3], 2);
31246         
31247         this.endDistance = Math.sqrt(x + y);
31248         
31249         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31250         
31251         if(!this.zoomable()){
31252             this.scale = this.startScale;
31253             return;
31254         }
31255         
31256         this.draw();
31257         
31258     },
31259     
31260     onTouchEnd : function(e)
31261     {
31262         this.pinching = false;
31263         this.dragable = false;
31264         
31265     },
31266     
31267     process : function(file, crop)
31268     {
31269         if(this.loadMask){
31270             this.maskEl.mask(this.loadingText);
31271         }
31272         
31273         this.xhr = new XMLHttpRequest();
31274         
31275         file.xhr = this.xhr;
31276
31277         this.xhr.open(this.method, this.url, true);
31278         
31279         var headers = {
31280             "Accept": "application/json",
31281             "Cache-Control": "no-cache",
31282             "X-Requested-With": "XMLHttpRequest"
31283         };
31284         
31285         for (var headerName in headers) {
31286             var headerValue = headers[headerName];
31287             if (headerValue) {
31288                 this.xhr.setRequestHeader(headerName, headerValue);
31289             }
31290         }
31291         
31292         var _this = this;
31293         
31294         this.xhr.onload = function()
31295         {
31296             _this.xhrOnLoad(_this.xhr);
31297         }
31298         
31299         this.xhr.onerror = function()
31300         {
31301             _this.xhrOnError(_this.xhr);
31302         }
31303         
31304         var formData = new FormData();
31305
31306         formData.append('returnHTML', 'NO');
31307         
31308         if(crop){
31309             formData.append('crop', crop);
31310         }
31311         
31312         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31313             formData.append(this.paramName, file, file.name);
31314         }
31315         
31316         if(typeof(file.filename) != 'undefined'){
31317             formData.append('filename', file.filename);
31318         }
31319         
31320         if(typeof(file.mimetype) != 'undefined'){
31321             formData.append('mimetype', file.mimetype);
31322         }
31323         
31324         if(this.fireEvent('arrange', this, formData) != false){
31325             this.xhr.send(formData);
31326         };
31327     },
31328     
31329     xhrOnLoad : function(xhr)
31330     {
31331         if(this.loadMask){
31332             this.maskEl.unmask();
31333         }
31334         
31335         if (xhr.readyState !== 4) {
31336             this.fireEvent('exception', this, xhr);
31337             return;
31338         }
31339
31340         var response = Roo.decode(xhr.responseText);
31341         
31342         if(!response.success){
31343             this.fireEvent('exception', this, xhr);
31344             return;
31345         }
31346         
31347         var response = Roo.decode(xhr.responseText);
31348         
31349         this.fireEvent('upload', this, response);
31350         
31351     },
31352     
31353     xhrOnError : function()
31354     {
31355         if(this.loadMask){
31356             this.maskEl.unmask();
31357         }
31358         
31359         Roo.log('xhr on error');
31360         
31361         var response = Roo.decode(xhr.responseText);
31362           
31363         Roo.log(response);
31364         
31365     },
31366     
31367     prepare : function(file)
31368     {   
31369         if(this.loadMask){
31370             this.maskEl.mask(this.loadingText);
31371         }
31372         
31373         this.file = false;
31374         this.exif = {};
31375         
31376         if(typeof(file) === 'string'){
31377             this.loadCanvas(file);
31378             return;
31379         }
31380         
31381         if(!file || !this.urlAPI){
31382             return;
31383         }
31384         
31385         this.file = file;
31386         this.cropType = file.type;
31387         
31388         var _this = this;
31389         
31390         if(this.fireEvent('prepare', this, this.file) != false){
31391             
31392             var reader = new FileReader();
31393             
31394             reader.onload = function (e) {
31395                 if (e.target.error) {
31396                     Roo.log(e.target.error);
31397                     return;
31398                 }
31399                 
31400                 var buffer = e.target.result,
31401                     dataView = new DataView(buffer),
31402                     offset = 2,
31403                     maxOffset = dataView.byteLength - 4,
31404                     markerBytes,
31405                     markerLength;
31406                 
31407                 if (dataView.getUint16(0) === 0xffd8) {
31408                     while (offset < maxOffset) {
31409                         markerBytes = dataView.getUint16(offset);
31410                         
31411                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31412                             markerLength = dataView.getUint16(offset + 2) + 2;
31413                             if (offset + markerLength > dataView.byteLength) {
31414                                 Roo.log('Invalid meta data: Invalid segment size.');
31415                                 break;
31416                             }
31417                             
31418                             if(markerBytes == 0xffe1){
31419                                 _this.parseExifData(
31420                                     dataView,
31421                                     offset,
31422                                     markerLength
31423                                 );
31424                             }
31425                             
31426                             offset += markerLength;
31427                             
31428                             continue;
31429                         }
31430                         
31431                         break;
31432                     }
31433                     
31434                 }
31435                 
31436                 var url = _this.urlAPI.createObjectURL(_this.file);
31437                 
31438                 _this.loadCanvas(url);
31439                 
31440                 return;
31441             }
31442             
31443             reader.readAsArrayBuffer(this.file);
31444             
31445         }
31446         
31447     },
31448     
31449     parseExifData : function(dataView, offset, length)
31450     {
31451         var tiffOffset = offset + 10,
31452             littleEndian,
31453             dirOffset;
31454     
31455         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31456             // No Exif data, might be XMP data instead
31457             return;
31458         }
31459         
31460         // Check for the ASCII code for "Exif" (0x45786966):
31461         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31462             // No Exif data, might be XMP data instead
31463             return;
31464         }
31465         if (tiffOffset + 8 > dataView.byteLength) {
31466             Roo.log('Invalid Exif data: Invalid segment size.');
31467             return;
31468         }
31469         // Check for the two null bytes:
31470         if (dataView.getUint16(offset + 8) !== 0x0000) {
31471             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31472             return;
31473         }
31474         // Check the byte alignment:
31475         switch (dataView.getUint16(tiffOffset)) {
31476         case 0x4949:
31477             littleEndian = true;
31478             break;
31479         case 0x4D4D:
31480             littleEndian = false;
31481             break;
31482         default:
31483             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31484             return;
31485         }
31486         // Check for the TIFF tag marker (0x002A):
31487         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31488             Roo.log('Invalid Exif data: Missing TIFF marker.');
31489             return;
31490         }
31491         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31492         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31493         
31494         this.parseExifTags(
31495             dataView,
31496             tiffOffset,
31497             tiffOffset + dirOffset,
31498             littleEndian
31499         );
31500     },
31501     
31502     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31503     {
31504         var tagsNumber,
31505             dirEndOffset,
31506             i;
31507         if (dirOffset + 6 > dataView.byteLength) {
31508             Roo.log('Invalid Exif data: Invalid directory offset.');
31509             return;
31510         }
31511         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31512         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31513         if (dirEndOffset + 4 > dataView.byteLength) {
31514             Roo.log('Invalid Exif data: Invalid directory size.');
31515             return;
31516         }
31517         for (i = 0; i < tagsNumber; i += 1) {
31518             this.parseExifTag(
31519                 dataView,
31520                 tiffOffset,
31521                 dirOffset + 2 + 12 * i, // tag offset
31522                 littleEndian
31523             );
31524         }
31525         // Return the offset to the next directory:
31526         return dataView.getUint32(dirEndOffset, littleEndian);
31527     },
31528     
31529     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31530     {
31531         var tag = dataView.getUint16(offset, littleEndian);
31532         
31533         this.exif[tag] = this.getExifValue(
31534             dataView,
31535             tiffOffset,
31536             offset,
31537             dataView.getUint16(offset + 2, littleEndian), // tag type
31538             dataView.getUint32(offset + 4, littleEndian), // tag length
31539             littleEndian
31540         );
31541     },
31542     
31543     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31544     {
31545         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31546             tagSize,
31547             dataOffset,
31548             values,
31549             i,
31550             str,
31551             c;
31552     
31553         if (!tagType) {
31554             Roo.log('Invalid Exif data: Invalid tag type.');
31555             return;
31556         }
31557         
31558         tagSize = tagType.size * length;
31559         // Determine if the value is contained in the dataOffset bytes,
31560         // or if the value at the dataOffset is a pointer to the actual data:
31561         dataOffset = tagSize > 4 ?
31562                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31563         if (dataOffset + tagSize > dataView.byteLength) {
31564             Roo.log('Invalid Exif data: Invalid data offset.');
31565             return;
31566         }
31567         if (length === 1) {
31568             return tagType.getValue(dataView, dataOffset, littleEndian);
31569         }
31570         values = [];
31571         for (i = 0; i < length; i += 1) {
31572             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31573         }
31574         
31575         if (tagType.ascii) {
31576             str = '';
31577             // Concatenate the chars:
31578             for (i = 0; i < values.length; i += 1) {
31579                 c = values[i];
31580                 // Ignore the terminating NULL byte(s):
31581                 if (c === '\u0000') {
31582                     break;
31583                 }
31584                 str += c;
31585             }
31586             return str;
31587         }
31588         return values;
31589     }
31590     
31591 });
31592
31593 Roo.apply(Roo.bootstrap.UploadCropbox, {
31594     tags : {
31595         'Orientation': 0x0112
31596     },
31597     
31598     Orientation: {
31599             1: 0, //'top-left',
31600 //            2: 'top-right',
31601             3: 180, //'bottom-right',
31602 //            4: 'bottom-left',
31603 //            5: 'left-top',
31604             6: 90, //'right-top',
31605 //            7: 'right-bottom',
31606             8: 270 //'left-bottom'
31607     },
31608     
31609     exifTagTypes : {
31610         // byte, 8-bit unsigned int:
31611         1: {
31612             getValue: function (dataView, dataOffset) {
31613                 return dataView.getUint8(dataOffset);
31614             },
31615             size: 1
31616         },
31617         // ascii, 8-bit byte:
31618         2: {
31619             getValue: function (dataView, dataOffset) {
31620                 return String.fromCharCode(dataView.getUint8(dataOffset));
31621             },
31622             size: 1,
31623             ascii: true
31624         },
31625         // short, 16 bit int:
31626         3: {
31627             getValue: function (dataView, dataOffset, littleEndian) {
31628                 return dataView.getUint16(dataOffset, littleEndian);
31629             },
31630             size: 2
31631         },
31632         // long, 32 bit int:
31633         4: {
31634             getValue: function (dataView, dataOffset, littleEndian) {
31635                 return dataView.getUint32(dataOffset, littleEndian);
31636             },
31637             size: 4
31638         },
31639         // rational = two long values, first is numerator, second is denominator:
31640         5: {
31641             getValue: function (dataView, dataOffset, littleEndian) {
31642                 return dataView.getUint32(dataOffset, littleEndian) /
31643                     dataView.getUint32(dataOffset + 4, littleEndian);
31644             },
31645             size: 8
31646         },
31647         // slong, 32 bit signed int:
31648         9: {
31649             getValue: function (dataView, dataOffset, littleEndian) {
31650                 return dataView.getInt32(dataOffset, littleEndian);
31651             },
31652             size: 4
31653         },
31654         // srational, two slongs, first is numerator, second is denominator:
31655         10: {
31656             getValue: function (dataView, dataOffset, littleEndian) {
31657                 return dataView.getInt32(dataOffset, littleEndian) /
31658                     dataView.getInt32(dataOffset + 4, littleEndian);
31659             },
31660             size: 8
31661         }
31662     },
31663     
31664     footer : {
31665         STANDARD : [
31666             {
31667                 tag : 'div',
31668                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31669                 action : 'rotate-left',
31670                 cn : [
31671                     {
31672                         tag : 'button',
31673                         cls : 'btn btn-default',
31674                         html : '<i class="fa fa-undo"></i>'
31675                     }
31676                 ]
31677             },
31678             {
31679                 tag : 'div',
31680                 cls : 'btn-group roo-upload-cropbox-picture',
31681                 action : 'picture',
31682                 cn : [
31683                     {
31684                         tag : 'button',
31685                         cls : 'btn btn-default',
31686                         html : '<i class="fa fa-picture-o"></i>'
31687                     }
31688                 ]
31689             },
31690             {
31691                 tag : 'div',
31692                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31693                 action : 'rotate-right',
31694                 cn : [
31695                     {
31696                         tag : 'button',
31697                         cls : 'btn btn-default',
31698                         html : '<i class="fa fa-repeat"></i>'
31699                     }
31700                 ]
31701             }
31702         ],
31703         DOCUMENT : [
31704             {
31705                 tag : 'div',
31706                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31707                 action : 'rotate-left',
31708                 cn : [
31709                     {
31710                         tag : 'button',
31711                         cls : 'btn btn-default',
31712                         html : '<i class="fa fa-undo"></i>'
31713                     }
31714                 ]
31715             },
31716             {
31717                 tag : 'div',
31718                 cls : 'btn-group roo-upload-cropbox-download',
31719                 action : 'download',
31720                 cn : [
31721                     {
31722                         tag : 'button',
31723                         cls : 'btn btn-default',
31724                         html : '<i class="fa fa-download"></i>'
31725                     }
31726                 ]
31727             },
31728             {
31729                 tag : 'div',
31730                 cls : 'btn-group roo-upload-cropbox-crop',
31731                 action : 'crop',
31732                 cn : [
31733                     {
31734                         tag : 'button',
31735                         cls : 'btn btn-default',
31736                         html : '<i class="fa fa-crop"></i>'
31737                     }
31738                 ]
31739             },
31740             {
31741                 tag : 'div',
31742                 cls : 'btn-group roo-upload-cropbox-trash',
31743                 action : 'trash',
31744                 cn : [
31745                     {
31746                         tag : 'button',
31747                         cls : 'btn btn-default',
31748                         html : '<i class="fa fa-trash"></i>'
31749                     }
31750                 ]
31751             },
31752             {
31753                 tag : 'div',
31754                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31755                 action : 'rotate-right',
31756                 cn : [
31757                     {
31758                         tag : 'button',
31759                         cls : 'btn btn-default',
31760                         html : '<i class="fa fa-repeat"></i>'
31761                     }
31762                 ]
31763             }
31764         ],
31765         ROTATOR : [
31766             {
31767                 tag : 'div',
31768                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31769                 action : 'rotate-left',
31770                 cn : [
31771                     {
31772                         tag : 'button',
31773                         cls : 'btn btn-default',
31774                         html : '<i class="fa fa-undo"></i>'
31775                     }
31776                 ]
31777             },
31778             {
31779                 tag : 'div',
31780                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31781                 action : 'rotate-right',
31782                 cn : [
31783                     {
31784                         tag : 'button',
31785                         cls : 'btn btn-default',
31786                         html : '<i class="fa fa-repeat"></i>'
31787                     }
31788                 ]
31789             }
31790         ]
31791     }
31792 });
31793
31794 /*
31795 * Licence: LGPL
31796 */
31797
31798 /**
31799  * @class Roo.bootstrap.DocumentManager
31800  * @extends Roo.bootstrap.Component
31801  * Bootstrap DocumentManager class
31802  * @cfg {String} paramName default 'imageUpload'
31803  * @cfg {String} toolTipName default 'filename'
31804  * @cfg {String} method default POST
31805  * @cfg {String} url action url
31806  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31807  * @cfg {Boolean} multiple multiple upload default true
31808  * @cfg {Number} thumbSize default 300
31809  * @cfg {String} fieldLabel
31810  * @cfg {Number} labelWidth default 4
31811  * @cfg {String} labelAlign (left|top) default left
31812  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31813 * @cfg {Number} labellg set the width of label (1-12)
31814  * @cfg {Number} labelmd set the width of label (1-12)
31815  * @cfg {Number} labelsm set the width of label (1-12)
31816  * @cfg {Number} labelxs set the width of label (1-12)
31817  * 
31818  * @constructor
31819  * Create a new DocumentManager
31820  * @param {Object} config The config object
31821  */
31822
31823 Roo.bootstrap.DocumentManager = function(config){
31824     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31825     
31826     this.files = [];
31827     this.delegates = [];
31828     
31829     this.addEvents({
31830         /**
31831          * @event initial
31832          * Fire when initial the DocumentManager
31833          * @param {Roo.bootstrap.DocumentManager} this
31834          */
31835         "initial" : true,
31836         /**
31837          * @event inspect
31838          * inspect selected file
31839          * @param {Roo.bootstrap.DocumentManager} this
31840          * @param {File} file
31841          */
31842         "inspect" : true,
31843         /**
31844          * @event exception
31845          * Fire when xhr load exception
31846          * @param {Roo.bootstrap.DocumentManager} this
31847          * @param {XMLHttpRequest} xhr
31848          */
31849         "exception" : true,
31850         /**
31851          * @event afterupload
31852          * Fire when xhr load exception
31853          * @param {Roo.bootstrap.DocumentManager} this
31854          * @param {XMLHttpRequest} xhr
31855          */
31856         "afterupload" : true,
31857         /**
31858          * @event prepare
31859          * prepare the form data
31860          * @param {Roo.bootstrap.DocumentManager} this
31861          * @param {Object} formData
31862          */
31863         "prepare" : true,
31864         /**
31865          * @event remove
31866          * Fire when remove the file
31867          * @param {Roo.bootstrap.DocumentManager} this
31868          * @param {Object} file
31869          */
31870         "remove" : true,
31871         /**
31872          * @event refresh
31873          * Fire after refresh the file
31874          * @param {Roo.bootstrap.DocumentManager} this
31875          */
31876         "refresh" : true,
31877         /**
31878          * @event click
31879          * Fire after click the image
31880          * @param {Roo.bootstrap.DocumentManager} this
31881          * @param {Object} file
31882          */
31883         "click" : true,
31884         /**
31885          * @event edit
31886          * Fire when upload a image and editable set to true
31887          * @param {Roo.bootstrap.DocumentManager} this
31888          * @param {Object} file
31889          */
31890         "edit" : true,
31891         /**
31892          * @event beforeselectfile
31893          * Fire before select file
31894          * @param {Roo.bootstrap.DocumentManager} this
31895          */
31896         "beforeselectfile" : true,
31897         /**
31898          * @event process
31899          * Fire before process file
31900          * @param {Roo.bootstrap.DocumentManager} this
31901          * @param {Object} file
31902          */
31903         "process" : true,
31904         /**
31905          * @event previewrendered
31906          * Fire when preview rendered
31907          * @param {Roo.bootstrap.DocumentManager} this
31908          * @param {Object} file
31909          */
31910         "previewrendered" : true,
31911         /**
31912          */
31913         "previewResize" : true
31914         
31915     });
31916 };
31917
31918 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31919     
31920     boxes : 0,
31921     inputName : '',
31922     thumbSize : 300,
31923     multiple : true,
31924     files : false,
31925     method : 'POST',
31926     url : '',
31927     paramName : 'imageUpload',
31928     toolTipName : 'filename',
31929     fieldLabel : '',
31930     labelWidth : 4,
31931     labelAlign : 'left',
31932     editable : true,
31933     delegates : false,
31934     xhr : false, 
31935     
31936     labellg : 0,
31937     labelmd : 0,
31938     labelsm : 0,
31939     labelxs : 0,
31940     
31941     getAutoCreate : function()
31942     {   
31943         var managerWidget = {
31944             tag : 'div',
31945             cls : 'roo-document-manager',
31946             cn : [
31947                 {
31948                     tag : 'input',
31949                     cls : 'roo-document-manager-selector',
31950                     type : 'file'
31951                 },
31952                 {
31953                     tag : 'div',
31954                     cls : 'roo-document-manager-uploader',
31955                     cn : [
31956                         {
31957                             tag : 'div',
31958                             cls : 'roo-document-manager-upload-btn',
31959                             html : '<i class="fa fa-plus"></i>'
31960                         }
31961                     ]
31962                     
31963                 }
31964             ]
31965         };
31966         
31967         var content = [
31968             {
31969                 tag : 'div',
31970                 cls : 'column col-md-12',
31971                 cn : managerWidget
31972             }
31973         ];
31974         
31975         if(this.fieldLabel.length){
31976             
31977             content = [
31978                 {
31979                     tag : 'div',
31980                     cls : 'column col-md-12',
31981                     html : this.fieldLabel
31982                 },
31983                 {
31984                     tag : 'div',
31985                     cls : 'column col-md-12',
31986                     cn : managerWidget
31987                 }
31988             ];
31989
31990             if(this.labelAlign == 'left'){
31991                 content = [
31992                     {
31993                         tag : 'div',
31994                         cls : 'column',
31995                         html : this.fieldLabel
31996                     },
31997                     {
31998                         tag : 'div',
31999                         cls : 'column',
32000                         cn : managerWidget
32001                     }
32002                 ];
32003                 
32004                 if(this.labelWidth > 12){
32005                     content[0].style = "width: " + this.labelWidth + 'px';
32006                 }
32007
32008                 if(this.labelWidth < 13 && this.labelmd == 0){
32009                     this.labelmd = this.labelWidth;
32010                 }
32011
32012                 if(this.labellg > 0){
32013                     content[0].cls += ' col-lg-' + this.labellg;
32014                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32015                 }
32016
32017                 if(this.labelmd > 0){
32018                     content[0].cls += ' col-md-' + this.labelmd;
32019                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32020                 }
32021
32022                 if(this.labelsm > 0){
32023                     content[0].cls += ' col-sm-' + this.labelsm;
32024                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32025                 }
32026
32027                 if(this.labelxs > 0){
32028                     content[0].cls += ' col-xs-' + this.labelxs;
32029                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32030                 }
32031                 
32032             }
32033         }
32034         
32035         var cfg = {
32036             tag : 'div',
32037             cls : 'row clearfix',
32038             cn : content
32039         };
32040         
32041         return cfg;
32042         
32043     },
32044     
32045     initEvents : function()
32046     {
32047         this.managerEl = this.el.select('.roo-document-manager', true).first();
32048         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32049         
32050         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32051         this.selectorEl.hide();
32052         
32053         if(this.multiple){
32054             this.selectorEl.attr('multiple', 'multiple');
32055         }
32056         
32057         this.selectorEl.on('change', this.onFileSelected, this);
32058         
32059         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32060         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32061         
32062         this.uploader.on('click', this.onUploaderClick, this);
32063         
32064         this.renderProgressDialog();
32065         
32066         var _this = this;
32067         
32068         window.addEventListener("resize", function() { _this.refresh(); } );
32069         
32070         this.fireEvent('initial', this);
32071     },
32072     
32073     renderProgressDialog : function()
32074     {
32075         var _this = this;
32076         
32077         this.progressDialog = new Roo.bootstrap.Modal({
32078             cls : 'roo-document-manager-progress-dialog',
32079             allow_close : false,
32080             animate : false,
32081             title : '',
32082             buttons : [
32083                 {
32084                     name  :'cancel',
32085                     weight : 'danger',
32086                     html : 'Cancel'
32087                 }
32088             ], 
32089             listeners : { 
32090                 btnclick : function() {
32091                     _this.uploadCancel();
32092                     this.hide();
32093                 }
32094             }
32095         });
32096          
32097         this.progressDialog.render(Roo.get(document.body));
32098          
32099         this.progress = new Roo.bootstrap.Progress({
32100             cls : 'roo-document-manager-progress',
32101             active : true,
32102             striped : true
32103         });
32104         
32105         this.progress.render(this.progressDialog.getChildContainer());
32106         
32107         this.progressBar = new Roo.bootstrap.ProgressBar({
32108             cls : 'roo-document-manager-progress-bar',
32109             aria_valuenow : 0,
32110             aria_valuemin : 0,
32111             aria_valuemax : 12,
32112             panel : 'success'
32113         });
32114         
32115         this.progressBar.render(this.progress.getChildContainer());
32116     },
32117     
32118     onUploaderClick : function(e)
32119     {
32120         e.preventDefault();
32121      
32122         if(this.fireEvent('beforeselectfile', this) != false){
32123             this.selectorEl.dom.click();
32124         }
32125         
32126     },
32127     
32128     onFileSelected : function(e)
32129     {
32130         e.preventDefault();
32131         
32132         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32133             return;
32134         }
32135         
32136         Roo.each(this.selectorEl.dom.files, function(file){
32137             if(this.fireEvent('inspect', this, file) != false){
32138                 this.files.push(file);
32139             }
32140         }, this);
32141         
32142         this.queue();
32143         
32144     },
32145     
32146     queue : function()
32147     {
32148         this.selectorEl.dom.value = '';
32149         
32150         if(!this.files || !this.files.length){
32151             return;
32152         }
32153         
32154         if(this.boxes > 0 && this.files.length > this.boxes){
32155             this.files = this.files.slice(0, this.boxes);
32156         }
32157         
32158         this.uploader.show();
32159         
32160         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32161             this.uploader.hide();
32162         }
32163         
32164         var _this = this;
32165         
32166         var files = [];
32167         
32168         var docs = [];
32169         
32170         Roo.each(this.files, function(file){
32171             
32172             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32173                 var f = this.renderPreview(file);
32174                 files.push(f);
32175                 return;
32176             }
32177             
32178             if(file.type.indexOf('image') != -1){
32179                 this.delegates.push(
32180                     (function(){
32181                         _this.process(file);
32182                     }).createDelegate(this)
32183                 );
32184         
32185                 return;
32186             }
32187             
32188             docs.push(
32189                 (function(){
32190                     _this.process(file);
32191                 }).createDelegate(this)
32192             );
32193             
32194         }, this);
32195         
32196         this.files = files;
32197         
32198         this.delegates = this.delegates.concat(docs);
32199         
32200         if(!this.delegates.length){
32201             this.refresh();
32202             return;
32203         }
32204         
32205         this.progressBar.aria_valuemax = this.delegates.length;
32206         
32207         this.arrange();
32208         
32209         return;
32210     },
32211     
32212     arrange : function()
32213     {
32214         if(!this.delegates.length){
32215             this.progressDialog.hide();
32216             this.refresh();
32217             return;
32218         }
32219         
32220         var delegate = this.delegates.shift();
32221         
32222         this.progressDialog.show();
32223         
32224         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32225         
32226         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32227         
32228         delegate();
32229     },
32230     
32231     refresh : function()
32232     {
32233         this.uploader.show();
32234         
32235         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32236             this.uploader.hide();
32237         }
32238         
32239         Roo.isTouch ? this.closable(false) : this.closable(true);
32240         
32241         this.fireEvent('refresh', this);
32242     },
32243     
32244     onRemove : function(e, el, o)
32245     {
32246         e.preventDefault();
32247         
32248         this.fireEvent('remove', this, o);
32249         
32250     },
32251     
32252     remove : function(o)
32253     {
32254         var files = [];
32255         
32256         Roo.each(this.files, function(file){
32257             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32258                 files.push(file);
32259                 return;
32260             }
32261
32262             o.target.remove();
32263
32264         }, this);
32265         
32266         this.files = files;
32267         
32268         this.refresh();
32269     },
32270     
32271     clear : function()
32272     {
32273         Roo.each(this.files, function(file){
32274             if(!file.target){
32275                 return;
32276             }
32277             
32278             file.target.remove();
32279
32280         }, this);
32281         
32282         this.files = [];
32283         
32284         this.refresh();
32285     },
32286     
32287     onClick : function(e, el, o)
32288     {
32289         e.preventDefault();
32290         
32291         this.fireEvent('click', this, o);
32292         
32293     },
32294     
32295     closable : function(closable)
32296     {
32297         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32298             
32299             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32300             
32301             if(closable){
32302                 el.show();
32303                 return;
32304             }
32305             
32306             el.hide();
32307             
32308         }, this);
32309     },
32310     
32311     xhrOnLoad : function(xhr)
32312     {
32313         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32314             el.remove();
32315         }, this);
32316         
32317         if (xhr.readyState !== 4) {
32318             this.arrange();
32319             this.fireEvent('exception', this, xhr);
32320             return;
32321         }
32322
32323         var response = Roo.decode(xhr.responseText);
32324         
32325         if(!response.success){
32326             this.arrange();
32327             this.fireEvent('exception', this, xhr);
32328             return;
32329         }
32330         
32331         var file = this.renderPreview(response.data);
32332         
32333         this.files.push(file);
32334         
32335         this.arrange();
32336         
32337         this.fireEvent('afterupload', this, xhr);
32338         
32339     },
32340     
32341     xhrOnError : function(xhr)
32342     {
32343         Roo.log('xhr on error');
32344         
32345         var response = Roo.decode(xhr.responseText);
32346           
32347         Roo.log(response);
32348         
32349         this.arrange();
32350     },
32351     
32352     process : function(file)
32353     {
32354         if(this.fireEvent('process', this, file) !== false){
32355             if(this.editable && file.type.indexOf('image') != -1){
32356                 this.fireEvent('edit', this, file);
32357                 return;
32358             }
32359
32360             this.uploadStart(file, false);
32361
32362             return;
32363         }
32364         
32365     },
32366     
32367     uploadStart : function(file, crop)
32368     {
32369         this.xhr = new XMLHttpRequest();
32370         
32371         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32372             this.arrange();
32373             return;
32374         }
32375         
32376         file.xhr = this.xhr;
32377             
32378         this.managerEl.createChild({
32379             tag : 'div',
32380             cls : 'roo-document-manager-loading',
32381             cn : [
32382                 {
32383                     tag : 'div',
32384                     tooltip : file.name,
32385                     cls : 'roo-document-manager-thumb',
32386                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32387                 }
32388             ]
32389
32390         });
32391
32392         this.xhr.open(this.method, this.url, true);
32393         
32394         var headers = {
32395             "Accept": "application/json",
32396             "Cache-Control": "no-cache",
32397             "X-Requested-With": "XMLHttpRequest"
32398         };
32399         
32400         for (var headerName in headers) {
32401             var headerValue = headers[headerName];
32402             if (headerValue) {
32403                 this.xhr.setRequestHeader(headerName, headerValue);
32404             }
32405         }
32406         
32407         var _this = this;
32408         
32409         this.xhr.onload = function()
32410         {
32411             _this.xhrOnLoad(_this.xhr);
32412         }
32413         
32414         this.xhr.onerror = function()
32415         {
32416             _this.xhrOnError(_this.xhr);
32417         }
32418         
32419         var formData = new FormData();
32420
32421         formData.append('returnHTML', 'NO');
32422         
32423         if(crop){
32424             formData.append('crop', crop);
32425         }
32426         
32427         formData.append(this.paramName, file, file.name);
32428         
32429         var options = {
32430             file : file, 
32431             manually : false
32432         };
32433         
32434         if(this.fireEvent('prepare', this, formData, options) != false){
32435             
32436             if(options.manually){
32437                 return;
32438             }
32439             
32440             this.xhr.send(formData);
32441             return;
32442         };
32443         
32444         this.uploadCancel();
32445     },
32446     
32447     uploadCancel : function()
32448     {
32449         if (this.xhr) {
32450             this.xhr.abort();
32451         }
32452         
32453         this.delegates = [];
32454         
32455         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32456             el.remove();
32457         }, this);
32458         
32459         this.arrange();
32460     },
32461     
32462     renderPreview : function(file)
32463     {
32464         if(typeof(file.target) != 'undefined' && file.target){
32465             return file;
32466         }
32467         
32468         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32469         
32470         var previewEl = this.managerEl.createChild({
32471             tag : 'div',
32472             cls : 'roo-document-manager-preview',
32473             cn : [
32474                 {
32475                     tag : 'div',
32476                     tooltip : file[this.toolTipName],
32477                     cls : 'roo-document-manager-thumb',
32478                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32479                 },
32480                 {
32481                     tag : 'button',
32482                     cls : 'close',
32483                     html : '<i class="fa fa-times-circle"></i>'
32484                 }
32485             ]
32486         });
32487
32488         var close = previewEl.select('button.close', true).first();
32489
32490         close.on('click', this.onRemove, this, file);
32491
32492         file.target = previewEl;
32493
32494         var image = previewEl.select('img', true).first();
32495         
32496         var _this = this;
32497         
32498         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32499         
32500         image.on('click', this.onClick, this, file);
32501         
32502         this.fireEvent('previewrendered', this, file);
32503         
32504         return file;
32505         
32506     },
32507     
32508     onPreviewLoad : function(file, image)
32509     {
32510         if(typeof(file.target) == 'undefined' || !file.target){
32511             return;
32512         }
32513         
32514         var width = image.dom.naturalWidth || image.dom.width;
32515         var height = image.dom.naturalHeight || image.dom.height;
32516         
32517         if(!this.previewResize) {
32518             return;
32519         }
32520         
32521         if(width > height){
32522             file.target.addClass('wide');
32523             return;
32524         }
32525         
32526         file.target.addClass('tall');
32527         return;
32528         
32529     },
32530     
32531     uploadFromSource : function(file, crop)
32532     {
32533         this.xhr = new XMLHttpRequest();
32534         
32535         this.managerEl.createChild({
32536             tag : 'div',
32537             cls : 'roo-document-manager-loading',
32538             cn : [
32539                 {
32540                     tag : 'div',
32541                     tooltip : file.name,
32542                     cls : 'roo-document-manager-thumb',
32543                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32544                 }
32545             ]
32546
32547         });
32548
32549         this.xhr.open(this.method, this.url, true);
32550         
32551         var headers = {
32552             "Accept": "application/json",
32553             "Cache-Control": "no-cache",
32554             "X-Requested-With": "XMLHttpRequest"
32555         };
32556         
32557         for (var headerName in headers) {
32558             var headerValue = headers[headerName];
32559             if (headerValue) {
32560                 this.xhr.setRequestHeader(headerName, headerValue);
32561             }
32562         }
32563         
32564         var _this = this;
32565         
32566         this.xhr.onload = function()
32567         {
32568             _this.xhrOnLoad(_this.xhr);
32569         }
32570         
32571         this.xhr.onerror = function()
32572         {
32573             _this.xhrOnError(_this.xhr);
32574         }
32575         
32576         var formData = new FormData();
32577
32578         formData.append('returnHTML', 'NO');
32579         
32580         formData.append('crop', crop);
32581         
32582         if(typeof(file.filename) != 'undefined'){
32583             formData.append('filename', file.filename);
32584         }
32585         
32586         if(typeof(file.mimetype) != 'undefined'){
32587             formData.append('mimetype', file.mimetype);
32588         }
32589         
32590         Roo.log(formData);
32591         
32592         if(this.fireEvent('prepare', this, formData) != false){
32593             this.xhr.send(formData);
32594         };
32595     }
32596 });
32597
32598 /*
32599 * Licence: LGPL
32600 */
32601
32602 /**
32603  * @class Roo.bootstrap.DocumentViewer
32604  * @extends Roo.bootstrap.Component
32605  * Bootstrap DocumentViewer class
32606  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32607  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32608  * 
32609  * @constructor
32610  * Create a new DocumentViewer
32611  * @param {Object} config The config object
32612  */
32613
32614 Roo.bootstrap.DocumentViewer = function(config){
32615     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32616     
32617     this.addEvents({
32618         /**
32619          * @event initial
32620          * Fire after initEvent
32621          * @param {Roo.bootstrap.DocumentViewer} this
32622          */
32623         "initial" : true,
32624         /**
32625          * @event click
32626          * Fire after click
32627          * @param {Roo.bootstrap.DocumentViewer} this
32628          */
32629         "click" : true,
32630         /**
32631          * @event download
32632          * Fire after download button
32633          * @param {Roo.bootstrap.DocumentViewer} this
32634          */
32635         "download" : true,
32636         /**
32637          * @event trash
32638          * Fire after trash button
32639          * @param {Roo.bootstrap.DocumentViewer} this
32640          */
32641         "trash" : true
32642         
32643     });
32644 };
32645
32646 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32647     
32648     showDownload : true,
32649     
32650     showTrash : true,
32651     
32652     getAutoCreate : function()
32653     {
32654         var cfg = {
32655             tag : 'div',
32656             cls : 'roo-document-viewer',
32657             cn : [
32658                 {
32659                     tag : 'div',
32660                     cls : 'roo-document-viewer-body',
32661                     cn : [
32662                         {
32663                             tag : 'div',
32664                             cls : 'roo-document-viewer-thumb',
32665                             cn : [
32666                                 {
32667                                     tag : 'img',
32668                                     cls : 'roo-document-viewer-image'
32669                                 }
32670                             ]
32671                         }
32672                     ]
32673                 },
32674                 {
32675                     tag : 'div',
32676                     cls : 'roo-document-viewer-footer',
32677                     cn : {
32678                         tag : 'div',
32679                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32680                         cn : [
32681                             {
32682                                 tag : 'div',
32683                                 cls : 'btn-group roo-document-viewer-download',
32684                                 cn : [
32685                                     {
32686                                         tag : 'button',
32687                                         cls : 'btn btn-default',
32688                                         html : '<i class="fa fa-download"></i>'
32689                                     }
32690                                 ]
32691                             },
32692                             {
32693                                 tag : 'div',
32694                                 cls : 'btn-group roo-document-viewer-trash',
32695                                 cn : [
32696                                     {
32697                                         tag : 'button',
32698                                         cls : 'btn btn-default',
32699                                         html : '<i class="fa fa-trash"></i>'
32700                                     }
32701                                 ]
32702                             }
32703                         ]
32704                     }
32705                 }
32706             ]
32707         };
32708         
32709         return cfg;
32710     },
32711     
32712     initEvents : function()
32713     {
32714         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32715         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32716         
32717         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32718         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32719         
32720         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32721         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32722         
32723         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32724         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32725         
32726         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32727         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32728         
32729         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32730         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32731         
32732         this.bodyEl.on('click', this.onClick, this);
32733         this.downloadBtn.on('click', this.onDownload, this);
32734         this.trashBtn.on('click', this.onTrash, this);
32735         
32736         this.downloadBtn.hide();
32737         this.trashBtn.hide();
32738         
32739         if(this.showDownload){
32740             this.downloadBtn.show();
32741         }
32742         
32743         if(this.showTrash){
32744             this.trashBtn.show();
32745         }
32746         
32747         if(!this.showDownload && !this.showTrash) {
32748             this.footerEl.hide();
32749         }
32750         
32751     },
32752     
32753     initial : function()
32754     {
32755         this.fireEvent('initial', this);
32756         
32757     },
32758     
32759     onClick : function(e)
32760     {
32761         e.preventDefault();
32762         
32763         this.fireEvent('click', this);
32764     },
32765     
32766     onDownload : function(e)
32767     {
32768         e.preventDefault();
32769         
32770         this.fireEvent('download', this);
32771     },
32772     
32773     onTrash : function(e)
32774     {
32775         e.preventDefault();
32776         
32777         this.fireEvent('trash', this);
32778     }
32779     
32780 });
32781 /*
32782  * - LGPL
32783  *
32784  * nav progress bar
32785  * 
32786  */
32787
32788 /**
32789  * @class Roo.bootstrap.NavProgressBar
32790  * @extends Roo.bootstrap.Component
32791  * Bootstrap NavProgressBar class
32792  * 
32793  * @constructor
32794  * Create a new nav progress bar
32795  * @param {Object} config The config object
32796  */
32797
32798 Roo.bootstrap.NavProgressBar = function(config){
32799     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32800
32801     this.bullets = this.bullets || [];
32802    
32803 //    Roo.bootstrap.NavProgressBar.register(this);
32804      this.addEvents({
32805         /**
32806              * @event changed
32807              * Fires when the active item changes
32808              * @param {Roo.bootstrap.NavProgressBar} this
32809              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32810              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32811          */
32812         'changed': true
32813      });
32814     
32815 };
32816
32817 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32818     
32819     bullets : [],
32820     barItems : [],
32821     
32822     getAutoCreate : function()
32823     {
32824         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32825         
32826         cfg = {
32827             tag : 'div',
32828             cls : 'roo-navigation-bar-group',
32829             cn : [
32830                 {
32831                     tag : 'div',
32832                     cls : 'roo-navigation-top-bar'
32833                 },
32834                 {
32835                     tag : 'div',
32836                     cls : 'roo-navigation-bullets-bar',
32837                     cn : [
32838                         {
32839                             tag : 'ul',
32840                             cls : 'roo-navigation-bar'
32841                         }
32842                     ]
32843                 },
32844                 
32845                 {
32846                     tag : 'div',
32847                     cls : 'roo-navigation-bottom-bar'
32848                 }
32849             ]
32850             
32851         };
32852         
32853         return cfg;
32854         
32855     },
32856     
32857     initEvents: function() 
32858     {
32859         
32860     },
32861     
32862     onRender : function(ct, position) 
32863     {
32864         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32865         
32866         if(this.bullets.length){
32867             Roo.each(this.bullets, function(b){
32868                this.addItem(b);
32869             }, this);
32870         }
32871         
32872         this.format();
32873         
32874     },
32875     
32876     addItem : function(cfg)
32877     {
32878         var item = new Roo.bootstrap.NavProgressItem(cfg);
32879         
32880         item.parentId = this.id;
32881         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32882         
32883         if(cfg.html){
32884             var top = new Roo.bootstrap.Element({
32885                 tag : 'div',
32886                 cls : 'roo-navigation-bar-text'
32887             });
32888             
32889             var bottom = new Roo.bootstrap.Element({
32890                 tag : 'div',
32891                 cls : 'roo-navigation-bar-text'
32892             });
32893             
32894             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32895             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32896             
32897             var topText = new Roo.bootstrap.Element({
32898                 tag : 'span',
32899                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32900             });
32901             
32902             var bottomText = new Roo.bootstrap.Element({
32903                 tag : 'span',
32904                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32905             });
32906             
32907             topText.onRender(top.el, null);
32908             bottomText.onRender(bottom.el, null);
32909             
32910             item.topEl = top;
32911             item.bottomEl = bottom;
32912         }
32913         
32914         this.barItems.push(item);
32915         
32916         return item;
32917     },
32918     
32919     getActive : function()
32920     {
32921         var active = false;
32922         
32923         Roo.each(this.barItems, function(v){
32924             
32925             if (!v.isActive()) {
32926                 return;
32927             }
32928             
32929             active = v;
32930             return false;
32931             
32932         });
32933         
32934         return active;
32935     },
32936     
32937     setActiveItem : function(item)
32938     {
32939         var prev = false;
32940         
32941         Roo.each(this.barItems, function(v){
32942             if (v.rid == item.rid) {
32943                 return ;
32944             }
32945             
32946             if (v.isActive()) {
32947                 v.setActive(false);
32948                 prev = v;
32949             }
32950         });
32951
32952         item.setActive(true);
32953         
32954         this.fireEvent('changed', this, item, prev);
32955     },
32956     
32957     getBarItem: function(rid)
32958     {
32959         var ret = false;
32960         
32961         Roo.each(this.barItems, function(e) {
32962             if (e.rid != rid) {
32963                 return;
32964             }
32965             
32966             ret =  e;
32967             return false;
32968         });
32969         
32970         return ret;
32971     },
32972     
32973     indexOfItem : function(item)
32974     {
32975         var index = false;
32976         
32977         Roo.each(this.barItems, function(v, i){
32978             
32979             if (v.rid != item.rid) {
32980                 return;
32981             }
32982             
32983             index = i;
32984             return false
32985         });
32986         
32987         return index;
32988     },
32989     
32990     setActiveNext : function()
32991     {
32992         var i = this.indexOfItem(this.getActive());
32993         
32994         if (i > this.barItems.length) {
32995             return;
32996         }
32997         
32998         this.setActiveItem(this.barItems[i+1]);
32999     },
33000     
33001     setActivePrev : function()
33002     {
33003         var i = this.indexOfItem(this.getActive());
33004         
33005         if (i  < 1) {
33006             return;
33007         }
33008         
33009         this.setActiveItem(this.barItems[i-1]);
33010     },
33011     
33012     format : function()
33013     {
33014         if(!this.barItems.length){
33015             return;
33016         }
33017      
33018         var width = 100 / this.barItems.length;
33019         
33020         Roo.each(this.barItems, function(i){
33021             i.el.setStyle('width', width + '%');
33022             i.topEl.el.setStyle('width', width + '%');
33023             i.bottomEl.el.setStyle('width', width + '%');
33024         }, this);
33025         
33026     }
33027     
33028 });
33029 /*
33030  * - LGPL
33031  *
33032  * Nav Progress Item
33033  * 
33034  */
33035
33036 /**
33037  * @class Roo.bootstrap.NavProgressItem
33038  * @extends Roo.bootstrap.Component
33039  * Bootstrap NavProgressItem class
33040  * @cfg {String} rid the reference id
33041  * @cfg {Boolean} active (true|false) Is item active default false
33042  * @cfg {Boolean} disabled (true|false) Is item active default false
33043  * @cfg {String} html
33044  * @cfg {String} position (top|bottom) text position default bottom
33045  * @cfg {String} icon show icon instead of number
33046  * 
33047  * @constructor
33048  * Create a new NavProgressItem
33049  * @param {Object} config The config object
33050  */
33051 Roo.bootstrap.NavProgressItem = function(config){
33052     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33053     this.addEvents({
33054         // raw events
33055         /**
33056          * @event click
33057          * The raw click event for the entire grid.
33058          * @param {Roo.bootstrap.NavProgressItem} this
33059          * @param {Roo.EventObject} e
33060          */
33061         "click" : true
33062     });
33063    
33064 };
33065
33066 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33067     
33068     rid : '',
33069     active : false,
33070     disabled : false,
33071     html : '',
33072     position : 'bottom',
33073     icon : false,
33074     
33075     getAutoCreate : function()
33076     {
33077         var iconCls = 'roo-navigation-bar-item-icon';
33078         
33079         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33080         
33081         var cfg = {
33082             tag: 'li',
33083             cls: 'roo-navigation-bar-item',
33084             cn : [
33085                 {
33086                     tag : 'i',
33087                     cls : iconCls
33088                 }
33089             ]
33090         };
33091         
33092         if(this.active){
33093             cfg.cls += ' active';
33094         }
33095         if(this.disabled){
33096             cfg.cls += ' disabled';
33097         }
33098         
33099         return cfg;
33100     },
33101     
33102     disable : function()
33103     {
33104         this.setDisabled(true);
33105     },
33106     
33107     enable : function()
33108     {
33109         this.setDisabled(false);
33110     },
33111     
33112     initEvents: function() 
33113     {
33114         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33115         
33116         this.iconEl.on('click', this.onClick, this);
33117     },
33118     
33119     onClick : function(e)
33120     {
33121         e.preventDefault();
33122         
33123         if(this.disabled){
33124             return;
33125         }
33126         
33127         if(this.fireEvent('click', this, e) === false){
33128             return;
33129         };
33130         
33131         this.parent().setActiveItem(this);
33132     },
33133     
33134     isActive: function () 
33135     {
33136         return this.active;
33137     },
33138     
33139     setActive : function(state)
33140     {
33141         if(this.active == state){
33142             return;
33143         }
33144         
33145         this.active = state;
33146         
33147         if (state) {
33148             this.el.addClass('active');
33149             return;
33150         }
33151         
33152         this.el.removeClass('active');
33153         
33154         return;
33155     },
33156     
33157     setDisabled : function(state)
33158     {
33159         if(this.disabled == state){
33160             return;
33161         }
33162         
33163         this.disabled = state;
33164         
33165         if (state) {
33166             this.el.addClass('disabled');
33167             return;
33168         }
33169         
33170         this.el.removeClass('disabled');
33171     },
33172     
33173     tooltipEl : function()
33174     {
33175         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33176     }
33177 });
33178  
33179
33180  /*
33181  * - LGPL
33182  *
33183  * FieldLabel
33184  * 
33185  */
33186
33187 /**
33188  * @class Roo.bootstrap.FieldLabel
33189  * @extends Roo.bootstrap.Component
33190  * Bootstrap FieldLabel class
33191  * @cfg {String} html contents of the element
33192  * @cfg {String} tag tag of the element default label
33193  * @cfg {String} cls class of the element
33194  * @cfg {String} target label target 
33195  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33196  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33197  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33198  * @cfg {String} iconTooltip default "This field is required"
33199  * @cfg {String} indicatorpos (left|right) default left
33200  * 
33201  * @constructor
33202  * Create a new FieldLabel
33203  * @param {Object} config The config object
33204  */
33205
33206 Roo.bootstrap.FieldLabel = function(config){
33207     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33208     
33209     this.addEvents({
33210             /**
33211              * @event invalid
33212              * Fires after the field has been marked as invalid.
33213              * @param {Roo.form.FieldLabel} this
33214              * @param {String} msg The validation message
33215              */
33216             invalid : true,
33217             /**
33218              * @event valid
33219              * Fires after the field has been validated with no errors.
33220              * @param {Roo.form.FieldLabel} this
33221              */
33222             valid : true
33223         });
33224 };
33225
33226 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33227     
33228     tag: 'label',
33229     cls: '',
33230     html: '',
33231     target: '',
33232     allowBlank : true,
33233     invalidClass : 'has-warning',
33234     validClass : 'has-success',
33235     iconTooltip : 'This field is required',
33236     indicatorpos : 'left',
33237     
33238     getAutoCreate : function(){
33239         
33240         var cls = "";
33241         if (!this.allowBlank) {
33242             cls  = "visible";
33243         }
33244         
33245         var cfg = {
33246             tag : this.tag,
33247             cls : 'roo-bootstrap-field-label ' + this.cls,
33248             for : this.target,
33249             cn : [
33250                 {
33251                     tag : 'i',
33252                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33253                     tooltip : this.iconTooltip
33254                 },
33255                 {
33256                     tag : 'span',
33257                     html : this.html
33258                 }
33259             ] 
33260         };
33261         
33262         if(this.indicatorpos == 'right'){
33263             var cfg = {
33264                 tag : this.tag,
33265                 cls : 'roo-bootstrap-field-label ' + this.cls,
33266                 for : this.target,
33267                 cn : [
33268                     {
33269                         tag : 'span',
33270                         html : this.html
33271                     },
33272                     {
33273                         tag : 'i',
33274                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33275                         tooltip : this.iconTooltip
33276                     }
33277                 ] 
33278             };
33279         }
33280         
33281         return cfg;
33282     },
33283     
33284     initEvents: function() 
33285     {
33286         Roo.bootstrap.Element.superclass.initEvents.call(this);
33287         
33288         this.indicator = this.indicatorEl();
33289         
33290         if(this.indicator){
33291             this.indicator.removeClass('visible');
33292             this.indicator.addClass('invisible');
33293         }
33294         
33295         Roo.bootstrap.FieldLabel.register(this);
33296     },
33297     
33298     indicatorEl : function()
33299     {
33300         var indicator = this.el.select('i.roo-required-indicator',true).first();
33301         
33302         if(!indicator){
33303             return false;
33304         }
33305         
33306         return indicator;
33307         
33308     },
33309     
33310     /**
33311      * Mark this field as valid
33312      */
33313     markValid : function()
33314     {
33315         if(this.indicator){
33316             this.indicator.removeClass('visible');
33317             this.indicator.addClass('invisible');
33318         }
33319         if (Roo.bootstrap.version == 3) {
33320             this.el.removeClass(this.invalidClass);
33321             this.el.addClass(this.validClass);
33322         } else {
33323             this.el.removeClass('is-invalid');
33324             this.el.addClass('is-valid');
33325         }
33326         
33327         
33328         this.fireEvent('valid', this);
33329     },
33330     
33331     /**
33332      * Mark this field as invalid
33333      * @param {String} msg The validation message
33334      */
33335     markInvalid : function(msg)
33336     {
33337         if(this.indicator){
33338             this.indicator.removeClass('invisible');
33339             this.indicator.addClass('visible');
33340         }
33341           if (Roo.bootstrap.version == 3) {
33342             this.el.removeClass(this.validClass);
33343             this.el.addClass(this.invalidClass);
33344         } else {
33345             this.el.removeClass('is-valid');
33346             this.el.addClass('is-invalid');
33347         }
33348         
33349         
33350         this.fireEvent('invalid', this, msg);
33351     }
33352     
33353    
33354 });
33355
33356 Roo.apply(Roo.bootstrap.FieldLabel, {
33357     
33358     groups: {},
33359     
33360      /**
33361     * register a FieldLabel Group
33362     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33363     */
33364     register : function(label)
33365     {
33366         if(this.groups.hasOwnProperty(label.target)){
33367             return;
33368         }
33369      
33370         this.groups[label.target] = label;
33371         
33372     },
33373     /**
33374     * fetch a FieldLabel Group based on the target
33375     * @param {string} target
33376     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33377     */
33378     get: function(target) {
33379         if (typeof(this.groups[target]) == 'undefined') {
33380             return false;
33381         }
33382         
33383         return this.groups[target] ;
33384     }
33385 });
33386
33387  
33388
33389  /*
33390  * - LGPL
33391  *
33392  * page DateSplitField.
33393  * 
33394  */
33395
33396
33397 /**
33398  * @class Roo.bootstrap.DateSplitField
33399  * @extends Roo.bootstrap.Component
33400  * Bootstrap DateSplitField class
33401  * @cfg {string} fieldLabel - the label associated
33402  * @cfg {Number} labelWidth set the width of label (0-12)
33403  * @cfg {String} labelAlign (top|left)
33404  * @cfg {Boolean} dayAllowBlank (true|false) default false
33405  * @cfg {Boolean} monthAllowBlank (true|false) default false
33406  * @cfg {Boolean} yearAllowBlank (true|false) default false
33407  * @cfg {string} dayPlaceholder 
33408  * @cfg {string} monthPlaceholder
33409  * @cfg {string} yearPlaceholder
33410  * @cfg {string} dayFormat default 'd'
33411  * @cfg {string} monthFormat default 'm'
33412  * @cfg {string} yearFormat default 'Y'
33413  * @cfg {Number} labellg set the width of label (1-12)
33414  * @cfg {Number} labelmd set the width of label (1-12)
33415  * @cfg {Number} labelsm set the width of label (1-12)
33416  * @cfg {Number} labelxs set the width of label (1-12)
33417
33418  *     
33419  * @constructor
33420  * Create a new DateSplitField
33421  * @param {Object} config The config object
33422  */
33423
33424 Roo.bootstrap.DateSplitField = function(config){
33425     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33426     
33427     this.addEvents({
33428         // raw events
33429          /**
33430          * @event years
33431          * getting the data of years
33432          * @param {Roo.bootstrap.DateSplitField} this
33433          * @param {Object} years
33434          */
33435         "years" : true,
33436         /**
33437          * @event days
33438          * getting the data of days
33439          * @param {Roo.bootstrap.DateSplitField} this
33440          * @param {Object} days
33441          */
33442         "days" : true,
33443         /**
33444          * @event invalid
33445          * Fires after the field has been marked as invalid.
33446          * @param {Roo.form.Field} this
33447          * @param {String} msg The validation message
33448          */
33449         invalid : true,
33450        /**
33451          * @event valid
33452          * Fires after the field has been validated with no errors.
33453          * @param {Roo.form.Field} this
33454          */
33455         valid : true
33456     });
33457 };
33458
33459 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33460     
33461     fieldLabel : '',
33462     labelAlign : 'top',
33463     labelWidth : 3,
33464     dayAllowBlank : false,
33465     monthAllowBlank : false,
33466     yearAllowBlank : false,
33467     dayPlaceholder : '',
33468     monthPlaceholder : '',
33469     yearPlaceholder : '',
33470     dayFormat : 'd',
33471     monthFormat : 'm',
33472     yearFormat : 'Y',
33473     isFormField : true,
33474     labellg : 0,
33475     labelmd : 0,
33476     labelsm : 0,
33477     labelxs : 0,
33478     
33479     getAutoCreate : function()
33480     {
33481         var cfg = {
33482             tag : 'div',
33483             cls : 'row roo-date-split-field-group',
33484             cn : [
33485                 {
33486                     tag : 'input',
33487                     type : 'hidden',
33488                     cls : 'form-hidden-field roo-date-split-field-group-value',
33489                     name : this.name
33490                 }
33491             ]
33492         };
33493         
33494         var labelCls = 'col-md-12';
33495         var contentCls = 'col-md-4';
33496         
33497         if(this.fieldLabel){
33498             
33499             var label = {
33500                 tag : 'div',
33501                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33502                 cn : [
33503                     {
33504                         tag : 'label',
33505                         html : this.fieldLabel
33506                     }
33507                 ]
33508             };
33509             
33510             if(this.labelAlign == 'left'){
33511             
33512                 if(this.labelWidth > 12){
33513                     label.style = "width: " + this.labelWidth + 'px';
33514                 }
33515
33516                 if(this.labelWidth < 13 && this.labelmd == 0){
33517                     this.labelmd = this.labelWidth;
33518                 }
33519
33520                 if(this.labellg > 0){
33521                     labelCls = ' col-lg-' + this.labellg;
33522                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33523                 }
33524
33525                 if(this.labelmd > 0){
33526                     labelCls = ' col-md-' + this.labelmd;
33527                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33528                 }
33529
33530                 if(this.labelsm > 0){
33531                     labelCls = ' col-sm-' + this.labelsm;
33532                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33533                 }
33534
33535                 if(this.labelxs > 0){
33536                     labelCls = ' col-xs-' + this.labelxs;
33537                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33538                 }
33539             }
33540             
33541             label.cls += ' ' + labelCls;
33542             
33543             cfg.cn.push(label);
33544         }
33545         
33546         Roo.each(['day', 'month', 'year'], function(t){
33547             cfg.cn.push({
33548                 tag : 'div',
33549                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33550             });
33551         }, this);
33552         
33553         return cfg;
33554     },
33555     
33556     inputEl: function ()
33557     {
33558         return this.el.select('.roo-date-split-field-group-value', true).first();
33559     },
33560     
33561     onRender : function(ct, position) 
33562     {
33563         var _this = this;
33564         
33565         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33566         
33567         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33568         
33569         this.dayField = new Roo.bootstrap.ComboBox({
33570             allowBlank : this.dayAllowBlank,
33571             alwaysQuery : true,
33572             displayField : 'value',
33573             editable : false,
33574             fieldLabel : '',
33575             forceSelection : true,
33576             mode : 'local',
33577             placeholder : this.dayPlaceholder,
33578             selectOnFocus : true,
33579             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33580             triggerAction : 'all',
33581             typeAhead : true,
33582             valueField : 'value',
33583             store : new Roo.data.SimpleStore({
33584                 data : (function() {    
33585                     var days = [];
33586                     _this.fireEvent('days', _this, days);
33587                     return days;
33588                 })(),
33589                 fields : [ 'value' ]
33590             }),
33591             listeners : {
33592                 select : function (_self, record, index)
33593                 {
33594                     _this.setValue(_this.getValue());
33595                 }
33596             }
33597         });
33598
33599         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33600         
33601         this.monthField = new Roo.bootstrap.MonthField({
33602             after : '<i class=\"fa fa-calendar\"></i>',
33603             allowBlank : this.monthAllowBlank,
33604             placeholder : this.monthPlaceholder,
33605             readOnly : true,
33606             listeners : {
33607                 render : function (_self)
33608                 {
33609                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33610                         e.preventDefault();
33611                         _self.focus();
33612                     });
33613                 },
33614                 select : function (_self, oldvalue, newvalue)
33615                 {
33616                     _this.setValue(_this.getValue());
33617                 }
33618             }
33619         });
33620         
33621         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33622         
33623         this.yearField = new Roo.bootstrap.ComboBox({
33624             allowBlank : this.yearAllowBlank,
33625             alwaysQuery : true,
33626             displayField : 'value',
33627             editable : false,
33628             fieldLabel : '',
33629             forceSelection : true,
33630             mode : 'local',
33631             placeholder : this.yearPlaceholder,
33632             selectOnFocus : true,
33633             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33634             triggerAction : 'all',
33635             typeAhead : true,
33636             valueField : 'value',
33637             store : new Roo.data.SimpleStore({
33638                 data : (function() {
33639                     var years = [];
33640                     _this.fireEvent('years', _this, years);
33641                     return years;
33642                 })(),
33643                 fields : [ 'value' ]
33644             }),
33645             listeners : {
33646                 select : function (_self, record, index)
33647                 {
33648                     _this.setValue(_this.getValue());
33649                 }
33650             }
33651         });
33652
33653         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33654     },
33655     
33656     setValue : function(v, format)
33657     {
33658         this.inputEl.dom.value = v;
33659         
33660         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33661         
33662         var d = Date.parseDate(v, f);
33663         
33664         if(!d){
33665             this.validate();
33666             return;
33667         }
33668         
33669         this.setDay(d.format(this.dayFormat));
33670         this.setMonth(d.format(this.monthFormat));
33671         this.setYear(d.format(this.yearFormat));
33672         
33673         this.validate();
33674         
33675         return;
33676     },
33677     
33678     setDay : function(v)
33679     {
33680         this.dayField.setValue(v);
33681         this.inputEl.dom.value = this.getValue();
33682         this.validate();
33683         return;
33684     },
33685     
33686     setMonth : function(v)
33687     {
33688         this.monthField.setValue(v, true);
33689         this.inputEl.dom.value = this.getValue();
33690         this.validate();
33691         return;
33692     },
33693     
33694     setYear : function(v)
33695     {
33696         this.yearField.setValue(v);
33697         this.inputEl.dom.value = this.getValue();
33698         this.validate();
33699         return;
33700     },
33701     
33702     getDay : function()
33703     {
33704         return this.dayField.getValue();
33705     },
33706     
33707     getMonth : function()
33708     {
33709         return this.monthField.getValue();
33710     },
33711     
33712     getYear : function()
33713     {
33714         return this.yearField.getValue();
33715     },
33716     
33717     getValue : function()
33718     {
33719         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33720         
33721         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33722         
33723         return date;
33724     },
33725     
33726     reset : function()
33727     {
33728         this.setDay('');
33729         this.setMonth('');
33730         this.setYear('');
33731         this.inputEl.dom.value = '';
33732         this.validate();
33733         return;
33734     },
33735     
33736     validate : function()
33737     {
33738         var d = this.dayField.validate();
33739         var m = this.monthField.validate();
33740         var y = this.yearField.validate();
33741         
33742         var valid = true;
33743         
33744         if(
33745                 (!this.dayAllowBlank && !d) ||
33746                 (!this.monthAllowBlank && !m) ||
33747                 (!this.yearAllowBlank && !y)
33748         ){
33749             valid = false;
33750         }
33751         
33752         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33753             return valid;
33754         }
33755         
33756         if(valid){
33757             this.markValid();
33758             return valid;
33759         }
33760         
33761         this.markInvalid();
33762         
33763         return valid;
33764     },
33765     
33766     markValid : function()
33767     {
33768         
33769         var label = this.el.select('label', true).first();
33770         var icon = this.el.select('i.fa-star', true).first();
33771
33772         if(label && icon){
33773             icon.remove();
33774         }
33775         
33776         this.fireEvent('valid', this);
33777     },
33778     
33779      /**
33780      * Mark this field as invalid
33781      * @param {String} msg The validation message
33782      */
33783     markInvalid : function(msg)
33784     {
33785         
33786         var label = this.el.select('label', true).first();
33787         var icon = this.el.select('i.fa-star', true).first();
33788
33789         if(label && !icon){
33790             this.el.select('.roo-date-split-field-label', true).createChild({
33791                 tag : 'i',
33792                 cls : 'text-danger fa fa-lg fa-star',
33793                 tooltip : 'This field is required',
33794                 style : 'margin-right:5px;'
33795             }, label, true);
33796         }
33797         
33798         this.fireEvent('invalid', this, msg);
33799     },
33800     
33801     clearInvalid : function()
33802     {
33803         var label = this.el.select('label', true).first();
33804         var icon = this.el.select('i.fa-star', true).first();
33805
33806         if(label && icon){
33807             icon.remove();
33808         }
33809         
33810         this.fireEvent('valid', this);
33811     },
33812     
33813     getName: function()
33814     {
33815         return this.name;
33816     }
33817     
33818 });
33819
33820  /**
33821  *
33822  * This is based on 
33823  * http://masonry.desandro.com
33824  *
33825  * The idea is to render all the bricks based on vertical width...
33826  *
33827  * The original code extends 'outlayer' - we might need to use that....
33828  * 
33829  */
33830
33831
33832 /**
33833  * @class Roo.bootstrap.LayoutMasonry
33834  * @extends Roo.bootstrap.Component
33835  * Bootstrap Layout Masonry class
33836  * 
33837  * @constructor
33838  * Create a new Element
33839  * @param {Object} config The config object
33840  */
33841
33842 Roo.bootstrap.LayoutMasonry = function(config){
33843     
33844     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33845     
33846     this.bricks = [];
33847     
33848     Roo.bootstrap.LayoutMasonry.register(this);
33849     
33850     this.addEvents({
33851         // raw events
33852         /**
33853          * @event layout
33854          * Fire after layout the items
33855          * @param {Roo.bootstrap.LayoutMasonry} this
33856          * @param {Roo.EventObject} e
33857          */
33858         "layout" : true
33859     });
33860     
33861 };
33862
33863 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33864     
33865     /**
33866      * @cfg {Boolean} isLayoutInstant = no animation?
33867      */   
33868     isLayoutInstant : false, // needed?
33869    
33870     /**
33871      * @cfg {Number} boxWidth  width of the columns
33872      */   
33873     boxWidth : 450,
33874     
33875       /**
33876      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33877      */   
33878     boxHeight : 0,
33879     
33880     /**
33881      * @cfg {Number} padWidth padding below box..
33882      */   
33883     padWidth : 10, 
33884     
33885     /**
33886      * @cfg {Number} gutter gutter width..
33887      */   
33888     gutter : 10,
33889     
33890      /**
33891      * @cfg {Number} maxCols maximum number of columns
33892      */   
33893     
33894     maxCols: 0,
33895     
33896     /**
33897      * @cfg {Boolean} isAutoInitial defalut true
33898      */   
33899     isAutoInitial : true, 
33900     
33901     containerWidth: 0,
33902     
33903     /**
33904      * @cfg {Boolean} isHorizontal defalut false
33905      */   
33906     isHorizontal : false, 
33907
33908     currentSize : null,
33909     
33910     tag: 'div',
33911     
33912     cls: '',
33913     
33914     bricks: null, //CompositeElement
33915     
33916     cols : 1,
33917     
33918     _isLayoutInited : false,
33919     
33920 //    isAlternative : false, // only use for vertical layout...
33921     
33922     /**
33923      * @cfg {Number} alternativePadWidth padding below box..
33924      */   
33925     alternativePadWidth : 50,
33926     
33927     selectedBrick : [],
33928     
33929     getAutoCreate : function(){
33930         
33931         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33932         
33933         var cfg = {
33934             tag: this.tag,
33935             cls: 'blog-masonary-wrapper ' + this.cls,
33936             cn : {
33937                 cls : 'mas-boxes masonary'
33938             }
33939         };
33940         
33941         return cfg;
33942     },
33943     
33944     getChildContainer: function( )
33945     {
33946         if (this.boxesEl) {
33947             return this.boxesEl;
33948         }
33949         
33950         this.boxesEl = this.el.select('.mas-boxes').first();
33951         
33952         return this.boxesEl;
33953     },
33954     
33955     
33956     initEvents : function()
33957     {
33958         var _this = this;
33959         
33960         if(this.isAutoInitial){
33961             Roo.log('hook children rendered');
33962             this.on('childrenrendered', function() {
33963                 Roo.log('children rendered');
33964                 _this.initial();
33965             } ,this);
33966         }
33967     },
33968     
33969     initial : function()
33970     {
33971         this.selectedBrick = [];
33972         
33973         this.currentSize = this.el.getBox(true);
33974         
33975         Roo.EventManager.onWindowResize(this.resize, this); 
33976
33977         if(!this.isAutoInitial){
33978             this.layout();
33979             return;
33980         }
33981         
33982         this.layout();
33983         
33984         return;
33985         //this.layout.defer(500,this);
33986         
33987     },
33988     
33989     resize : function()
33990     {
33991         var cs = this.el.getBox(true);
33992         
33993         if (
33994                 this.currentSize.width == cs.width && 
33995                 this.currentSize.x == cs.x && 
33996                 this.currentSize.height == cs.height && 
33997                 this.currentSize.y == cs.y 
33998         ) {
33999             Roo.log("no change in with or X or Y");
34000             return;
34001         }
34002         
34003         this.currentSize = cs;
34004         
34005         this.layout();
34006         
34007     },
34008     
34009     layout : function()
34010     {   
34011         this._resetLayout();
34012         
34013         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34014         
34015         this.layoutItems( isInstant );
34016       
34017         this._isLayoutInited = true;
34018         
34019         this.fireEvent('layout', this);
34020         
34021     },
34022     
34023     _resetLayout : function()
34024     {
34025         if(this.isHorizontal){
34026             this.horizontalMeasureColumns();
34027             return;
34028         }
34029         
34030         this.verticalMeasureColumns();
34031         
34032     },
34033     
34034     verticalMeasureColumns : function()
34035     {
34036         this.getContainerWidth();
34037         
34038 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34039 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34040 //            return;
34041 //        }
34042         
34043         var boxWidth = this.boxWidth + this.padWidth;
34044         
34045         if(this.containerWidth < this.boxWidth){
34046             boxWidth = this.containerWidth
34047         }
34048         
34049         var containerWidth = this.containerWidth;
34050         
34051         var cols = Math.floor(containerWidth / boxWidth);
34052         
34053         this.cols = Math.max( cols, 1 );
34054         
34055         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34056         
34057         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34058         
34059         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34060         
34061         this.colWidth = boxWidth + avail - this.padWidth;
34062         
34063         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34064         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34065     },
34066     
34067     horizontalMeasureColumns : function()
34068     {
34069         this.getContainerWidth();
34070         
34071         var boxWidth = this.boxWidth;
34072         
34073         if(this.containerWidth < boxWidth){
34074             boxWidth = this.containerWidth;
34075         }
34076         
34077         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34078         
34079         this.el.setHeight(boxWidth);
34080         
34081     },
34082     
34083     getContainerWidth : function()
34084     {
34085         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34086     },
34087     
34088     layoutItems : function( isInstant )
34089     {
34090         Roo.log(this.bricks);
34091         
34092         var items = Roo.apply([], this.bricks);
34093         
34094         if(this.isHorizontal){
34095             this._horizontalLayoutItems( items , isInstant );
34096             return;
34097         }
34098         
34099 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34100 //            this._verticalAlternativeLayoutItems( items , isInstant );
34101 //            return;
34102 //        }
34103         
34104         this._verticalLayoutItems( items , isInstant );
34105         
34106     },
34107     
34108     _verticalLayoutItems : function ( items , isInstant)
34109     {
34110         if ( !items || !items.length ) {
34111             return;
34112         }
34113         
34114         var standard = [
34115             ['xs', 'xs', 'xs', 'tall'],
34116             ['xs', 'xs', 'tall'],
34117             ['xs', 'xs', 'sm'],
34118             ['xs', 'xs', 'xs'],
34119             ['xs', 'tall'],
34120             ['xs', 'sm'],
34121             ['xs', 'xs'],
34122             ['xs'],
34123             
34124             ['sm', 'xs', 'xs'],
34125             ['sm', 'xs'],
34126             ['sm'],
34127             
34128             ['tall', 'xs', 'xs', 'xs'],
34129             ['tall', 'xs', 'xs'],
34130             ['tall', 'xs'],
34131             ['tall']
34132             
34133         ];
34134         
34135         var queue = [];
34136         
34137         var boxes = [];
34138         
34139         var box = [];
34140         
34141         Roo.each(items, function(item, k){
34142             
34143             switch (item.size) {
34144                 // these layouts take up a full box,
34145                 case 'md' :
34146                 case 'md-left' :
34147                 case 'md-right' :
34148                 case 'wide' :
34149                     
34150                     if(box.length){
34151                         boxes.push(box);
34152                         box = [];
34153                     }
34154                     
34155                     boxes.push([item]);
34156                     
34157                     break;
34158                     
34159                 case 'xs' :
34160                 case 'sm' :
34161                 case 'tall' :
34162                     
34163                     box.push(item);
34164                     
34165                     break;
34166                 default :
34167                     break;
34168                     
34169             }
34170             
34171         }, this);
34172         
34173         if(box.length){
34174             boxes.push(box);
34175             box = [];
34176         }
34177         
34178         var filterPattern = function(box, length)
34179         {
34180             if(!box.length){
34181                 return;
34182             }
34183             
34184             var match = false;
34185             
34186             var pattern = box.slice(0, length);
34187             
34188             var format = [];
34189             
34190             Roo.each(pattern, function(i){
34191                 format.push(i.size);
34192             }, this);
34193             
34194             Roo.each(standard, function(s){
34195                 
34196                 if(String(s) != String(format)){
34197                     return;
34198                 }
34199                 
34200                 match = true;
34201                 return false;
34202                 
34203             }, this);
34204             
34205             if(!match && length == 1){
34206                 return;
34207             }
34208             
34209             if(!match){
34210                 filterPattern(box, length - 1);
34211                 return;
34212             }
34213                 
34214             queue.push(pattern);
34215
34216             box = box.slice(length, box.length);
34217
34218             filterPattern(box, 4);
34219
34220             return;
34221             
34222         }
34223         
34224         Roo.each(boxes, function(box, k){
34225             
34226             if(!box.length){
34227                 return;
34228             }
34229             
34230             if(box.length == 1){
34231                 queue.push(box);
34232                 return;
34233             }
34234             
34235             filterPattern(box, 4);
34236             
34237         }, this);
34238         
34239         this._processVerticalLayoutQueue( queue, isInstant );
34240         
34241     },
34242     
34243 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34244 //    {
34245 //        if ( !items || !items.length ) {
34246 //            return;
34247 //        }
34248 //
34249 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34250 //        
34251 //    },
34252     
34253     _horizontalLayoutItems : function ( items , isInstant)
34254     {
34255         if ( !items || !items.length || items.length < 3) {
34256             return;
34257         }
34258         
34259         items.reverse();
34260         
34261         var eItems = items.slice(0, 3);
34262         
34263         items = items.slice(3, items.length);
34264         
34265         var standard = [
34266             ['xs', 'xs', 'xs', 'wide'],
34267             ['xs', 'xs', 'wide'],
34268             ['xs', 'xs', 'sm'],
34269             ['xs', 'xs', 'xs'],
34270             ['xs', 'wide'],
34271             ['xs', 'sm'],
34272             ['xs', 'xs'],
34273             ['xs'],
34274             
34275             ['sm', 'xs', 'xs'],
34276             ['sm', 'xs'],
34277             ['sm'],
34278             
34279             ['wide', 'xs', 'xs', 'xs'],
34280             ['wide', 'xs', 'xs'],
34281             ['wide', 'xs'],
34282             ['wide'],
34283             
34284             ['wide-thin']
34285         ];
34286         
34287         var queue = [];
34288         
34289         var boxes = [];
34290         
34291         var box = [];
34292         
34293         Roo.each(items, function(item, k){
34294             
34295             switch (item.size) {
34296                 case 'md' :
34297                 case 'md-left' :
34298                 case 'md-right' :
34299                 case 'tall' :
34300                     
34301                     if(box.length){
34302                         boxes.push(box);
34303                         box = [];
34304                     }
34305                     
34306                     boxes.push([item]);
34307                     
34308                     break;
34309                     
34310                 case 'xs' :
34311                 case 'sm' :
34312                 case 'wide' :
34313                 case 'wide-thin' :
34314                     
34315                     box.push(item);
34316                     
34317                     break;
34318                 default :
34319                     break;
34320                     
34321             }
34322             
34323         }, this);
34324         
34325         if(box.length){
34326             boxes.push(box);
34327             box = [];
34328         }
34329         
34330         var filterPattern = function(box, length)
34331         {
34332             if(!box.length){
34333                 return;
34334             }
34335             
34336             var match = false;
34337             
34338             var pattern = box.slice(0, length);
34339             
34340             var format = [];
34341             
34342             Roo.each(pattern, function(i){
34343                 format.push(i.size);
34344             }, this);
34345             
34346             Roo.each(standard, function(s){
34347                 
34348                 if(String(s) != String(format)){
34349                     return;
34350                 }
34351                 
34352                 match = true;
34353                 return false;
34354                 
34355             }, this);
34356             
34357             if(!match && length == 1){
34358                 return;
34359             }
34360             
34361             if(!match){
34362                 filterPattern(box, length - 1);
34363                 return;
34364             }
34365                 
34366             queue.push(pattern);
34367
34368             box = box.slice(length, box.length);
34369
34370             filterPattern(box, 4);
34371
34372             return;
34373             
34374         }
34375         
34376         Roo.each(boxes, function(box, k){
34377             
34378             if(!box.length){
34379                 return;
34380             }
34381             
34382             if(box.length == 1){
34383                 queue.push(box);
34384                 return;
34385             }
34386             
34387             filterPattern(box, 4);
34388             
34389         }, this);
34390         
34391         
34392         var prune = [];
34393         
34394         var pos = this.el.getBox(true);
34395         
34396         var minX = pos.x;
34397         
34398         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34399         
34400         var hit_end = false;
34401         
34402         Roo.each(queue, function(box){
34403             
34404             if(hit_end){
34405                 
34406                 Roo.each(box, function(b){
34407                 
34408                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34409                     b.el.hide();
34410
34411                 }, this);
34412
34413                 return;
34414             }
34415             
34416             var mx = 0;
34417             
34418             Roo.each(box, function(b){
34419                 
34420                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34421                 b.el.show();
34422
34423                 mx = Math.max(mx, b.x);
34424                 
34425             }, this);
34426             
34427             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34428             
34429             if(maxX < minX){
34430                 
34431                 Roo.each(box, function(b){
34432                 
34433                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34434                     b.el.hide();
34435                     
34436                 }, this);
34437                 
34438                 hit_end = true;
34439                 
34440                 return;
34441             }
34442             
34443             prune.push(box);
34444             
34445         }, this);
34446         
34447         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34448     },
34449     
34450     /** Sets position of item in DOM
34451     * @param {Element} item
34452     * @param {Number} x - horizontal position
34453     * @param {Number} y - vertical position
34454     * @param {Boolean} isInstant - disables transitions
34455     */
34456     _processVerticalLayoutQueue : function( queue, isInstant )
34457     {
34458         var pos = this.el.getBox(true);
34459         var x = pos.x;
34460         var y = pos.y;
34461         var maxY = [];
34462         
34463         for (var i = 0; i < this.cols; i++){
34464             maxY[i] = pos.y;
34465         }
34466         
34467         Roo.each(queue, function(box, k){
34468             
34469             var col = k % this.cols;
34470             
34471             Roo.each(box, function(b,kk){
34472                 
34473                 b.el.position('absolute');
34474                 
34475                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34476                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34477                 
34478                 if(b.size == 'md-left' || b.size == 'md-right'){
34479                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34480                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34481                 }
34482                 
34483                 b.el.setWidth(width);
34484                 b.el.setHeight(height);
34485                 // iframe?
34486                 b.el.select('iframe',true).setSize(width,height);
34487                 
34488             }, this);
34489             
34490             for (var i = 0; i < this.cols; i++){
34491                 
34492                 if(maxY[i] < maxY[col]){
34493                     col = i;
34494                     continue;
34495                 }
34496                 
34497                 col = Math.min(col, i);
34498                 
34499             }
34500             
34501             x = pos.x + col * (this.colWidth + this.padWidth);
34502             
34503             y = maxY[col];
34504             
34505             var positions = [];
34506             
34507             switch (box.length){
34508                 case 1 :
34509                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34510                     break;
34511                 case 2 :
34512                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34513                     break;
34514                 case 3 :
34515                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34516                     break;
34517                 case 4 :
34518                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34519                     break;
34520                 default :
34521                     break;
34522             }
34523             
34524             Roo.each(box, function(b,kk){
34525                 
34526                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34527                 
34528                 var sz = b.el.getSize();
34529                 
34530                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34531                 
34532             }, this);
34533             
34534         }, this);
34535         
34536         var mY = 0;
34537         
34538         for (var i = 0; i < this.cols; i++){
34539             mY = Math.max(mY, maxY[i]);
34540         }
34541         
34542         this.el.setHeight(mY - pos.y);
34543         
34544     },
34545     
34546 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34547 //    {
34548 //        var pos = this.el.getBox(true);
34549 //        var x = pos.x;
34550 //        var y = pos.y;
34551 //        var maxX = pos.right;
34552 //        
34553 //        var maxHeight = 0;
34554 //        
34555 //        Roo.each(items, function(item, k){
34556 //            
34557 //            var c = k % 2;
34558 //            
34559 //            item.el.position('absolute');
34560 //                
34561 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34562 //
34563 //            item.el.setWidth(width);
34564 //
34565 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34566 //
34567 //            item.el.setHeight(height);
34568 //            
34569 //            if(c == 0){
34570 //                item.el.setXY([x, y], isInstant ? false : true);
34571 //            } else {
34572 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34573 //            }
34574 //            
34575 //            y = y + height + this.alternativePadWidth;
34576 //            
34577 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34578 //            
34579 //        }, this);
34580 //        
34581 //        this.el.setHeight(maxHeight);
34582 //        
34583 //    },
34584     
34585     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34586     {
34587         var pos = this.el.getBox(true);
34588         
34589         var minX = pos.x;
34590         var minY = pos.y;
34591         
34592         var maxX = pos.right;
34593         
34594         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34595         
34596         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34597         
34598         Roo.each(queue, function(box, k){
34599             
34600             Roo.each(box, function(b, kk){
34601                 
34602                 b.el.position('absolute');
34603                 
34604                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34605                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34606                 
34607                 if(b.size == 'md-left' || b.size == 'md-right'){
34608                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34609                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34610                 }
34611                 
34612                 b.el.setWidth(width);
34613                 b.el.setHeight(height);
34614                 
34615             }, this);
34616             
34617             if(!box.length){
34618                 return;
34619             }
34620             
34621             var positions = [];
34622             
34623             switch (box.length){
34624                 case 1 :
34625                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34626                     break;
34627                 case 2 :
34628                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34629                     break;
34630                 case 3 :
34631                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34632                     break;
34633                 case 4 :
34634                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34635                     break;
34636                 default :
34637                     break;
34638             }
34639             
34640             Roo.each(box, function(b,kk){
34641                 
34642                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34643                 
34644                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34645                 
34646             }, this);
34647             
34648         }, this);
34649         
34650     },
34651     
34652     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34653     {
34654         Roo.each(eItems, function(b,k){
34655             
34656             b.size = (k == 0) ? 'sm' : 'xs';
34657             b.x = (k == 0) ? 2 : 1;
34658             b.y = (k == 0) ? 2 : 1;
34659             
34660             b.el.position('absolute');
34661             
34662             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34663                 
34664             b.el.setWidth(width);
34665             
34666             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34667             
34668             b.el.setHeight(height);
34669             
34670         }, this);
34671
34672         var positions = [];
34673         
34674         positions.push({
34675             x : maxX - this.unitWidth * 2 - this.gutter,
34676             y : minY
34677         });
34678         
34679         positions.push({
34680             x : maxX - this.unitWidth,
34681             y : minY + (this.unitWidth + this.gutter) * 2
34682         });
34683         
34684         positions.push({
34685             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34686             y : minY
34687         });
34688         
34689         Roo.each(eItems, function(b,k){
34690             
34691             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34692
34693         }, this);
34694         
34695     },
34696     
34697     getVerticalOneBoxColPositions : function(x, y, box)
34698     {
34699         var pos = [];
34700         
34701         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34702         
34703         if(box[0].size == 'md-left'){
34704             rand = 0;
34705         }
34706         
34707         if(box[0].size == 'md-right'){
34708             rand = 1;
34709         }
34710         
34711         pos.push({
34712             x : x + (this.unitWidth + this.gutter) * rand,
34713             y : y
34714         });
34715         
34716         return pos;
34717     },
34718     
34719     getVerticalTwoBoxColPositions : function(x, y, box)
34720     {
34721         var pos = [];
34722         
34723         if(box[0].size == 'xs'){
34724             
34725             pos.push({
34726                 x : x,
34727                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34728             });
34729
34730             pos.push({
34731                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34732                 y : y
34733             });
34734             
34735             return pos;
34736             
34737         }
34738         
34739         pos.push({
34740             x : x,
34741             y : y
34742         });
34743
34744         pos.push({
34745             x : x + (this.unitWidth + this.gutter) * 2,
34746             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34747         });
34748         
34749         return pos;
34750         
34751     },
34752     
34753     getVerticalThreeBoxColPositions : function(x, y, box)
34754     {
34755         var pos = [];
34756         
34757         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34758             
34759             pos.push({
34760                 x : x,
34761                 y : y
34762             });
34763
34764             pos.push({
34765                 x : x + (this.unitWidth + this.gutter) * 1,
34766                 y : y
34767             });
34768             
34769             pos.push({
34770                 x : x + (this.unitWidth + this.gutter) * 2,
34771                 y : y
34772             });
34773             
34774             return pos;
34775             
34776         }
34777         
34778         if(box[0].size == 'xs' && box[1].size == 'xs'){
34779             
34780             pos.push({
34781                 x : x,
34782                 y : y
34783             });
34784
34785             pos.push({
34786                 x : x,
34787                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34788             });
34789             
34790             pos.push({
34791                 x : x + (this.unitWidth + this.gutter) * 1,
34792                 y : y
34793             });
34794             
34795             return pos;
34796             
34797         }
34798         
34799         pos.push({
34800             x : x,
34801             y : y
34802         });
34803
34804         pos.push({
34805             x : x + (this.unitWidth + this.gutter) * 2,
34806             y : y
34807         });
34808
34809         pos.push({
34810             x : x + (this.unitWidth + this.gutter) * 2,
34811             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34812         });
34813             
34814         return pos;
34815         
34816     },
34817     
34818     getVerticalFourBoxColPositions : function(x, y, box)
34819     {
34820         var pos = [];
34821         
34822         if(box[0].size == 'xs'){
34823             
34824             pos.push({
34825                 x : x,
34826                 y : y
34827             });
34828
34829             pos.push({
34830                 x : x,
34831                 y : y + (this.unitHeight + this.gutter) * 1
34832             });
34833             
34834             pos.push({
34835                 x : x,
34836                 y : y + (this.unitHeight + this.gutter) * 2
34837             });
34838             
34839             pos.push({
34840                 x : x + (this.unitWidth + this.gutter) * 1,
34841                 y : y
34842             });
34843             
34844             return pos;
34845             
34846         }
34847         
34848         pos.push({
34849             x : x,
34850             y : y
34851         });
34852
34853         pos.push({
34854             x : x + (this.unitWidth + this.gutter) * 2,
34855             y : y
34856         });
34857
34858         pos.push({
34859             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34860             y : y + (this.unitHeight + this.gutter) * 1
34861         });
34862
34863         pos.push({
34864             x : x + (this.unitWidth + this.gutter) * 2,
34865             y : y + (this.unitWidth + this.gutter) * 2
34866         });
34867
34868         return pos;
34869         
34870     },
34871     
34872     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34873     {
34874         var pos = [];
34875         
34876         if(box[0].size == 'md-left'){
34877             pos.push({
34878                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34879                 y : minY
34880             });
34881             
34882             return pos;
34883         }
34884         
34885         if(box[0].size == 'md-right'){
34886             pos.push({
34887                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34888                 y : minY + (this.unitWidth + this.gutter) * 1
34889             });
34890             
34891             return pos;
34892         }
34893         
34894         var rand = Math.floor(Math.random() * (4 - box[0].y));
34895         
34896         pos.push({
34897             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34898             y : minY + (this.unitWidth + this.gutter) * rand
34899         });
34900         
34901         return pos;
34902         
34903     },
34904     
34905     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34906     {
34907         var pos = [];
34908         
34909         if(box[0].size == 'xs'){
34910             
34911             pos.push({
34912                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34913                 y : minY
34914             });
34915
34916             pos.push({
34917                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34918                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34919             });
34920             
34921             return pos;
34922             
34923         }
34924         
34925         pos.push({
34926             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34927             y : minY
34928         });
34929
34930         pos.push({
34931             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34932             y : minY + (this.unitWidth + this.gutter) * 2
34933         });
34934         
34935         return pos;
34936         
34937     },
34938     
34939     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34940     {
34941         var pos = [];
34942         
34943         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34944             
34945             pos.push({
34946                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34947                 y : minY
34948             });
34949
34950             pos.push({
34951                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34952                 y : minY + (this.unitWidth + this.gutter) * 1
34953             });
34954             
34955             pos.push({
34956                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34957                 y : minY + (this.unitWidth + this.gutter) * 2
34958             });
34959             
34960             return pos;
34961             
34962         }
34963         
34964         if(box[0].size == 'xs' && box[1].size == 'xs'){
34965             
34966             pos.push({
34967                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34968                 y : minY
34969             });
34970
34971             pos.push({
34972                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34973                 y : minY
34974             });
34975             
34976             pos.push({
34977                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34978                 y : minY + (this.unitWidth + this.gutter) * 1
34979             });
34980             
34981             return pos;
34982             
34983         }
34984         
34985         pos.push({
34986             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34987             y : minY
34988         });
34989
34990         pos.push({
34991             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34992             y : minY + (this.unitWidth + this.gutter) * 2
34993         });
34994
34995         pos.push({
34996             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34997             y : minY + (this.unitWidth + this.gutter) * 2
34998         });
34999             
35000         return pos;
35001         
35002     },
35003     
35004     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35005     {
35006         var pos = [];
35007         
35008         if(box[0].size == 'xs'){
35009             
35010             pos.push({
35011                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35012                 y : minY
35013             });
35014
35015             pos.push({
35016                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35017                 y : minY
35018             });
35019             
35020             pos.push({
35021                 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),
35022                 y : minY
35023             });
35024             
35025             pos.push({
35026                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35027                 y : minY + (this.unitWidth + this.gutter) * 1
35028             });
35029             
35030             return pos;
35031             
35032         }
35033         
35034         pos.push({
35035             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35036             y : minY
35037         });
35038         
35039         pos.push({
35040             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35041             y : minY + (this.unitWidth + this.gutter) * 2
35042         });
35043         
35044         pos.push({
35045             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35046             y : minY + (this.unitWidth + this.gutter) * 2
35047         });
35048         
35049         pos.push({
35050             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),
35051             y : minY + (this.unitWidth + this.gutter) * 2
35052         });
35053
35054         return pos;
35055         
35056     },
35057     
35058     /**
35059     * remove a Masonry Brick
35060     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35061     */
35062     removeBrick : function(brick_id)
35063     {
35064         if (!brick_id) {
35065             return;
35066         }
35067         
35068         for (var i = 0; i<this.bricks.length; i++) {
35069             if (this.bricks[i].id == brick_id) {
35070                 this.bricks.splice(i,1);
35071                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35072                 this.initial();
35073             }
35074         }
35075     },
35076     
35077     /**
35078     * adds a Masonry Brick
35079     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35080     */
35081     addBrick : function(cfg)
35082     {
35083         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35084         //this.register(cn);
35085         cn.parentId = this.id;
35086         cn.render(this.el);
35087         return cn;
35088     },
35089     
35090     /**
35091     * register a Masonry Brick
35092     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35093     */
35094     
35095     register : function(brick)
35096     {
35097         this.bricks.push(brick);
35098         brick.masonryId = this.id;
35099     },
35100     
35101     /**
35102     * clear all the Masonry Brick
35103     */
35104     clearAll : function()
35105     {
35106         this.bricks = [];
35107         //this.getChildContainer().dom.innerHTML = "";
35108         this.el.dom.innerHTML = '';
35109     },
35110     
35111     getSelected : function()
35112     {
35113         if (!this.selectedBrick) {
35114             return false;
35115         }
35116         
35117         return this.selectedBrick;
35118     }
35119 });
35120
35121 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35122     
35123     groups: {},
35124      /**
35125     * register a Masonry Layout
35126     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35127     */
35128     
35129     register : function(layout)
35130     {
35131         this.groups[layout.id] = layout;
35132     },
35133     /**
35134     * fetch a  Masonry Layout based on the masonry layout ID
35135     * @param {string} the masonry layout to add
35136     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35137     */
35138     
35139     get: function(layout_id) {
35140         if (typeof(this.groups[layout_id]) == 'undefined') {
35141             return false;
35142         }
35143         return this.groups[layout_id] ;
35144     }
35145     
35146     
35147     
35148 });
35149
35150  
35151
35152  /**
35153  *
35154  * This is based on 
35155  * http://masonry.desandro.com
35156  *
35157  * The idea is to render all the bricks based on vertical width...
35158  *
35159  * The original code extends 'outlayer' - we might need to use that....
35160  * 
35161  */
35162
35163
35164 /**
35165  * @class Roo.bootstrap.LayoutMasonryAuto
35166  * @extends Roo.bootstrap.Component
35167  * Bootstrap Layout Masonry class
35168  * 
35169  * @constructor
35170  * Create a new Element
35171  * @param {Object} config The config object
35172  */
35173
35174 Roo.bootstrap.LayoutMasonryAuto = function(config){
35175     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35176 };
35177
35178 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35179     
35180       /**
35181      * @cfg {Boolean} isFitWidth  - resize the width..
35182      */   
35183     isFitWidth : false,  // options..
35184     /**
35185      * @cfg {Boolean} isOriginLeft = left align?
35186      */   
35187     isOriginLeft : true,
35188     /**
35189      * @cfg {Boolean} isOriginTop = top align?
35190      */   
35191     isOriginTop : false,
35192     /**
35193      * @cfg {Boolean} isLayoutInstant = no animation?
35194      */   
35195     isLayoutInstant : false, // needed?
35196     /**
35197      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35198      */   
35199     isResizingContainer : true,
35200     /**
35201      * @cfg {Number} columnWidth  width of the columns 
35202      */   
35203     
35204     columnWidth : 0,
35205     
35206     /**
35207      * @cfg {Number} maxCols maximum number of columns
35208      */   
35209     
35210     maxCols: 0,
35211     /**
35212      * @cfg {Number} padHeight padding below box..
35213      */   
35214     
35215     padHeight : 10, 
35216     
35217     /**
35218      * @cfg {Boolean} isAutoInitial defalut true
35219      */   
35220     
35221     isAutoInitial : true, 
35222     
35223     // private?
35224     gutter : 0,
35225     
35226     containerWidth: 0,
35227     initialColumnWidth : 0,
35228     currentSize : null,
35229     
35230     colYs : null, // array.
35231     maxY : 0,
35232     padWidth: 10,
35233     
35234     
35235     tag: 'div',
35236     cls: '',
35237     bricks: null, //CompositeElement
35238     cols : 0, // array?
35239     // element : null, // wrapped now this.el
35240     _isLayoutInited : null, 
35241     
35242     
35243     getAutoCreate : function(){
35244         
35245         var cfg = {
35246             tag: this.tag,
35247             cls: 'blog-masonary-wrapper ' + this.cls,
35248             cn : {
35249                 cls : 'mas-boxes masonary'
35250             }
35251         };
35252         
35253         return cfg;
35254     },
35255     
35256     getChildContainer: function( )
35257     {
35258         if (this.boxesEl) {
35259             return this.boxesEl;
35260         }
35261         
35262         this.boxesEl = this.el.select('.mas-boxes').first();
35263         
35264         return this.boxesEl;
35265     },
35266     
35267     
35268     initEvents : function()
35269     {
35270         var _this = this;
35271         
35272         if(this.isAutoInitial){
35273             Roo.log('hook children rendered');
35274             this.on('childrenrendered', function() {
35275                 Roo.log('children rendered');
35276                 _this.initial();
35277             } ,this);
35278         }
35279         
35280     },
35281     
35282     initial : function()
35283     {
35284         this.reloadItems();
35285
35286         this.currentSize = this.el.getBox(true);
35287
35288         /// was window resize... - let's see if this works..
35289         Roo.EventManager.onWindowResize(this.resize, this); 
35290
35291         if(!this.isAutoInitial){
35292             this.layout();
35293             return;
35294         }
35295         
35296         this.layout.defer(500,this);
35297     },
35298     
35299     reloadItems: function()
35300     {
35301         this.bricks = this.el.select('.masonry-brick', true);
35302         
35303         this.bricks.each(function(b) {
35304             //Roo.log(b.getSize());
35305             if (!b.attr('originalwidth')) {
35306                 b.attr('originalwidth',  b.getSize().width);
35307             }
35308             
35309         });
35310         
35311         Roo.log(this.bricks.elements.length);
35312     },
35313     
35314     resize : function()
35315     {
35316         Roo.log('resize');
35317         var cs = this.el.getBox(true);
35318         
35319         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35320             Roo.log("no change in with or X");
35321             return;
35322         }
35323         this.currentSize = cs;
35324         this.layout();
35325     },
35326     
35327     layout : function()
35328     {
35329          Roo.log('layout');
35330         this._resetLayout();
35331         //this._manageStamps();
35332       
35333         // don't animate first layout
35334         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35335         this.layoutItems( isInstant );
35336       
35337         // flag for initalized
35338         this._isLayoutInited = true;
35339     },
35340     
35341     layoutItems : function( isInstant )
35342     {
35343         //var items = this._getItemsForLayout( this.items );
35344         // original code supports filtering layout items.. we just ignore it..
35345         
35346         this._layoutItems( this.bricks , isInstant );
35347       
35348         this._postLayout();
35349     },
35350     _layoutItems : function ( items , isInstant)
35351     {
35352        //this.fireEvent( 'layout', this, items );
35353     
35354
35355         if ( !items || !items.elements.length ) {
35356           // no items, emit event with empty array
35357             return;
35358         }
35359
35360         var queue = [];
35361         items.each(function(item) {
35362             Roo.log("layout item");
35363             Roo.log(item);
35364             // get x/y object from method
35365             var position = this._getItemLayoutPosition( item );
35366             // enqueue
35367             position.item = item;
35368             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35369             queue.push( position );
35370         }, this);
35371       
35372         this._processLayoutQueue( queue );
35373     },
35374     /** Sets position of item in DOM
35375     * @param {Element} item
35376     * @param {Number} x - horizontal position
35377     * @param {Number} y - vertical position
35378     * @param {Boolean} isInstant - disables transitions
35379     */
35380     _processLayoutQueue : function( queue )
35381     {
35382         for ( var i=0, len = queue.length; i < len; i++ ) {
35383             var obj = queue[i];
35384             obj.item.position('absolute');
35385             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35386         }
35387     },
35388       
35389     
35390     /**
35391     * Any logic you want to do after each layout,
35392     * i.e. size the container
35393     */
35394     _postLayout : function()
35395     {
35396         this.resizeContainer();
35397     },
35398     
35399     resizeContainer : function()
35400     {
35401         if ( !this.isResizingContainer ) {
35402             return;
35403         }
35404         var size = this._getContainerSize();
35405         if ( size ) {
35406             this.el.setSize(size.width,size.height);
35407             this.boxesEl.setSize(size.width,size.height);
35408         }
35409     },
35410     
35411     
35412     
35413     _resetLayout : function()
35414     {
35415         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35416         this.colWidth = this.el.getWidth();
35417         //this.gutter = this.el.getWidth(); 
35418         
35419         this.measureColumns();
35420
35421         // reset column Y
35422         var i = this.cols;
35423         this.colYs = [];
35424         while (i--) {
35425             this.colYs.push( 0 );
35426         }
35427     
35428         this.maxY = 0;
35429     },
35430
35431     measureColumns : function()
35432     {
35433         this.getContainerWidth();
35434       // if columnWidth is 0, default to outerWidth of first item
35435         if ( !this.columnWidth ) {
35436             var firstItem = this.bricks.first();
35437             Roo.log(firstItem);
35438             this.columnWidth  = this.containerWidth;
35439             if (firstItem && firstItem.attr('originalwidth') ) {
35440                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35441             }
35442             // columnWidth fall back to item of first element
35443             Roo.log("set column width?");
35444                         this.initialColumnWidth = this.columnWidth  ;
35445
35446             // if first elem has no width, default to size of container
35447             
35448         }
35449         
35450         
35451         if (this.initialColumnWidth) {
35452             this.columnWidth = this.initialColumnWidth;
35453         }
35454         
35455         
35456             
35457         // column width is fixed at the top - however if container width get's smaller we should
35458         // reduce it...
35459         
35460         // this bit calcs how man columns..
35461             
35462         var columnWidth = this.columnWidth += this.gutter;
35463       
35464         // calculate columns
35465         var containerWidth = this.containerWidth + this.gutter;
35466         
35467         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35468         // fix rounding errors, typically with gutters
35469         var excess = columnWidth - containerWidth % columnWidth;
35470         
35471         
35472         // if overshoot is less than a pixel, round up, otherwise floor it
35473         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35474         cols = Math[ mathMethod ]( cols );
35475         this.cols = Math.max( cols, 1 );
35476         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35477         
35478          // padding positioning..
35479         var totalColWidth = this.cols * this.columnWidth;
35480         var padavail = this.containerWidth - totalColWidth;
35481         // so for 2 columns - we need 3 'pads'
35482         
35483         var padNeeded = (1+this.cols) * this.padWidth;
35484         
35485         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35486         
35487         this.columnWidth += padExtra
35488         //this.padWidth = Math.floor(padavail /  ( this.cols));
35489         
35490         // adjust colum width so that padding is fixed??
35491         
35492         // we have 3 columns ... total = width * 3
35493         // we have X left over... that should be used by 
35494         
35495         //if (this.expandC) {
35496             
35497         //}
35498         
35499         
35500         
35501     },
35502     
35503     getContainerWidth : function()
35504     {
35505        /* // container is parent if fit width
35506         var container = this.isFitWidth ? this.element.parentNode : this.element;
35507         // check that this.size and size are there
35508         // IE8 triggers resize on body size change, so they might not be
35509         
35510         var size = getSize( container );  //FIXME
35511         this.containerWidth = size && size.innerWidth; //FIXME
35512         */
35513          
35514         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35515         
35516     },
35517     
35518     _getItemLayoutPosition : function( item )  // what is item?
35519     {
35520         // we resize the item to our columnWidth..
35521       
35522         item.setWidth(this.columnWidth);
35523         item.autoBoxAdjust  = false;
35524         
35525         var sz = item.getSize();
35526  
35527         // how many columns does this brick span
35528         var remainder = this.containerWidth % this.columnWidth;
35529         
35530         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35531         // round if off by 1 pixel, otherwise use ceil
35532         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35533         colSpan = Math.min( colSpan, this.cols );
35534         
35535         // normally this should be '1' as we dont' currently allow multi width columns..
35536         
35537         var colGroup = this._getColGroup( colSpan );
35538         // get the minimum Y value from the columns
35539         var minimumY = Math.min.apply( Math, colGroup );
35540         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35541         
35542         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35543          
35544         // position the brick
35545         var position = {
35546             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35547             y: this.currentSize.y + minimumY + this.padHeight
35548         };
35549         
35550         Roo.log(position);
35551         // apply setHeight to necessary columns
35552         var setHeight = minimumY + sz.height + this.padHeight;
35553         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35554         
35555         var setSpan = this.cols + 1 - colGroup.length;
35556         for ( var i = 0; i < setSpan; i++ ) {
35557           this.colYs[ shortColIndex + i ] = setHeight ;
35558         }
35559       
35560         return position;
35561     },
35562     
35563     /**
35564      * @param {Number} colSpan - number of columns the element spans
35565      * @returns {Array} colGroup
35566      */
35567     _getColGroup : function( colSpan )
35568     {
35569         if ( colSpan < 2 ) {
35570           // if brick spans only one column, use all the column Ys
35571           return this.colYs;
35572         }
35573       
35574         var colGroup = [];
35575         // how many different places could this brick fit horizontally
35576         var groupCount = this.cols + 1 - colSpan;
35577         // for each group potential horizontal position
35578         for ( var i = 0; i < groupCount; i++ ) {
35579           // make an array of colY values for that one group
35580           var groupColYs = this.colYs.slice( i, i + colSpan );
35581           // and get the max value of the array
35582           colGroup[i] = Math.max.apply( Math, groupColYs );
35583         }
35584         return colGroup;
35585     },
35586     /*
35587     _manageStamp : function( stamp )
35588     {
35589         var stampSize =  stamp.getSize();
35590         var offset = stamp.getBox();
35591         // get the columns that this stamp affects
35592         var firstX = this.isOriginLeft ? offset.x : offset.right;
35593         var lastX = firstX + stampSize.width;
35594         var firstCol = Math.floor( firstX / this.columnWidth );
35595         firstCol = Math.max( 0, firstCol );
35596         
35597         var lastCol = Math.floor( lastX / this.columnWidth );
35598         // lastCol should not go over if multiple of columnWidth #425
35599         lastCol -= lastX % this.columnWidth ? 0 : 1;
35600         lastCol = Math.min( this.cols - 1, lastCol );
35601         
35602         // set colYs to bottom of the stamp
35603         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35604             stampSize.height;
35605             
35606         for ( var i = firstCol; i <= lastCol; i++ ) {
35607           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35608         }
35609     },
35610     */
35611     
35612     _getContainerSize : function()
35613     {
35614         this.maxY = Math.max.apply( Math, this.colYs );
35615         var size = {
35616             height: this.maxY
35617         };
35618       
35619         if ( this.isFitWidth ) {
35620             size.width = this._getContainerFitWidth();
35621         }
35622       
35623         return size;
35624     },
35625     
35626     _getContainerFitWidth : function()
35627     {
35628         var unusedCols = 0;
35629         // count unused columns
35630         var i = this.cols;
35631         while ( --i ) {
35632           if ( this.colYs[i] !== 0 ) {
35633             break;
35634           }
35635           unusedCols++;
35636         }
35637         // fit container to columns that have been used
35638         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35639     },
35640     
35641     needsResizeLayout : function()
35642     {
35643         var previousWidth = this.containerWidth;
35644         this.getContainerWidth();
35645         return previousWidth !== this.containerWidth;
35646     }
35647  
35648 });
35649
35650  
35651
35652  /*
35653  * - LGPL
35654  *
35655  * element
35656  * 
35657  */
35658
35659 /**
35660  * @class Roo.bootstrap.MasonryBrick
35661  * @extends Roo.bootstrap.Component
35662  * Bootstrap MasonryBrick class
35663  * 
35664  * @constructor
35665  * Create a new MasonryBrick
35666  * @param {Object} config The config object
35667  */
35668
35669 Roo.bootstrap.MasonryBrick = function(config){
35670     
35671     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35672     
35673     Roo.bootstrap.MasonryBrick.register(this);
35674     
35675     this.addEvents({
35676         // raw events
35677         /**
35678          * @event click
35679          * When a MasonryBrick is clcik
35680          * @param {Roo.bootstrap.MasonryBrick} this
35681          * @param {Roo.EventObject} e
35682          */
35683         "click" : true
35684     });
35685 };
35686
35687 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35688     
35689     /**
35690      * @cfg {String} title
35691      */   
35692     title : '',
35693     /**
35694      * @cfg {String} html
35695      */   
35696     html : '',
35697     /**
35698      * @cfg {String} bgimage
35699      */   
35700     bgimage : '',
35701     /**
35702      * @cfg {String} videourl
35703      */   
35704     videourl : '',
35705     /**
35706      * @cfg {String} cls
35707      */   
35708     cls : '',
35709     /**
35710      * @cfg {String} href
35711      */   
35712     href : '',
35713     /**
35714      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35715      */   
35716     size : 'xs',
35717     
35718     /**
35719      * @cfg {String} placetitle (center|bottom)
35720      */   
35721     placetitle : '',
35722     
35723     /**
35724      * @cfg {Boolean} isFitContainer defalut true
35725      */   
35726     isFitContainer : true, 
35727     
35728     /**
35729      * @cfg {Boolean} preventDefault defalut false
35730      */   
35731     preventDefault : false, 
35732     
35733     /**
35734      * @cfg {Boolean} inverse defalut false
35735      */   
35736     maskInverse : false, 
35737     
35738     getAutoCreate : function()
35739     {
35740         if(!this.isFitContainer){
35741             return this.getSplitAutoCreate();
35742         }
35743         
35744         var cls = 'masonry-brick masonry-brick-full';
35745         
35746         if(this.href.length){
35747             cls += ' masonry-brick-link';
35748         }
35749         
35750         if(this.bgimage.length){
35751             cls += ' masonry-brick-image';
35752         }
35753         
35754         if(this.maskInverse){
35755             cls += ' mask-inverse';
35756         }
35757         
35758         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35759             cls += ' enable-mask';
35760         }
35761         
35762         if(this.size){
35763             cls += ' masonry-' + this.size + '-brick';
35764         }
35765         
35766         if(this.placetitle.length){
35767             
35768             switch (this.placetitle) {
35769                 case 'center' :
35770                     cls += ' masonry-center-title';
35771                     break;
35772                 case 'bottom' :
35773                     cls += ' masonry-bottom-title';
35774                     break;
35775                 default:
35776                     break;
35777             }
35778             
35779         } else {
35780             if(!this.html.length && !this.bgimage.length){
35781                 cls += ' masonry-center-title';
35782             }
35783
35784             if(!this.html.length && this.bgimage.length){
35785                 cls += ' masonry-bottom-title';
35786             }
35787         }
35788         
35789         if(this.cls){
35790             cls += ' ' + this.cls;
35791         }
35792         
35793         var cfg = {
35794             tag: (this.href.length) ? 'a' : 'div',
35795             cls: cls,
35796             cn: [
35797                 {
35798                     tag: 'div',
35799                     cls: 'masonry-brick-mask'
35800                 },
35801                 {
35802                     tag: 'div',
35803                     cls: 'masonry-brick-paragraph',
35804                     cn: []
35805                 }
35806             ]
35807         };
35808         
35809         if(this.href.length){
35810             cfg.href = this.href;
35811         }
35812         
35813         var cn = cfg.cn[1].cn;
35814         
35815         if(this.title.length){
35816             cn.push({
35817                 tag: 'h4',
35818                 cls: 'masonry-brick-title',
35819                 html: this.title
35820             });
35821         }
35822         
35823         if(this.html.length){
35824             cn.push({
35825                 tag: 'p',
35826                 cls: 'masonry-brick-text',
35827                 html: this.html
35828             });
35829         }
35830         
35831         if (!this.title.length && !this.html.length) {
35832             cfg.cn[1].cls += ' hide';
35833         }
35834         
35835         if(this.bgimage.length){
35836             cfg.cn.push({
35837                 tag: 'img',
35838                 cls: 'masonry-brick-image-view',
35839                 src: this.bgimage
35840             });
35841         }
35842         
35843         if(this.videourl.length){
35844             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35845             // youtube support only?
35846             cfg.cn.push({
35847                 tag: 'iframe',
35848                 cls: 'masonry-brick-image-view',
35849                 src: vurl,
35850                 frameborder : 0,
35851                 allowfullscreen : true
35852             });
35853         }
35854         
35855         return cfg;
35856         
35857     },
35858     
35859     getSplitAutoCreate : function()
35860     {
35861         var cls = 'masonry-brick masonry-brick-split';
35862         
35863         if(this.href.length){
35864             cls += ' masonry-brick-link';
35865         }
35866         
35867         if(this.bgimage.length){
35868             cls += ' masonry-brick-image';
35869         }
35870         
35871         if(this.size){
35872             cls += ' masonry-' + this.size + '-brick';
35873         }
35874         
35875         switch (this.placetitle) {
35876             case 'center' :
35877                 cls += ' masonry-center-title';
35878                 break;
35879             case 'bottom' :
35880                 cls += ' masonry-bottom-title';
35881                 break;
35882             default:
35883                 if(!this.bgimage.length){
35884                     cls += ' masonry-center-title';
35885                 }
35886
35887                 if(this.bgimage.length){
35888                     cls += ' masonry-bottom-title';
35889                 }
35890                 break;
35891         }
35892         
35893         if(this.cls){
35894             cls += ' ' + this.cls;
35895         }
35896         
35897         var cfg = {
35898             tag: (this.href.length) ? 'a' : 'div',
35899             cls: cls,
35900             cn: [
35901                 {
35902                     tag: 'div',
35903                     cls: 'masonry-brick-split-head',
35904                     cn: [
35905                         {
35906                             tag: 'div',
35907                             cls: 'masonry-brick-paragraph',
35908                             cn: []
35909                         }
35910                     ]
35911                 },
35912                 {
35913                     tag: 'div',
35914                     cls: 'masonry-brick-split-body',
35915                     cn: []
35916                 }
35917             ]
35918         };
35919         
35920         if(this.href.length){
35921             cfg.href = this.href;
35922         }
35923         
35924         if(this.title.length){
35925             cfg.cn[0].cn[0].cn.push({
35926                 tag: 'h4',
35927                 cls: 'masonry-brick-title',
35928                 html: this.title
35929             });
35930         }
35931         
35932         if(this.html.length){
35933             cfg.cn[1].cn.push({
35934                 tag: 'p',
35935                 cls: 'masonry-brick-text',
35936                 html: this.html
35937             });
35938         }
35939
35940         if(this.bgimage.length){
35941             cfg.cn[0].cn.push({
35942                 tag: 'img',
35943                 cls: 'masonry-brick-image-view',
35944                 src: this.bgimage
35945             });
35946         }
35947         
35948         if(this.videourl.length){
35949             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35950             // youtube support only?
35951             cfg.cn[0].cn.cn.push({
35952                 tag: 'iframe',
35953                 cls: 'masonry-brick-image-view',
35954                 src: vurl,
35955                 frameborder : 0,
35956                 allowfullscreen : true
35957             });
35958         }
35959         
35960         return cfg;
35961     },
35962     
35963     initEvents: function() 
35964     {
35965         switch (this.size) {
35966             case 'xs' :
35967                 this.x = 1;
35968                 this.y = 1;
35969                 break;
35970             case 'sm' :
35971                 this.x = 2;
35972                 this.y = 2;
35973                 break;
35974             case 'md' :
35975             case 'md-left' :
35976             case 'md-right' :
35977                 this.x = 3;
35978                 this.y = 3;
35979                 break;
35980             case 'tall' :
35981                 this.x = 2;
35982                 this.y = 3;
35983                 break;
35984             case 'wide' :
35985                 this.x = 3;
35986                 this.y = 2;
35987                 break;
35988             case 'wide-thin' :
35989                 this.x = 3;
35990                 this.y = 1;
35991                 break;
35992                         
35993             default :
35994                 break;
35995         }
35996         
35997         if(Roo.isTouch){
35998             this.el.on('touchstart', this.onTouchStart, this);
35999             this.el.on('touchmove', this.onTouchMove, this);
36000             this.el.on('touchend', this.onTouchEnd, this);
36001             this.el.on('contextmenu', this.onContextMenu, this);
36002         } else {
36003             this.el.on('mouseenter'  ,this.enter, this);
36004             this.el.on('mouseleave', this.leave, this);
36005             this.el.on('click', this.onClick, this);
36006         }
36007         
36008         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36009             this.parent().bricks.push(this);   
36010         }
36011         
36012     },
36013     
36014     onClick: function(e, el)
36015     {
36016         var time = this.endTimer - this.startTimer;
36017         // Roo.log(e.preventDefault());
36018         if(Roo.isTouch){
36019             if(time > 1000){
36020                 e.preventDefault();
36021                 return;
36022             }
36023         }
36024         
36025         if(!this.preventDefault){
36026             return;
36027         }
36028         
36029         e.preventDefault();
36030         
36031         if (this.activeClass != '') {
36032             this.selectBrick();
36033         }
36034         
36035         this.fireEvent('click', this, e);
36036     },
36037     
36038     enter: function(e, el)
36039     {
36040         e.preventDefault();
36041         
36042         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36043             return;
36044         }
36045         
36046         if(this.bgimage.length && this.html.length){
36047             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36048         }
36049     },
36050     
36051     leave: function(e, el)
36052     {
36053         e.preventDefault();
36054         
36055         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36056             return;
36057         }
36058         
36059         if(this.bgimage.length && this.html.length){
36060             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36061         }
36062     },
36063     
36064     onTouchStart: function(e, el)
36065     {
36066 //        e.preventDefault();
36067         
36068         this.touchmoved = false;
36069         
36070         if(!this.isFitContainer){
36071             return;
36072         }
36073         
36074         if(!this.bgimage.length || !this.html.length){
36075             return;
36076         }
36077         
36078         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36079         
36080         this.timer = new Date().getTime();
36081         
36082     },
36083     
36084     onTouchMove: function(e, el)
36085     {
36086         this.touchmoved = true;
36087     },
36088     
36089     onContextMenu : function(e,el)
36090     {
36091         e.preventDefault();
36092         e.stopPropagation();
36093         return false;
36094     },
36095     
36096     onTouchEnd: function(e, el)
36097     {
36098 //        e.preventDefault();
36099         
36100         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36101         
36102             this.leave(e,el);
36103             
36104             return;
36105         }
36106         
36107         if(!this.bgimage.length || !this.html.length){
36108             
36109             if(this.href.length){
36110                 window.location.href = this.href;
36111             }
36112             
36113             return;
36114         }
36115         
36116         if(!this.isFitContainer){
36117             return;
36118         }
36119         
36120         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36121         
36122         window.location.href = this.href;
36123     },
36124     
36125     //selection on single brick only
36126     selectBrick : function() {
36127         
36128         if (!this.parentId) {
36129             return;
36130         }
36131         
36132         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36133         var index = m.selectedBrick.indexOf(this.id);
36134         
36135         if ( index > -1) {
36136             m.selectedBrick.splice(index,1);
36137             this.el.removeClass(this.activeClass);
36138             return;
36139         }
36140         
36141         for(var i = 0; i < m.selectedBrick.length; i++) {
36142             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36143             b.el.removeClass(b.activeClass);
36144         }
36145         
36146         m.selectedBrick = [];
36147         
36148         m.selectedBrick.push(this.id);
36149         this.el.addClass(this.activeClass);
36150         return;
36151     },
36152     
36153     isSelected : function(){
36154         return this.el.hasClass(this.activeClass);
36155         
36156     }
36157 });
36158
36159 Roo.apply(Roo.bootstrap.MasonryBrick, {
36160     
36161     //groups: {},
36162     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36163      /**
36164     * register a Masonry Brick
36165     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36166     */
36167     
36168     register : function(brick)
36169     {
36170         //this.groups[brick.id] = brick;
36171         this.groups.add(brick.id, brick);
36172     },
36173     /**
36174     * fetch a  masonry brick based on the masonry brick ID
36175     * @param {string} the masonry brick to add
36176     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36177     */
36178     
36179     get: function(brick_id) 
36180     {
36181         // if (typeof(this.groups[brick_id]) == 'undefined') {
36182         //     return false;
36183         // }
36184         // return this.groups[brick_id] ;
36185         
36186         if(this.groups.key(brick_id)) {
36187             return this.groups.key(brick_id);
36188         }
36189         
36190         return false;
36191     }
36192     
36193     
36194     
36195 });
36196
36197  /*
36198  * - LGPL
36199  *
36200  * element
36201  * 
36202  */
36203
36204 /**
36205  * @class Roo.bootstrap.Brick
36206  * @extends Roo.bootstrap.Component
36207  * Bootstrap Brick class
36208  * 
36209  * @constructor
36210  * Create a new Brick
36211  * @param {Object} config The config object
36212  */
36213
36214 Roo.bootstrap.Brick = function(config){
36215     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36216     
36217     this.addEvents({
36218         // raw events
36219         /**
36220          * @event click
36221          * When a Brick is click
36222          * @param {Roo.bootstrap.Brick} this
36223          * @param {Roo.EventObject} e
36224          */
36225         "click" : true
36226     });
36227 };
36228
36229 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36230     
36231     /**
36232      * @cfg {String} title
36233      */   
36234     title : '',
36235     /**
36236      * @cfg {String} html
36237      */   
36238     html : '',
36239     /**
36240      * @cfg {String} bgimage
36241      */   
36242     bgimage : '',
36243     /**
36244      * @cfg {String} cls
36245      */   
36246     cls : '',
36247     /**
36248      * @cfg {String} href
36249      */   
36250     href : '',
36251     /**
36252      * @cfg {String} video
36253      */   
36254     video : '',
36255     /**
36256      * @cfg {Boolean} square
36257      */   
36258     square : true,
36259     
36260     getAutoCreate : function()
36261     {
36262         var cls = 'roo-brick';
36263         
36264         if(this.href.length){
36265             cls += ' roo-brick-link';
36266         }
36267         
36268         if(this.bgimage.length){
36269             cls += ' roo-brick-image';
36270         }
36271         
36272         if(!this.html.length && !this.bgimage.length){
36273             cls += ' roo-brick-center-title';
36274         }
36275         
36276         if(!this.html.length && this.bgimage.length){
36277             cls += ' roo-brick-bottom-title';
36278         }
36279         
36280         if(this.cls){
36281             cls += ' ' + this.cls;
36282         }
36283         
36284         var cfg = {
36285             tag: (this.href.length) ? 'a' : 'div',
36286             cls: cls,
36287             cn: [
36288                 {
36289                     tag: 'div',
36290                     cls: 'roo-brick-paragraph',
36291                     cn: []
36292                 }
36293             ]
36294         };
36295         
36296         if(this.href.length){
36297             cfg.href = this.href;
36298         }
36299         
36300         var cn = cfg.cn[0].cn;
36301         
36302         if(this.title.length){
36303             cn.push({
36304                 tag: 'h4',
36305                 cls: 'roo-brick-title',
36306                 html: this.title
36307             });
36308         }
36309         
36310         if(this.html.length){
36311             cn.push({
36312                 tag: 'p',
36313                 cls: 'roo-brick-text',
36314                 html: this.html
36315             });
36316         } else {
36317             cn.cls += ' hide';
36318         }
36319         
36320         if(this.bgimage.length){
36321             cfg.cn.push({
36322                 tag: 'img',
36323                 cls: 'roo-brick-image-view',
36324                 src: this.bgimage
36325             });
36326         }
36327         
36328         return cfg;
36329     },
36330     
36331     initEvents: function() 
36332     {
36333         if(this.title.length || this.html.length){
36334             this.el.on('mouseenter'  ,this.enter, this);
36335             this.el.on('mouseleave', this.leave, this);
36336         }
36337         
36338         Roo.EventManager.onWindowResize(this.resize, this); 
36339         
36340         if(this.bgimage.length){
36341             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36342             this.imageEl.on('load', this.onImageLoad, this);
36343             return;
36344         }
36345         
36346         this.resize();
36347     },
36348     
36349     onImageLoad : function()
36350     {
36351         this.resize();
36352     },
36353     
36354     resize : function()
36355     {
36356         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36357         
36358         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36359         
36360         if(this.bgimage.length){
36361             var image = this.el.select('.roo-brick-image-view', true).first();
36362             
36363             image.setWidth(paragraph.getWidth());
36364             
36365             if(this.square){
36366                 image.setHeight(paragraph.getWidth());
36367             }
36368             
36369             this.el.setHeight(image.getHeight());
36370             paragraph.setHeight(image.getHeight());
36371             
36372         }
36373         
36374     },
36375     
36376     enter: function(e, el)
36377     {
36378         e.preventDefault();
36379         
36380         if(this.bgimage.length){
36381             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36382             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36383         }
36384     },
36385     
36386     leave: function(e, el)
36387     {
36388         e.preventDefault();
36389         
36390         if(this.bgimage.length){
36391             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36392             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36393         }
36394     }
36395     
36396 });
36397
36398  
36399
36400  /*
36401  * - LGPL
36402  *
36403  * Number field 
36404  */
36405
36406 /**
36407  * @class Roo.bootstrap.NumberField
36408  * @extends Roo.bootstrap.Input
36409  * Bootstrap NumberField class
36410  * 
36411  * 
36412  * 
36413  * 
36414  * @constructor
36415  * Create a new NumberField
36416  * @param {Object} config The config object
36417  */
36418
36419 Roo.bootstrap.NumberField = function(config){
36420     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36421 };
36422
36423 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36424     
36425     /**
36426      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36427      */
36428     allowDecimals : true,
36429     /**
36430      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36431      */
36432     decimalSeparator : ".",
36433     /**
36434      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36435      */
36436     decimalPrecision : 2,
36437     /**
36438      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36439      */
36440     allowNegative : true,
36441     
36442     /**
36443      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36444      */
36445     allowZero: true,
36446     /**
36447      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36448      */
36449     minValue : Number.NEGATIVE_INFINITY,
36450     /**
36451      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36452      */
36453     maxValue : Number.MAX_VALUE,
36454     /**
36455      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36456      */
36457     minText : "The minimum value for this field is {0}",
36458     /**
36459      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36460      */
36461     maxText : "The maximum value for this field is {0}",
36462     /**
36463      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36464      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36465      */
36466     nanText : "{0} is not a valid number",
36467     /**
36468      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36469      */
36470     thousandsDelimiter : false,
36471     /**
36472      * @cfg {String} valueAlign alignment of value
36473      */
36474     valueAlign : "left",
36475
36476     getAutoCreate : function()
36477     {
36478         var hiddenInput = {
36479             tag: 'input',
36480             type: 'hidden',
36481             id: Roo.id(),
36482             cls: 'hidden-number-input'
36483         };
36484         
36485         if (this.name) {
36486             hiddenInput.name = this.name;
36487         }
36488         
36489         this.name = '';
36490         
36491         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36492         
36493         this.name = hiddenInput.name;
36494         
36495         if(cfg.cn.length > 0) {
36496             cfg.cn.push(hiddenInput);
36497         }
36498         
36499         return cfg;
36500     },
36501
36502     // private
36503     initEvents : function()
36504     {   
36505         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36506         
36507         var allowed = "0123456789";
36508         
36509         if(this.allowDecimals){
36510             allowed += this.decimalSeparator;
36511         }
36512         
36513         if(this.allowNegative){
36514             allowed += "-";
36515         }
36516         
36517         if(this.thousandsDelimiter) {
36518             allowed += ",";
36519         }
36520         
36521         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36522         
36523         var keyPress = function(e){
36524             
36525             var k = e.getKey();
36526             
36527             var c = e.getCharCode();
36528             
36529             if(
36530                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36531                     allowed.indexOf(String.fromCharCode(c)) === -1
36532             ){
36533                 e.stopEvent();
36534                 return;
36535             }
36536             
36537             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36538                 return;
36539             }
36540             
36541             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36542                 e.stopEvent();
36543             }
36544         };
36545         
36546         this.el.on("keypress", keyPress, this);
36547     },
36548     
36549     validateValue : function(value)
36550     {
36551         
36552         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36553             return false;
36554         }
36555         
36556         var num = this.parseValue(value);
36557         
36558         if(isNaN(num)){
36559             this.markInvalid(String.format(this.nanText, value));
36560             return false;
36561         }
36562         
36563         if(num < this.minValue){
36564             this.markInvalid(String.format(this.minText, this.minValue));
36565             return false;
36566         }
36567         
36568         if(num > this.maxValue){
36569             this.markInvalid(String.format(this.maxText, this.maxValue));
36570             return false;
36571         }
36572         
36573         return true;
36574     },
36575
36576     getValue : function()
36577     {
36578         var v = this.hiddenEl().getValue();
36579         
36580         return this.fixPrecision(this.parseValue(v));
36581     },
36582
36583     parseValue : function(value)
36584     {
36585         if(this.thousandsDelimiter) {
36586             value += "";
36587             r = new RegExp(",", "g");
36588             value = value.replace(r, "");
36589         }
36590         
36591         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36592         return isNaN(value) ? '' : value;
36593     },
36594
36595     fixPrecision : function(value)
36596     {
36597         if(this.thousandsDelimiter) {
36598             value += "";
36599             r = new RegExp(",", "g");
36600             value = value.replace(r, "");
36601         }
36602         
36603         var nan = isNaN(value);
36604         
36605         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36606             return nan ? '' : value;
36607         }
36608         return parseFloat(value).toFixed(this.decimalPrecision);
36609     },
36610
36611     setValue : function(v)
36612     {
36613         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36614         
36615         this.value = v;
36616         
36617         if(this.rendered){
36618             
36619             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36620             
36621             this.inputEl().dom.value = (v == '') ? '' :
36622                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36623             
36624             if(!this.allowZero && v === '0') {
36625                 this.hiddenEl().dom.value = '';
36626                 this.inputEl().dom.value = '';
36627             }
36628             
36629             this.validate();
36630         }
36631     },
36632
36633     decimalPrecisionFcn : function(v)
36634     {
36635         return Math.floor(v);
36636     },
36637
36638     beforeBlur : function()
36639     {
36640         var v = this.parseValue(this.getRawValue());
36641         
36642         if(v || v === 0 || v === ''){
36643             this.setValue(v);
36644         }
36645     },
36646     
36647     hiddenEl : function()
36648     {
36649         return this.el.select('input.hidden-number-input',true).first();
36650     }
36651     
36652 });
36653
36654  
36655
36656 /*
36657 * Licence: LGPL
36658 */
36659
36660 /**
36661  * @class Roo.bootstrap.DocumentSlider
36662  * @extends Roo.bootstrap.Component
36663  * Bootstrap DocumentSlider class
36664  * 
36665  * @constructor
36666  * Create a new DocumentViewer
36667  * @param {Object} config The config object
36668  */
36669
36670 Roo.bootstrap.DocumentSlider = function(config){
36671     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36672     
36673     this.files = [];
36674     
36675     this.addEvents({
36676         /**
36677          * @event initial
36678          * Fire after initEvent
36679          * @param {Roo.bootstrap.DocumentSlider} this
36680          */
36681         "initial" : true,
36682         /**
36683          * @event update
36684          * Fire after update
36685          * @param {Roo.bootstrap.DocumentSlider} this
36686          */
36687         "update" : true,
36688         /**
36689          * @event click
36690          * Fire after click
36691          * @param {Roo.bootstrap.DocumentSlider} this
36692          */
36693         "click" : true
36694     });
36695 };
36696
36697 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36698     
36699     files : false,
36700     
36701     indicator : 0,
36702     
36703     getAutoCreate : function()
36704     {
36705         var cfg = {
36706             tag : 'div',
36707             cls : 'roo-document-slider',
36708             cn : [
36709                 {
36710                     tag : 'div',
36711                     cls : 'roo-document-slider-header',
36712                     cn : [
36713                         {
36714                             tag : 'div',
36715                             cls : 'roo-document-slider-header-title'
36716                         }
36717                     ]
36718                 },
36719                 {
36720                     tag : 'div',
36721                     cls : 'roo-document-slider-body',
36722                     cn : [
36723                         {
36724                             tag : 'div',
36725                             cls : 'roo-document-slider-prev',
36726                             cn : [
36727                                 {
36728                                     tag : 'i',
36729                                     cls : 'fa fa-chevron-left'
36730                                 }
36731                             ]
36732                         },
36733                         {
36734                             tag : 'div',
36735                             cls : 'roo-document-slider-thumb',
36736                             cn : [
36737                                 {
36738                                     tag : 'img',
36739                                     cls : 'roo-document-slider-image'
36740                                 }
36741                             ]
36742                         },
36743                         {
36744                             tag : 'div',
36745                             cls : 'roo-document-slider-next',
36746                             cn : [
36747                                 {
36748                                     tag : 'i',
36749                                     cls : 'fa fa-chevron-right'
36750                                 }
36751                             ]
36752                         }
36753                     ]
36754                 }
36755             ]
36756         };
36757         
36758         return cfg;
36759     },
36760     
36761     initEvents : function()
36762     {
36763         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36764         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36765         
36766         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36767         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36768         
36769         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36770         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36771         
36772         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36773         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36774         
36775         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36776         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36777         
36778         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36779         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36780         
36781         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36782         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36783         
36784         this.thumbEl.on('click', this.onClick, this);
36785         
36786         this.prevIndicator.on('click', this.prev, this);
36787         
36788         this.nextIndicator.on('click', this.next, this);
36789         
36790     },
36791     
36792     initial : function()
36793     {
36794         if(this.files.length){
36795             this.indicator = 1;
36796             this.update()
36797         }
36798         
36799         this.fireEvent('initial', this);
36800     },
36801     
36802     update : function()
36803     {
36804         this.imageEl.attr('src', this.files[this.indicator - 1]);
36805         
36806         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36807         
36808         this.prevIndicator.show();
36809         
36810         if(this.indicator == 1){
36811             this.prevIndicator.hide();
36812         }
36813         
36814         this.nextIndicator.show();
36815         
36816         if(this.indicator == this.files.length){
36817             this.nextIndicator.hide();
36818         }
36819         
36820         this.thumbEl.scrollTo('top');
36821         
36822         this.fireEvent('update', this);
36823     },
36824     
36825     onClick : function(e)
36826     {
36827         e.preventDefault();
36828         
36829         this.fireEvent('click', this);
36830     },
36831     
36832     prev : function(e)
36833     {
36834         e.preventDefault();
36835         
36836         this.indicator = Math.max(1, this.indicator - 1);
36837         
36838         this.update();
36839     },
36840     
36841     next : function(e)
36842     {
36843         e.preventDefault();
36844         
36845         this.indicator = Math.min(this.files.length, this.indicator + 1);
36846         
36847         this.update();
36848     }
36849 });
36850 /*
36851  * - LGPL
36852  *
36853  * RadioSet
36854  *
36855  *
36856  */
36857
36858 /**
36859  * @class Roo.bootstrap.RadioSet
36860  * @extends Roo.bootstrap.Input
36861  * Bootstrap RadioSet class
36862  * @cfg {String} indicatorpos (left|right) default left
36863  * @cfg {Boolean} inline (true|false) inline the element (default true)
36864  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36865  * @constructor
36866  * Create a new RadioSet
36867  * @param {Object} config The config object
36868  */
36869
36870 Roo.bootstrap.RadioSet = function(config){
36871     
36872     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36873     
36874     this.radioes = [];
36875     
36876     Roo.bootstrap.RadioSet.register(this);
36877     
36878     this.addEvents({
36879         /**
36880         * @event check
36881         * Fires when the element is checked or unchecked.
36882         * @param {Roo.bootstrap.RadioSet} this This radio
36883         * @param {Roo.bootstrap.Radio} item The checked item
36884         */
36885        check : true,
36886        /**
36887         * @event click
36888         * Fires when the element is click.
36889         * @param {Roo.bootstrap.RadioSet} this This radio set
36890         * @param {Roo.bootstrap.Radio} item The checked item
36891         * @param {Roo.EventObject} e The event object
36892         */
36893        click : true
36894     });
36895     
36896 };
36897
36898 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36899
36900     radioes : false,
36901     
36902     inline : true,
36903     
36904     weight : '',
36905     
36906     indicatorpos : 'left',
36907     
36908     getAutoCreate : function()
36909     {
36910         var label = {
36911             tag : 'label',
36912             cls : 'roo-radio-set-label',
36913             cn : [
36914                 {
36915                     tag : 'span',
36916                     html : this.fieldLabel
36917                 }
36918             ]
36919         };
36920         if (Roo.bootstrap.version == 3) {
36921             
36922             
36923             if(this.indicatorpos == 'left'){
36924                 label.cn.unshift({
36925                     tag : 'i',
36926                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36927                     tooltip : 'This field is required'
36928                 });
36929             } else {
36930                 label.cn.push({
36931                     tag : 'i',
36932                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36933                     tooltip : 'This field is required'
36934                 });
36935             }
36936         }
36937         var items = {
36938             tag : 'div',
36939             cls : 'roo-radio-set-items'
36940         };
36941         
36942         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36943         
36944         if (align === 'left' && this.fieldLabel.length) {
36945             
36946             items = {
36947                 cls : "roo-radio-set-right", 
36948                 cn: [
36949                     items
36950                 ]
36951             };
36952             
36953             if(this.labelWidth > 12){
36954                 label.style = "width: " + this.labelWidth + 'px';
36955             }
36956             
36957             if(this.labelWidth < 13 && this.labelmd == 0){
36958                 this.labelmd = this.labelWidth;
36959             }
36960             
36961             if(this.labellg > 0){
36962                 label.cls += ' col-lg-' + this.labellg;
36963                 items.cls += ' col-lg-' + (12 - this.labellg);
36964             }
36965             
36966             if(this.labelmd > 0){
36967                 label.cls += ' col-md-' + this.labelmd;
36968                 items.cls += ' col-md-' + (12 - this.labelmd);
36969             }
36970             
36971             if(this.labelsm > 0){
36972                 label.cls += ' col-sm-' + this.labelsm;
36973                 items.cls += ' col-sm-' + (12 - this.labelsm);
36974             }
36975             
36976             if(this.labelxs > 0){
36977                 label.cls += ' col-xs-' + this.labelxs;
36978                 items.cls += ' col-xs-' + (12 - this.labelxs);
36979             }
36980         }
36981         
36982         var cfg = {
36983             tag : 'div',
36984             cls : 'roo-radio-set',
36985             cn : [
36986                 {
36987                     tag : 'input',
36988                     cls : 'roo-radio-set-input',
36989                     type : 'hidden',
36990                     name : this.name,
36991                     value : this.value ? this.value :  ''
36992                 },
36993                 label,
36994                 items
36995             ]
36996         };
36997         
36998         if(this.weight.length){
36999             cfg.cls += ' roo-radio-' + this.weight;
37000         }
37001         
37002         if(this.inline) {
37003             cfg.cls += ' roo-radio-set-inline';
37004         }
37005         
37006         var settings=this;
37007         ['xs','sm','md','lg'].map(function(size){
37008             if (settings[size]) {
37009                 cfg.cls += ' col-' + size + '-' + settings[size];
37010             }
37011         });
37012         
37013         return cfg;
37014         
37015     },
37016
37017     initEvents : function()
37018     {
37019         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37020         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37021         
37022         if(!this.fieldLabel.length){
37023             this.labelEl.hide();
37024         }
37025         
37026         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37027         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37028         
37029         this.indicator = this.indicatorEl();
37030         
37031         if(this.indicator){
37032             this.indicator.addClass('invisible');
37033         }
37034         
37035         this.originalValue = this.getValue();
37036         
37037     },
37038     
37039     inputEl: function ()
37040     {
37041         return this.el.select('.roo-radio-set-input', true).first();
37042     },
37043     
37044     getChildContainer : function()
37045     {
37046         return this.itemsEl;
37047     },
37048     
37049     register : function(item)
37050     {
37051         this.radioes.push(item);
37052         
37053     },
37054     
37055     validate : function()
37056     {   
37057         if(this.getVisibilityEl().hasClass('hidden')){
37058             return true;
37059         }
37060         
37061         var valid = false;
37062         
37063         Roo.each(this.radioes, function(i){
37064             if(!i.checked){
37065                 return;
37066             }
37067             
37068             valid = true;
37069             return false;
37070         });
37071         
37072         if(this.allowBlank) {
37073             return true;
37074         }
37075         
37076         if(this.disabled || valid){
37077             this.markValid();
37078             return true;
37079         }
37080         
37081         this.markInvalid();
37082         return false;
37083         
37084     },
37085     
37086     markValid : function()
37087     {
37088         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37089             this.indicatorEl().removeClass('visible');
37090             this.indicatorEl().addClass('invisible');
37091         }
37092         
37093         
37094         if (Roo.bootstrap.version == 3) {
37095             this.el.removeClass([this.invalidClass, this.validClass]);
37096             this.el.addClass(this.validClass);
37097         } else {
37098             this.el.removeClass(['is-invalid','is-valid']);
37099             this.el.addClass(['is-valid']);
37100         }
37101         this.fireEvent('valid', this);
37102     },
37103     
37104     markInvalid : function(msg)
37105     {
37106         if(this.allowBlank || this.disabled){
37107             return;
37108         }
37109         
37110         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37111             this.indicatorEl().removeClass('invisible');
37112             this.indicatorEl().addClass('visible');
37113         }
37114         if (Roo.bootstrap.version == 3) {
37115             this.el.removeClass([this.invalidClass, this.validClass]);
37116             this.el.addClass(this.invalidClass);
37117         } else {
37118             this.el.removeClass(['is-invalid','is-valid']);
37119             this.el.addClass(['is-invalid']);
37120         }
37121         
37122         this.fireEvent('invalid', this, msg);
37123         
37124     },
37125     
37126     setValue : function(v, suppressEvent)
37127     {   
37128         if(this.value === v){
37129             return;
37130         }
37131         
37132         this.value = v;
37133         
37134         if(this.rendered){
37135             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37136         }
37137         
37138         Roo.each(this.radioes, function(i){
37139             i.checked = false;
37140             i.el.removeClass('checked');
37141         });
37142         
37143         Roo.each(this.radioes, function(i){
37144             
37145             if(i.value === v || i.value.toString() === v.toString()){
37146                 i.checked = true;
37147                 i.el.addClass('checked');
37148                 
37149                 if(suppressEvent !== true){
37150                     this.fireEvent('check', this, i);
37151                 }
37152                 
37153                 return false;
37154             }
37155             
37156         }, this);
37157         
37158         this.validate();
37159     },
37160     
37161     clearInvalid : function(){
37162         
37163         if(!this.el || this.preventMark){
37164             return;
37165         }
37166         
37167         this.el.removeClass([this.invalidClass]);
37168         
37169         this.fireEvent('valid', this);
37170     }
37171     
37172 });
37173
37174 Roo.apply(Roo.bootstrap.RadioSet, {
37175     
37176     groups: {},
37177     
37178     register : function(set)
37179     {
37180         this.groups[set.name] = set;
37181     },
37182     
37183     get: function(name) 
37184     {
37185         if (typeof(this.groups[name]) == 'undefined') {
37186             return false;
37187         }
37188         
37189         return this.groups[name] ;
37190     }
37191     
37192 });
37193 /*
37194  * Based on:
37195  * Ext JS Library 1.1.1
37196  * Copyright(c) 2006-2007, Ext JS, LLC.
37197  *
37198  * Originally Released Under LGPL - original licence link has changed is not relivant.
37199  *
37200  * Fork - LGPL
37201  * <script type="text/javascript">
37202  */
37203
37204
37205 /**
37206  * @class Roo.bootstrap.SplitBar
37207  * @extends Roo.util.Observable
37208  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37209  * <br><br>
37210  * Usage:
37211  * <pre><code>
37212 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37213                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37214 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37215 split.minSize = 100;
37216 split.maxSize = 600;
37217 split.animate = true;
37218 split.on('moved', splitterMoved);
37219 </code></pre>
37220  * @constructor
37221  * Create a new SplitBar
37222  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37223  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37224  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37225  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37226                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37227                         position of the SplitBar).
37228  */
37229 Roo.bootstrap.SplitBar = function(cfg){
37230     
37231     /** @private */
37232     
37233     //{
37234     //  dragElement : elm
37235     //  resizingElement: el,
37236         // optional..
37237     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37238     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37239         // existingProxy ???
37240     //}
37241     
37242     this.el = Roo.get(cfg.dragElement, true);
37243     this.el.dom.unselectable = "on";
37244     /** @private */
37245     this.resizingEl = Roo.get(cfg.resizingElement, true);
37246
37247     /**
37248      * @private
37249      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37250      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37251      * @type Number
37252      */
37253     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37254     
37255     /**
37256      * The minimum size of the resizing element. (Defaults to 0)
37257      * @type Number
37258      */
37259     this.minSize = 0;
37260     
37261     /**
37262      * The maximum size of the resizing element. (Defaults to 2000)
37263      * @type Number
37264      */
37265     this.maxSize = 2000;
37266     
37267     /**
37268      * Whether to animate the transition to the new size
37269      * @type Boolean
37270      */
37271     this.animate = false;
37272     
37273     /**
37274      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37275      * @type Boolean
37276      */
37277     this.useShim = false;
37278     
37279     /** @private */
37280     this.shim = null;
37281     
37282     if(!cfg.existingProxy){
37283         /** @private */
37284         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37285     }else{
37286         this.proxy = Roo.get(cfg.existingProxy).dom;
37287     }
37288     /** @private */
37289     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37290     
37291     /** @private */
37292     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37293     
37294     /** @private */
37295     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37296     
37297     /** @private */
37298     this.dragSpecs = {};
37299     
37300     /**
37301      * @private The adapter to use to positon and resize elements
37302      */
37303     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37304     this.adapter.init(this);
37305     
37306     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37307         /** @private */
37308         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37309         this.el.addClass("roo-splitbar-h");
37310     }else{
37311         /** @private */
37312         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37313         this.el.addClass("roo-splitbar-v");
37314     }
37315     
37316     this.addEvents({
37317         /**
37318          * @event resize
37319          * Fires when the splitter is moved (alias for {@link #event-moved})
37320          * @param {Roo.bootstrap.SplitBar} this
37321          * @param {Number} newSize the new width or height
37322          */
37323         "resize" : true,
37324         /**
37325          * @event moved
37326          * Fires when the splitter is moved
37327          * @param {Roo.bootstrap.SplitBar} this
37328          * @param {Number} newSize the new width or height
37329          */
37330         "moved" : true,
37331         /**
37332          * @event beforeresize
37333          * Fires before the splitter is dragged
37334          * @param {Roo.bootstrap.SplitBar} this
37335          */
37336         "beforeresize" : true,
37337
37338         "beforeapply" : true
37339     });
37340
37341     Roo.util.Observable.call(this);
37342 };
37343
37344 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37345     onStartProxyDrag : function(x, y){
37346         this.fireEvent("beforeresize", this);
37347         if(!this.overlay){
37348             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37349             o.unselectable();
37350             o.enableDisplayMode("block");
37351             // all splitbars share the same overlay
37352             Roo.bootstrap.SplitBar.prototype.overlay = o;
37353         }
37354         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37355         this.overlay.show();
37356         Roo.get(this.proxy).setDisplayed("block");
37357         var size = this.adapter.getElementSize(this);
37358         this.activeMinSize = this.getMinimumSize();;
37359         this.activeMaxSize = this.getMaximumSize();;
37360         var c1 = size - this.activeMinSize;
37361         var c2 = Math.max(this.activeMaxSize - size, 0);
37362         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37363             this.dd.resetConstraints();
37364             this.dd.setXConstraint(
37365                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37366                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37367             );
37368             this.dd.setYConstraint(0, 0);
37369         }else{
37370             this.dd.resetConstraints();
37371             this.dd.setXConstraint(0, 0);
37372             this.dd.setYConstraint(
37373                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37374                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37375             );
37376          }
37377         this.dragSpecs.startSize = size;
37378         this.dragSpecs.startPoint = [x, y];
37379         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37380     },
37381     
37382     /** 
37383      * @private Called after the drag operation by the DDProxy
37384      */
37385     onEndProxyDrag : function(e){
37386         Roo.get(this.proxy).setDisplayed(false);
37387         var endPoint = Roo.lib.Event.getXY(e);
37388         if(this.overlay){
37389             this.overlay.hide();
37390         }
37391         var newSize;
37392         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37393             newSize = this.dragSpecs.startSize + 
37394                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37395                     endPoint[0] - this.dragSpecs.startPoint[0] :
37396                     this.dragSpecs.startPoint[0] - endPoint[0]
37397                 );
37398         }else{
37399             newSize = this.dragSpecs.startSize + 
37400                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37401                     endPoint[1] - this.dragSpecs.startPoint[1] :
37402                     this.dragSpecs.startPoint[1] - endPoint[1]
37403                 );
37404         }
37405         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37406         if(newSize != this.dragSpecs.startSize){
37407             if(this.fireEvent('beforeapply', this, newSize) !== false){
37408                 this.adapter.setElementSize(this, newSize);
37409                 this.fireEvent("moved", this, newSize);
37410                 this.fireEvent("resize", this, newSize);
37411             }
37412         }
37413     },
37414     
37415     /**
37416      * Get the adapter this SplitBar uses
37417      * @return The adapter object
37418      */
37419     getAdapter : function(){
37420         return this.adapter;
37421     },
37422     
37423     /**
37424      * Set the adapter this SplitBar uses
37425      * @param {Object} adapter A SplitBar adapter object
37426      */
37427     setAdapter : function(adapter){
37428         this.adapter = adapter;
37429         this.adapter.init(this);
37430     },
37431     
37432     /**
37433      * Gets the minimum size for the resizing element
37434      * @return {Number} The minimum size
37435      */
37436     getMinimumSize : function(){
37437         return this.minSize;
37438     },
37439     
37440     /**
37441      * Sets the minimum size for the resizing element
37442      * @param {Number} minSize The minimum size
37443      */
37444     setMinimumSize : function(minSize){
37445         this.minSize = minSize;
37446     },
37447     
37448     /**
37449      * Gets the maximum size for the resizing element
37450      * @return {Number} The maximum size
37451      */
37452     getMaximumSize : function(){
37453         return this.maxSize;
37454     },
37455     
37456     /**
37457      * Sets the maximum size for the resizing element
37458      * @param {Number} maxSize The maximum size
37459      */
37460     setMaximumSize : function(maxSize){
37461         this.maxSize = maxSize;
37462     },
37463     
37464     /**
37465      * Sets the initialize size for the resizing element
37466      * @param {Number} size The initial size
37467      */
37468     setCurrentSize : function(size){
37469         var oldAnimate = this.animate;
37470         this.animate = false;
37471         this.adapter.setElementSize(this, size);
37472         this.animate = oldAnimate;
37473     },
37474     
37475     /**
37476      * Destroy this splitbar. 
37477      * @param {Boolean} removeEl True to remove the element
37478      */
37479     destroy : function(removeEl){
37480         if(this.shim){
37481             this.shim.remove();
37482         }
37483         this.dd.unreg();
37484         this.proxy.parentNode.removeChild(this.proxy);
37485         if(removeEl){
37486             this.el.remove();
37487         }
37488     }
37489 });
37490
37491 /**
37492  * @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.
37493  */
37494 Roo.bootstrap.SplitBar.createProxy = function(dir){
37495     var proxy = new Roo.Element(document.createElement("div"));
37496     proxy.unselectable();
37497     var cls = 'roo-splitbar-proxy';
37498     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37499     document.body.appendChild(proxy.dom);
37500     return proxy.dom;
37501 };
37502
37503 /** 
37504  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37505  * Default Adapter. It assumes the splitter and resizing element are not positioned
37506  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37507  */
37508 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37509 };
37510
37511 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37512     // do nothing for now
37513     init : function(s){
37514     
37515     },
37516     /**
37517      * Called before drag operations to get the current size of the resizing element. 
37518      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37519      */
37520      getElementSize : function(s){
37521         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37522             return s.resizingEl.getWidth();
37523         }else{
37524             return s.resizingEl.getHeight();
37525         }
37526     },
37527     
37528     /**
37529      * Called after drag operations to set the size of the resizing element.
37530      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37531      * @param {Number} newSize The new size to set
37532      * @param {Function} onComplete A function to be invoked when resizing is complete
37533      */
37534     setElementSize : function(s, newSize, onComplete){
37535         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37536             if(!s.animate){
37537                 s.resizingEl.setWidth(newSize);
37538                 if(onComplete){
37539                     onComplete(s, newSize);
37540                 }
37541             }else{
37542                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37543             }
37544         }else{
37545             
37546             if(!s.animate){
37547                 s.resizingEl.setHeight(newSize);
37548                 if(onComplete){
37549                     onComplete(s, newSize);
37550                 }
37551             }else{
37552                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37553             }
37554         }
37555     }
37556 };
37557
37558 /** 
37559  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37560  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37561  * Adapter that  moves the splitter element to align with the resized sizing element. 
37562  * Used with an absolute positioned SplitBar.
37563  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37564  * document.body, make sure you assign an id to the body element.
37565  */
37566 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37567     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37568     this.container = Roo.get(container);
37569 };
37570
37571 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37572     init : function(s){
37573         this.basic.init(s);
37574     },
37575     
37576     getElementSize : function(s){
37577         return this.basic.getElementSize(s);
37578     },
37579     
37580     setElementSize : function(s, newSize, onComplete){
37581         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37582     },
37583     
37584     moveSplitter : function(s){
37585         var yes = Roo.bootstrap.SplitBar;
37586         switch(s.placement){
37587             case yes.LEFT:
37588                 s.el.setX(s.resizingEl.getRight());
37589                 break;
37590             case yes.RIGHT:
37591                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37592                 break;
37593             case yes.TOP:
37594                 s.el.setY(s.resizingEl.getBottom());
37595                 break;
37596             case yes.BOTTOM:
37597                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37598                 break;
37599         }
37600     }
37601 };
37602
37603 /**
37604  * Orientation constant - Create a vertical SplitBar
37605  * @static
37606  * @type Number
37607  */
37608 Roo.bootstrap.SplitBar.VERTICAL = 1;
37609
37610 /**
37611  * Orientation constant - Create a horizontal SplitBar
37612  * @static
37613  * @type Number
37614  */
37615 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37616
37617 /**
37618  * Placement constant - The resizing element is to the left of the splitter element
37619  * @static
37620  * @type Number
37621  */
37622 Roo.bootstrap.SplitBar.LEFT = 1;
37623
37624 /**
37625  * Placement constant - The resizing element is to the right of the splitter element
37626  * @static
37627  * @type Number
37628  */
37629 Roo.bootstrap.SplitBar.RIGHT = 2;
37630
37631 /**
37632  * Placement constant - The resizing element is positioned above the splitter element
37633  * @static
37634  * @type Number
37635  */
37636 Roo.bootstrap.SplitBar.TOP = 3;
37637
37638 /**
37639  * Placement constant - The resizing element is positioned under splitter element
37640  * @static
37641  * @type Number
37642  */
37643 Roo.bootstrap.SplitBar.BOTTOM = 4;
37644 Roo.namespace("Roo.bootstrap.layout");/*
37645  * Based on:
37646  * Ext JS Library 1.1.1
37647  * Copyright(c) 2006-2007, Ext JS, LLC.
37648  *
37649  * Originally Released Under LGPL - original licence link has changed is not relivant.
37650  *
37651  * Fork - LGPL
37652  * <script type="text/javascript">
37653  */
37654
37655 /**
37656  * @class Roo.bootstrap.layout.Manager
37657  * @extends Roo.bootstrap.Component
37658  * Base class for layout managers.
37659  */
37660 Roo.bootstrap.layout.Manager = function(config)
37661 {
37662     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37663
37664
37665
37666
37667
37668     /** false to disable window resize monitoring @type Boolean */
37669     this.monitorWindowResize = true;
37670     this.regions = {};
37671     this.addEvents({
37672         /**
37673          * @event layout
37674          * Fires when a layout is performed.
37675          * @param {Roo.LayoutManager} this
37676          */
37677         "layout" : true,
37678         /**
37679          * @event regionresized
37680          * Fires when the user resizes a region.
37681          * @param {Roo.LayoutRegion} region The resized region
37682          * @param {Number} newSize The new size (width for east/west, height for north/south)
37683          */
37684         "regionresized" : true,
37685         /**
37686          * @event regioncollapsed
37687          * Fires when a region is collapsed.
37688          * @param {Roo.LayoutRegion} region The collapsed region
37689          */
37690         "regioncollapsed" : true,
37691         /**
37692          * @event regionexpanded
37693          * Fires when a region is expanded.
37694          * @param {Roo.LayoutRegion} region The expanded region
37695          */
37696         "regionexpanded" : true
37697     });
37698     this.updating = false;
37699
37700     if (config.el) {
37701         this.el = Roo.get(config.el);
37702         this.initEvents();
37703     }
37704
37705 };
37706
37707 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37708
37709
37710     regions : null,
37711
37712     monitorWindowResize : true,
37713
37714
37715     updating : false,
37716
37717
37718     onRender : function(ct, position)
37719     {
37720         if(!this.el){
37721             this.el = Roo.get(ct);
37722             this.initEvents();
37723         }
37724         //this.fireEvent('render',this);
37725     },
37726
37727
37728     initEvents: function()
37729     {
37730
37731
37732         // ie scrollbar fix
37733         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37734             document.body.scroll = "no";
37735         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37736             this.el.position('relative');
37737         }
37738         this.id = this.el.id;
37739         this.el.addClass("roo-layout-container");
37740         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37741         if(this.el.dom != document.body ) {
37742             this.el.on('resize', this.layout,this);
37743             this.el.on('show', this.layout,this);
37744         }
37745
37746     },
37747
37748     /**
37749      * Returns true if this layout is currently being updated
37750      * @return {Boolean}
37751      */
37752     isUpdating : function(){
37753         return this.updating;
37754     },
37755
37756     /**
37757      * Suspend the LayoutManager from doing auto-layouts while
37758      * making multiple add or remove calls
37759      */
37760     beginUpdate : function(){
37761         this.updating = true;
37762     },
37763
37764     /**
37765      * Restore auto-layouts and optionally disable the manager from performing a layout
37766      * @param {Boolean} noLayout true to disable a layout update
37767      */
37768     endUpdate : function(noLayout){
37769         this.updating = false;
37770         if(!noLayout){
37771             this.layout();
37772         }
37773     },
37774
37775     layout: function(){
37776         // abstract...
37777     },
37778
37779     onRegionResized : function(region, newSize){
37780         this.fireEvent("regionresized", region, newSize);
37781         this.layout();
37782     },
37783
37784     onRegionCollapsed : function(region){
37785         this.fireEvent("regioncollapsed", region);
37786     },
37787
37788     onRegionExpanded : function(region){
37789         this.fireEvent("regionexpanded", region);
37790     },
37791
37792     /**
37793      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37794      * performs box-model adjustments.
37795      * @return {Object} The size as an object {width: (the width), height: (the height)}
37796      */
37797     getViewSize : function()
37798     {
37799         var size;
37800         if(this.el.dom != document.body){
37801             size = this.el.getSize();
37802         }else{
37803             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37804         }
37805         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37806         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37807         return size;
37808     },
37809
37810     /**
37811      * Returns the Element this layout is bound to.
37812      * @return {Roo.Element}
37813      */
37814     getEl : function(){
37815         return this.el;
37816     },
37817
37818     /**
37819      * Returns the specified region.
37820      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37821      * @return {Roo.LayoutRegion}
37822      */
37823     getRegion : function(target){
37824         return this.regions[target.toLowerCase()];
37825     },
37826
37827     onWindowResize : function(){
37828         if(this.monitorWindowResize){
37829             this.layout();
37830         }
37831     }
37832 });
37833 /*
37834  * Based on:
37835  * Ext JS Library 1.1.1
37836  * Copyright(c) 2006-2007, Ext JS, LLC.
37837  *
37838  * Originally Released Under LGPL - original licence link has changed is not relivant.
37839  *
37840  * Fork - LGPL
37841  * <script type="text/javascript">
37842  */
37843 /**
37844  * @class Roo.bootstrap.layout.Border
37845  * @extends Roo.bootstrap.layout.Manager
37846  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37847  * please see: examples/bootstrap/nested.html<br><br>
37848  
37849 <b>The container the layout is rendered into can be either the body element or any other element.
37850 If it is not the body element, the container needs to either be an absolute positioned element,
37851 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37852 the container size if it is not the body element.</b>
37853
37854 * @constructor
37855 * Create a new Border
37856 * @param {Object} config Configuration options
37857  */
37858 Roo.bootstrap.layout.Border = function(config){
37859     config = config || {};
37860     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37861     
37862     
37863     
37864     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37865         if(config[region]){
37866             config[region].region = region;
37867             this.addRegion(config[region]);
37868         }
37869     },this);
37870     
37871 };
37872
37873 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37874
37875 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37876     
37877     parent : false, // this might point to a 'nest' or a ???
37878     
37879     /**
37880      * Creates and adds a new region if it doesn't already exist.
37881      * @param {String} target The target region key (north, south, east, west or center).
37882      * @param {Object} config The regions config object
37883      * @return {BorderLayoutRegion} The new region
37884      */
37885     addRegion : function(config)
37886     {
37887         if(!this.regions[config.region]){
37888             var r = this.factory(config);
37889             this.bindRegion(r);
37890         }
37891         return this.regions[config.region];
37892     },
37893
37894     // private (kinda)
37895     bindRegion : function(r){
37896         this.regions[r.config.region] = r;
37897         
37898         r.on("visibilitychange",    this.layout, this);
37899         r.on("paneladded",          this.layout, this);
37900         r.on("panelremoved",        this.layout, this);
37901         r.on("invalidated",         this.layout, this);
37902         r.on("resized",             this.onRegionResized, this);
37903         r.on("collapsed",           this.onRegionCollapsed, this);
37904         r.on("expanded",            this.onRegionExpanded, this);
37905     },
37906
37907     /**
37908      * Performs a layout update.
37909      */
37910     layout : function()
37911     {
37912         if(this.updating) {
37913             return;
37914         }
37915         
37916         // render all the rebions if they have not been done alreayd?
37917         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37918             if(this.regions[region] && !this.regions[region].bodyEl){
37919                 this.regions[region].onRender(this.el)
37920             }
37921         },this);
37922         
37923         var size = this.getViewSize();
37924         var w = size.width;
37925         var h = size.height;
37926         var centerW = w;
37927         var centerH = h;
37928         var centerY = 0;
37929         var centerX = 0;
37930         //var x = 0, y = 0;
37931
37932         var rs = this.regions;
37933         var north = rs["north"];
37934         var south = rs["south"]; 
37935         var west = rs["west"];
37936         var east = rs["east"];
37937         var center = rs["center"];
37938         //if(this.hideOnLayout){ // not supported anymore
37939             //c.el.setStyle("display", "none");
37940         //}
37941         if(north && north.isVisible()){
37942             var b = north.getBox();
37943             var m = north.getMargins();
37944             b.width = w - (m.left+m.right);
37945             b.x = m.left;
37946             b.y = m.top;
37947             centerY = b.height + b.y + m.bottom;
37948             centerH -= centerY;
37949             north.updateBox(this.safeBox(b));
37950         }
37951         if(south && south.isVisible()){
37952             var b = south.getBox();
37953             var m = south.getMargins();
37954             b.width = w - (m.left+m.right);
37955             b.x = m.left;
37956             var totalHeight = (b.height + m.top + m.bottom);
37957             b.y = h - totalHeight + m.top;
37958             centerH -= totalHeight;
37959             south.updateBox(this.safeBox(b));
37960         }
37961         if(west && west.isVisible()){
37962             var b = west.getBox();
37963             var m = west.getMargins();
37964             b.height = centerH - (m.top+m.bottom);
37965             b.x = m.left;
37966             b.y = centerY + m.top;
37967             var totalWidth = (b.width + m.left + m.right);
37968             centerX += totalWidth;
37969             centerW -= totalWidth;
37970             west.updateBox(this.safeBox(b));
37971         }
37972         if(east && east.isVisible()){
37973             var b = east.getBox();
37974             var m = east.getMargins();
37975             b.height = centerH - (m.top+m.bottom);
37976             var totalWidth = (b.width + m.left + m.right);
37977             b.x = w - totalWidth + m.left;
37978             b.y = centerY + m.top;
37979             centerW -= totalWidth;
37980             east.updateBox(this.safeBox(b));
37981         }
37982         if(center){
37983             var m = center.getMargins();
37984             var centerBox = {
37985                 x: centerX + m.left,
37986                 y: centerY + m.top,
37987                 width: centerW - (m.left+m.right),
37988                 height: centerH - (m.top+m.bottom)
37989             };
37990             //if(this.hideOnLayout){
37991                 //center.el.setStyle("display", "block");
37992             //}
37993             center.updateBox(this.safeBox(centerBox));
37994         }
37995         this.el.repaint();
37996         this.fireEvent("layout", this);
37997     },
37998
37999     // private
38000     safeBox : function(box){
38001         box.width = Math.max(0, box.width);
38002         box.height = Math.max(0, box.height);
38003         return box;
38004     },
38005
38006     /**
38007      * Adds a ContentPanel (or subclass) to this layout.
38008      * @param {String} target The target region key (north, south, east, west or center).
38009      * @param {Roo.ContentPanel} panel The panel to add
38010      * @return {Roo.ContentPanel} The added panel
38011      */
38012     add : function(target, panel){
38013          
38014         target = target.toLowerCase();
38015         return this.regions[target].add(panel);
38016     },
38017
38018     /**
38019      * Remove a ContentPanel (or subclass) to this layout.
38020      * @param {String} target The target region key (north, south, east, west or center).
38021      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38022      * @return {Roo.ContentPanel} The removed panel
38023      */
38024     remove : function(target, panel){
38025         target = target.toLowerCase();
38026         return this.regions[target].remove(panel);
38027     },
38028
38029     /**
38030      * Searches all regions for a panel with the specified id
38031      * @param {String} panelId
38032      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38033      */
38034     findPanel : function(panelId){
38035         var rs = this.regions;
38036         for(var target in rs){
38037             if(typeof rs[target] != "function"){
38038                 var p = rs[target].getPanel(panelId);
38039                 if(p){
38040                     return p;
38041                 }
38042             }
38043         }
38044         return null;
38045     },
38046
38047     /**
38048      * Searches all regions for a panel with the specified id and activates (shows) it.
38049      * @param {String/ContentPanel} panelId The panels id or the panel itself
38050      * @return {Roo.ContentPanel} The shown panel or null
38051      */
38052     showPanel : function(panelId) {
38053       var rs = this.regions;
38054       for(var target in rs){
38055          var r = rs[target];
38056          if(typeof r != "function"){
38057             if(r.hasPanel(panelId)){
38058                return r.showPanel(panelId);
38059             }
38060          }
38061       }
38062       return null;
38063    },
38064
38065    /**
38066      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38067      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38068      */
38069    /*
38070     restoreState : function(provider){
38071         if(!provider){
38072             provider = Roo.state.Manager;
38073         }
38074         var sm = new Roo.LayoutStateManager();
38075         sm.init(this, provider);
38076     },
38077 */
38078  
38079  
38080     /**
38081      * Adds a xtype elements to the layout.
38082      * <pre><code>
38083
38084 layout.addxtype({
38085        xtype : 'ContentPanel',
38086        region: 'west',
38087        items: [ .... ]
38088    }
38089 );
38090
38091 layout.addxtype({
38092         xtype : 'NestedLayoutPanel',
38093         region: 'west',
38094         layout: {
38095            center: { },
38096            west: { }   
38097         },
38098         items : [ ... list of content panels or nested layout panels.. ]
38099    }
38100 );
38101 </code></pre>
38102      * @param {Object} cfg Xtype definition of item to add.
38103      */
38104     addxtype : function(cfg)
38105     {
38106         // basically accepts a pannel...
38107         // can accept a layout region..!?!?
38108         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38109         
38110         
38111         // theory?  children can only be panels??
38112         
38113         //if (!cfg.xtype.match(/Panel$/)) {
38114         //    return false;
38115         //}
38116         var ret = false;
38117         
38118         if (typeof(cfg.region) == 'undefined') {
38119             Roo.log("Failed to add Panel, region was not set");
38120             Roo.log(cfg);
38121             return false;
38122         }
38123         var region = cfg.region;
38124         delete cfg.region;
38125         
38126           
38127         var xitems = [];
38128         if (cfg.items) {
38129             xitems = cfg.items;
38130             delete cfg.items;
38131         }
38132         var nb = false;
38133         
38134         if ( region == 'center') {
38135             Roo.log("Center: " + cfg.title);
38136         }
38137         
38138         
38139         switch(cfg.xtype) 
38140         {
38141             case 'Content':  // ContentPanel (el, cfg)
38142             case 'Scroll':  // ContentPanel (el, cfg)
38143             case 'View': 
38144                 cfg.autoCreate = cfg.autoCreate || true;
38145                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38146                 //} else {
38147                 //    var el = this.el.createChild();
38148                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38149                 //}
38150                 
38151                 this.add(region, ret);
38152                 break;
38153             
38154             /*
38155             case 'TreePanel': // our new panel!
38156                 cfg.el = this.el.createChild();
38157                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38158                 this.add(region, ret);
38159                 break;
38160             */
38161             
38162             case 'Nest': 
38163                 // create a new Layout (which is  a Border Layout...
38164                 
38165                 var clayout = cfg.layout;
38166                 clayout.el  = this.el.createChild();
38167                 clayout.items   = clayout.items  || [];
38168                 
38169                 delete cfg.layout;
38170                 
38171                 // replace this exitems with the clayout ones..
38172                 xitems = clayout.items;
38173                  
38174                 // force background off if it's in center...
38175                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38176                     cfg.background = false;
38177                 }
38178                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38179                 
38180                 
38181                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38182                 //console.log('adding nested layout panel '  + cfg.toSource());
38183                 this.add(region, ret);
38184                 nb = {}; /// find first...
38185                 break;
38186             
38187             case 'Grid':
38188                 
38189                 // needs grid and region
38190                 
38191                 //var el = this.getRegion(region).el.createChild();
38192                 /*
38193                  *var el = this.el.createChild();
38194                 // create the grid first...
38195                 cfg.grid.container = el;
38196                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38197                 */
38198                 
38199                 if (region == 'center' && this.active ) {
38200                     cfg.background = false;
38201                 }
38202                 
38203                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38204                 
38205                 this.add(region, ret);
38206                 /*
38207                 if (cfg.background) {
38208                     // render grid on panel activation (if panel background)
38209                     ret.on('activate', function(gp) {
38210                         if (!gp.grid.rendered) {
38211                     //        gp.grid.render(el);
38212                         }
38213                     });
38214                 } else {
38215                   //  cfg.grid.render(el);
38216                 }
38217                 */
38218                 break;
38219            
38220            
38221             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38222                 // it was the old xcomponent building that caused this before.
38223                 // espeically if border is the top element in the tree.
38224                 ret = this;
38225                 break; 
38226                 
38227                     
38228                 
38229                 
38230                 
38231             default:
38232                 /*
38233                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38234                     
38235                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38236                     this.add(region, ret);
38237                 } else {
38238                 */
38239                     Roo.log(cfg);
38240                     throw "Can not add '" + cfg.xtype + "' to Border";
38241                     return null;
38242              
38243                                 
38244              
38245         }
38246         this.beginUpdate();
38247         // add children..
38248         var region = '';
38249         var abn = {};
38250         Roo.each(xitems, function(i)  {
38251             region = nb && i.region ? i.region : false;
38252             
38253             var add = ret.addxtype(i);
38254            
38255             if (region) {
38256                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38257                 if (!i.background) {
38258                     abn[region] = nb[region] ;
38259                 }
38260             }
38261             
38262         });
38263         this.endUpdate();
38264
38265         // make the last non-background panel active..
38266         //if (nb) { Roo.log(abn); }
38267         if (nb) {
38268             
38269             for(var r in abn) {
38270                 region = this.getRegion(r);
38271                 if (region) {
38272                     // tried using nb[r], but it does not work..
38273                      
38274                     region.showPanel(abn[r]);
38275                    
38276                 }
38277             }
38278         }
38279         return ret;
38280         
38281     },
38282     
38283     
38284 // private
38285     factory : function(cfg)
38286     {
38287         
38288         var validRegions = Roo.bootstrap.layout.Border.regions;
38289
38290         var target = cfg.region;
38291         cfg.mgr = this;
38292         
38293         var r = Roo.bootstrap.layout;
38294         Roo.log(target);
38295         switch(target){
38296             case "north":
38297                 return new r.North(cfg);
38298             case "south":
38299                 return new r.South(cfg);
38300             case "east":
38301                 return new r.East(cfg);
38302             case "west":
38303                 return new r.West(cfg);
38304             case "center":
38305                 return new r.Center(cfg);
38306         }
38307         throw 'Layout region "'+target+'" not supported.';
38308     }
38309     
38310     
38311 });
38312  /*
38313  * Based on:
38314  * Ext JS Library 1.1.1
38315  * Copyright(c) 2006-2007, Ext JS, LLC.
38316  *
38317  * Originally Released Under LGPL - original licence link has changed is not relivant.
38318  *
38319  * Fork - LGPL
38320  * <script type="text/javascript">
38321  */
38322  
38323 /**
38324  * @class Roo.bootstrap.layout.Basic
38325  * @extends Roo.util.Observable
38326  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38327  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38328  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38329  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38330  * @cfg {string}   region  the region that it inhabits..
38331  * @cfg {bool}   skipConfig skip config?
38332  * 
38333
38334  */
38335 Roo.bootstrap.layout.Basic = function(config){
38336     
38337     this.mgr = config.mgr;
38338     
38339     this.position = config.region;
38340     
38341     var skipConfig = config.skipConfig;
38342     
38343     this.events = {
38344         /**
38345          * @scope Roo.BasicLayoutRegion
38346          */
38347         
38348         /**
38349          * @event beforeremove
38350          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38351          * @param {Roo.LayoutRegion} this
38352          * @param {Roo.ContentPanel} panel The panel
38353          * @param {Object} e The cancel event object
38354          */
38355         "beforeremove" : true,
38356         /**
38357          * @event invalidated
38358          * Fires when the layout for this region is changed.
38359          * @param {Roo.LayoutRegion} this
38360          */
38361         "invalidated" : true,
38362         /**
38363          * @event visibilitychange
38364          * Fires when this region is shown or hidden 
38365          * @param {Roo.LayoutRegion} this
38366          * @param {Boolean} visibility true or false
38367          */
38368         "visibilitychange" : true,
38369         /**
38370          * @event paneladded
38371          * Fires when a panel is added. 
38372          * @param {Roo.LayoutRegion} this
38373          * @param {Roo.ContentPanel} panel The panel
38374          */
38375         "paneladded" : true,
38376         /**
38377          * @event panelremoved
38378          * Fires when a panel is removed. 
38379          * @param {Roo.LayoutRegion} this
38380          * @param {Roo.ContentPanel} panel The panel
38381          */
38382         "panelremoved" : true,
38383         /**
38384          * @event beforecollapse
38385          * Fires when this region before collapse.
38386          * @param {Roo.LayoutRegion} this
38387          */
38388         "beforecollapse" : true,
38389         /**
38390          * @event collapsed
38391          * Fires when this region is collapsed.
38392          * @param {Roo.LayoutRegion} this
38393          */
38394         "collapsed" : true,
38395         /**
38396          * @event expanded
38397          * Fires when this region is expanded.
38398          * @param {Roo.LayoutRegion} this
38399          */
38400         "expanded" : true,
38401         /**
38402          * @event slideshow
38403          * Fires when this region is slid into view.
38404          * @param {Roo.LayoutRegion} this
38405          */
38406         "slideshow" : true,
38407         /**
38408          * @event slidehide
38409          * Fires when this region slides out of view. 
38410          * @param {Roo.LayoutRegion} this
38411          */
38412         "slidehide" : true,
38413         /**
38414          * @event panelactivated
38415          * Fires when a panel is activated. 
38416          * @param {Roo.LayoutRegion} this
38417          * @param {Roo.ContentPanel} panel The activated panel
38418          */
38419         "panelactivated" : true,
38420         /**
38421          * @event resized
38422          * Fires when the user resizes this region. 
38423          * @param {Roo.LayoutRegion} this
38424          * @param {Number} newSize The new size (width for east/west, height for north/south)
38425          */
38426         "resized" : true
38427     };
38428     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38429     this.panels = new Roo.util.MixedCollection();
38430     this.panels.getKey = this.getPanelId.createDelegate(this);
38431     this.box = null;
38432     this.activePanel = null;
38433     // ensure listeners are added...
38434     
38435     if (config.listeners || config.events) {
38436         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38437             listeners : config.listeners || {},
38438             events : config.events || {}
38439         });
38440     }
38441     
38442     if(skipConfig !== true){
38443         this.applyConfig(config);
38444     }
38445 };
38446
38447 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38448 {
38449     getPanelId : function(p){
38450         return p.getId();
38451     },
38452     
38453     applyConfig : function(config){
38454         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38455         this.config = config;
38456         
38457     },
38458     
38459     /**
38460      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38461      * the width, for horizontal (north, south) the height.
38462      * @param {Number} newSize The new width or height
38463      */
38464     resizeTo : function(newSize){
38465         var el = this.el ? this.el :
38466                  (this.activePanel ? this.activePanel.getEl() : null);
38467         if(el){
38468             switch(this.position){
38469                 case "east":
38470                 case "west":
38471                     el.setWidth(newSize);
38472                     this.fireEvent("resized", this, newSize);
38473                 break;
38474                 case "north":
38475                 case "south":
38476                     el.setHeight(newSize);
38477                     this.fireEvent("resized", this, newSize);
38478                 break;                
38479             }
38480         }
38481     },
38482     
38483     getBox : function(){
38484         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38485     },
38486     
38487     getMargins : function(){
38488         return this.margins;
38489     },
38490     
38491     updateBox : function(box){
38492         this.box = box;
38493         var el = this.activePanel.getEl();
38494         el.dom.style.left = box.x + "px";
38495         el.dom.style.top = box.y + "px";
38496         this.activePanel.setSize(box.width, box.height);
38497     },
38498     
38499     /**
38500      * Returns the container element for this region.
38501      * @return {Roo.Element}
38502      */
38503     getEl : function(){
38504         return this.activePanel;
38505     },
38506     
38507     /**
38508      * Returns true if this region is currently visible.
38509      * @return {Boolean}
38510      */
38511     isVisible : function(){
38512         return this.activePanel ? true : false;
38513     },
38514     
38515     setActivePanel : function(panel){
38516         panel = this.getPanel(panel);
38517         if(this.activePanel && this.activePanel != panel){
38518             this.activePanel.setActiveState(false);
38519             this.activePanel.getEl().setLeftTop(-10000,-10000);
38520         }
38521         this.activePanel = panel;
38522         panel.setActiveState(true);
38523         if(this.box){
38524             panel.setSize(this.box.width, this.box.height);
38525         }
38526         this.fireEvent("panelactivated", this, panel);
38527         this.fireEvent("invalidated");
38528     },
38529     
38530     /**
38531      * Show the specified panel.
38532      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38533      * @return {Roo.ContentPanel} The shown panel or null
38534      */
38535     showPanel : function(panel){
38536         panel = this.getPanel(panel);
38537         if(panel){
38538             this.setActivePanel(panel);
38539         }
38540         return panel;
38541     },
38542     
38543     /**
38544      * Get the active panel for this region.
38545      * @return {Roo.ContentPanel} The active panel or null
38546      */
38547     getActivePanel : function(){
38548         return this.activePanel;
38549     },
38550     
38551     /**
38552      * Add the passed ContentPanel(s)
38553      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38554      * @return {Roo.ContentPanel} The panel added (if only one was added)
38555      */
38556     add : function(panel){
38557         if(arguments.length > 1){
38558             for(var i = 0, len = arguments.length; i < len; i++) {
38559                 this.add(arguments[i]);
38560             }
38561             return null;
38562         }
38563         if(this.hasPanel(panel)){
38564             this.showPanel(panel);
38565             return panel;
38566         }
38567         var el = panel.getEl();
38568         if(el.dom.parentNode != this.mgr.el.dom){
38569             this.mgr.el.dom.appendChild(el.dom);
38570         }
38571         if(panel.setRegion){
38572             panel.setRegion(this);
38573         }
38574         this.panels.add(panel);
38575         el.setStyle("position", "absolute");
38576         if(!panel.background){
38577             this.setActivePanel(panel);
38578             if(this.config.initialSize && this.panels.getCount()==1){
38579                 this.resizeTo(this.config.initialSize);
38580             }
38581         }
38582         this.fireEvent("paneladded", this, panel);
38583         return panel;
38584     },
38585     
38586     /**
38587      * Returns true if the panel is in this region.
38588      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38589      * @return {Boolean}
38590      */
38591     hasPanel : function(panel){
38592         if(typeof panel == "object"){ // must be panel obj
38593             panel = panel.getId();
38594         }
38595         return this.getPanel(panel) ? true : false;
38596     },
38597     
38598     /**
38599      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38600      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38601      * @param {Boolean} preservePanel Overrides the config preservePanel option
38602      * @return {Roo.ContentPanel} The panel that was removed
38603      */
38604     remove : function(panel, preservePanel){
38605         panel = this.getPanel(panel);
38606         if(!panel){
38607             return null;
38608         }
38609         var e = {};
38610         this.fireEvent("beforeremove", this, panel, e);
38611         if(e.cancel === true){
38612             return null;
38613         }
38614         var panelId = panel.getId();
38615         this.panels.removeKey(panelId);
38616         return panel;
38617     },
38618     
38619     /**
38620      * Returns the panel specified or null if it's not in this region.
38621      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38622      * @return {Roo.ContentPanel}
38623      */
38624     getPanel : function(id){
38625         if(typeof id == "object"){ // must be panel obj
38626             return id;
38627         }
38628         return this.panels.get(id);
38629     },
38630     
38631     /**
38632      * Returns this regions position (north/south/east/west/center).
38633      * @return {String} 
38634      */
38635     getPosition: function(){
38636         return this.position;    
38637     }
38638 });/*
38639  * Based on:
38640  * Ext JS Library 1.1.1
38641  * Copyright(c) 2006-2007, Ext JS, LLC.
38642  *
38643  * Originally Released Under LGPL - original licence link has changed is not relivant.
38644  *
38645  * Fork - LGPL
38646  * <script type="text/javascript">
38647  */
38648  
38649 /**
38650  * @class Roo.bootstrap.layout.Region
38651  * @extends Roo.bootstrap.layout.Basic
38652  * This class represents a region in a layout manager.
38653  
38654  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38655  * @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})
38656  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38657  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38658  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38659  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38660  * @cfg {String}    title           The title for the region (overrides panel titles)
38661  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38662  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38663  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38664  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38665  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38666  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38667  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38668  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38669  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38670  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38671
38672  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38673  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38674  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38675  * @cfg {Number}    width           For East/West panels
38676  * @cfg {Number}    height          For North/South panels
38677  * @cfg {Boolean}   split           To show the splitter
38678  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38679  * 
38680  * @cfg {string}   cls             Extra CSS classes to add to region
38681  * 
38682  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38683  * @cfg {string}   region  the region that it inhabits..
38684  *
38685
38686  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38687  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38688
38689  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38690  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38691  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38692  */
38693 Roo.bootstrap.layout.Region = function(config)
38694 {
38695     this.applyConfig(config);
38696
38697     var mgr = config.mgr;
38698     var pos = config.region;
38699     config.skipConfig = true;
38700     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38701     
38702     if (mgr.el) {
38703         this.onRender(mgr.el);   
38704     }
38705      
38706     this.visible = true;
38707     this.collapsed = false;
38708     this.unrendered_panels = [];
38709 };
38710
38711 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38712
38713     position: '', // set by wrapper (eg. north/south etc..)
38714     unrendered_panels : null,  // unrendered panels.
38715     
38716     tabPosition : false,
38717     
38718     mgr: false, // points to 'Border'
38719     
38720     
38721     createBody : function(){
38722         /** This region's body element 
38723         * @type Roo.Element */
38724         this.bodyEl = this.el.createChild({
38725                 tag: "div",
38726                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38727         });
38728     },
38729
38730     onRender: function(ctr, pos)
38731     {
38732         var dh = Roo.DomHelper;
38733         /** This region's container element 
38734         * @type Roo.Element */
38735         this.el = dh.append(ctr.dom, {
38736                 tag: "div",
38737                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38738             }, true);
38739         /** This region's title element 
38740         * @type Roo.Element */
38741     
38742         this.titleEl = dh.append(this.el.dom,  {
38743                 tag: "div",
38744                 unselectable: "on",
38745                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38746                 children:[
38747                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38748                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38749                 ]
38750             }, true);
38751         
38752         this.titleEl.enableDisplayMode();
38753         /** This region's title text element 
38754         * @type HTMLElement */
38755         this.titleTextEl = this.titleEl.dom.firstChild;
38756         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38757         /*
38758         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38759         this.closeBtn.enableDisplayMode();
38760         this.closeBtn.on("click", this.closeClicked, this);
38761         this.closeBtn.hide();
38762     */
38763         this.createBody(this.config);
38764         if(this.config.hideWhenEmpty){
38765             this.hide();
38766             this.on("paneladded", this.validateVisibility, this);
38767             this.on("panelremoved", this.validateVisibility, this);
38768         }
38769         if(this.autoScroll){
38770             this.bodyEl.setStyle("overflow", "auto");
38771         }else{
38772             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38773         }
38774         //if(c.titlebar !== false){
38775             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38776                 this.titleEl.hide();
38777             }else{
38778                 this.titleEl.show();
38779                 if(this.config.title){
38780                     this.titleTextEl.innerHTML = this.config.title;
38781                 }
38782             }
38783         //}
38784         if(this.config.collapsed){
38785             this.collapse(true);
38786         }
38787         if(this.config.hidden){
38788             this.hide();
38789         }
38790         
38791         if (this.unrendered_panels && this.unrendered_panels.length) {
38792             for (var i =0;i< this.unrendered_panels.length; i++) {
38793                 this.add(this.unrendered_panels[i]);
38794             }
38795             this.unrendered_panels = null;
38796             
38797         }
38798         
38799     },
38800     
38801     applyConfig : function(c)
38802     {
38803         /*
38804          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38805             var dh = Roo.DomHelper;
38806             if(c.titlebar !== false){
38807                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38808                 this.collapseBtn.on("click", this.collapse, this);
38809                 this.collapseBtn.enableDisplayMode();
38810                 /*
38811                 if(c.showPin === true || this.showPin){
38812                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38813                     this.stickBtn.enableDisplayMode();
38814                     this.stickBtn.on("click", this.expand, this);
38815                     this.stickBtn.hide();
38816                 }
38817                 
38818             }
38819             */
38820             /** This region's collapsed element
38821             * @type Roo.Element */
38822             /*
38823              *
38824             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38825                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38826             ]}, true);
38827             
38828             if(c.floatable !== false){
38829                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38830                this.collapsedEl.on("click", this.collapseClick, this);
38831             }
38832
38833             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38834                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38835                    id: "message", unselectable: "on", style:{"float":"left"}});
38836                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38837              }
38838             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38839             this.expandBtn.on("click", this.expand, this);
38840             
38841         }
38842         
38843         if(this.collapseBtn){
38844             this.collapseBtn.setVisible(c.collapsible == true);
38845         }
38846         
38847         this.cmargins = c.cmargins || this.cmargins ||
38848                          (this.position == "west" || this.position == "east" ?
38849                              {top: 0, left: 2, right:2, bottom: 0} :
38850                              {top: 2, left: 0, right:0, bottom: 2});
38851         */
38852         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38853         
38854         
38855         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38856         
38857         this.autoScroll = c.autoScroll || false;
38858         
38859         
38860        
38861         
38862         this.duration = c.duration || .30;
38863         this.slideDuration = c.slideDuration || .45;
38864         this.config = c;
38865        
38866     },
38867     /**
38868      * Returns true if this region is currently visible.
38869      * @return {Boolean}
38870      */
38871     isVisible : function(){
38872         return this.visible;
38873     },
38874
38875     /**
38876      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38877      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38878      */
38879     //setCollapsedTitle : function(title){
38880     //    title = title || "&#160;";
38881      //   if(this.collapsedTitleTextEl){
38882       //      this.collapsedTitleTextEl.innerHTML = title;
38883        // }
38884     //},
38885
38886     getBox : function(){
38887         var b;
38888       //  if(!this.collapsed){
38889             b = this.el.getBox(false, true);
38890        // }else{
38891           //  b = this.collapsedEl.getBox(false, true);
38892         //}
38893         return b;
38894     },
38895
38896     getMargins : function(){
38897         return this.margins;
38898         //return this.collapsed ? this.cmargins : this.margins;
38899     },
38900 /*
38901     highlight : function(){
38902         this.el.addClass("x-layout-panel-dragover");
38903     },
38904
38905     unhighlight : function(){
38906         this.el.removeClass("x-layout-panel-dragover");
38907     },
38908 */
38909     updateBox : function(box)
38910     {
38911         if (!this.bodyEl) {
38912             return; // not rendered yet..
38913         }
38914         
38915         this.box = box;
38916         if(!this.collapsed){
38917             this.el.dom.style.left = box.x + "px";
38918             this.el.dom.style.top = box.y + "px";
38919             this.updateBody(box.width, box.height);
38920         }else{
38921             this.collapsedEl.dom.style.left = box.x + "px";
38922             this.collapsedEl.dom.style.top = box.y + "px";
38923             this.collapsedEl.setSize(box.width, box.height);
38924         }
38925         if(this.tabs){
38926             this.tabs.autoSizeTabs();
38927         }
38928     },
38929
38930     updateBody : function(w, h)
38931     {
38932         if(w !== null){
38933             this.el.setWidth(w);
38934             w -= this.el.getBorderWidth("rl");
38935             if(this.config.adjustments){
38936                 w += this.config.adjustments[0];
38937             }
38938         }
38939         if(h !== null && h > 0){
38940             this.el.setHeight(h);
38941             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38942             h -= this.el.getBorderWidth("tb");
38943             if(this.config.adjustments){
38944                 h += this.config.adjustments[1];
38945             }
38946             this.bodyEl.setHeight(h);
38947             if(this.tabs){
38948                 h = this.tabs.syncHeight(h);
38949             }
38950         }
38951         if(this.panelSize){
38952             w = w !== null ? w : this.panelSize.width;
38953             h = h !== null ? h : this.panelSize.height;
38954         }
38955         if(this.activePanel){
38956             var el = this.activePanel.getEl();
38957             w = w !== null ? w : el.getWidth();
38958             h = h !== null ? h : el.getHeight();
38959             this.panelSize = {width: w, height: h};
38960             this.activePanel.setSize(w, h);
38961         }
38962         if(Roo.isIE && this.tabs){
38963             this.tabs.el.repaint();
38964         }
38965     },
38966
38967     /**
38968      * Returns the container element for this region.
38969      * @return {Roo.Element}
38970      */
38971     getEl : function(){
38972         return this.el;
38973     },
38974
38975     /**
38976      * Hides this region.
38977      */
38978     hide : function(){
38979         //if(!this.collapsed){
38980             this.el.dom.style.left = "-2000px";
38981             this.el.hide();
38982         //}else{
38983          //   this.collapsedEl.dom.style.left = "-2000px";
38984          //   this.collapsedEl.hide();
38985        // }
38986         this.visible = false;
38987         this.fireEvent("visibilitychange", this, false);
38988     },
38989
38990     /**
38991      * Shows this region if it was previously hidden.
38992      */
38993     show : function(){
38994         //if(!this.collapsed){
38995             this.el.show();
38996         //}else{
38997         //    this.collapsedEl.show();
38998        // }
38999         this.visible = true;
39000         this.fireEvent("visibilitychange", this, true);
39001     },
39002 /*
39003     closeClicked : function(){
39004         if(this.activePanel){
39005             this.remove(this.activePanel);
39006         }
39007     },
39008
39009     collapseClick : function(e){
39010         if(this.isSlid){
39011            e.stopPropagation();
39012            this.slideIn();
39013         }else{
39014            e.stopPropagation();
39015            this.slideOut();
39016         }
39017     },
39018 */
39019     /**
39020      * Collapses this region.
39021      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39022      */
39023     /*
39024     collapse : function(skipAnim, skipCheck = false){
39025         if(this.collapsed) {
39026             return;
39027         }
39028         
39029         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39030             
39031             this.collapsed = true;
39032             if(this.split){
39033                 this.split.el.hide();
39034             }
39035             if(this.config.animate && skipAnim !== true){
39036                 this.fireEvent("invalidated", this);
39037                 this.animateCollapse();
39038             }else{
39039                 this.el.setLocation(-20000,-20000);
39040                 this.el.hide();
39041                 this.collapsedEl.show();
39042                 this.fireEvent("collapsed", this);
39043                 this.fireEvent("invalidated", this);
39044             }
39045         }
39046         
39047     },
39048 */
39049     animateCollapse : function(){
39050         // overridden
39051     },
39052
39053     /**
39054      * Expands this region if it was previously collapsed.
39055      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39056      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39057      */
39058     /*
39059     expand : function(e, skipAnim){
39060         if(e) {
39061             e.stopPropagation();
39062         }
39063         if(!this.collapsed || this.el.hasActiveFx()) {
39064             return;
39065         }
39066         if(this.isSlid){
39067             this.afterSlideIn();
39068             skipAnim = true;
39069         }
39070         this.collapsed = false;
39071         if(this.config.animate && skipAnim !== true){
39072             this.animateExpand();
39073         }else{
39074             this.el.show();
39075             if(this.split){
39076                 this.split.el.show();
39077             }
39078             this.collapsedEl.setLocation(-2000,-2000);
39079             this.collapsedEl.hide();
39080             this.fireEvent("invalidated", this);
39081             this.fireEvent("expanded", this);
39082         }
39083     },
39084 */
39085     animateExpand : function(){
39086         // overridden
39087     },
39088
39089     initTabs : function()
39090     {
39091         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39092         
39093         var ts = new Roo.bootstrap.panel.Tabs({
39094             el: this.bodyEl.dom,
39095             region : this,
39096             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39097             disableTooltips: this.config.disableTabTips,
39098             toolbar : this.config.toolbar
39099         });
39100         
39101         if(this.config.hideTabs){
39102             ts.stripWrap.setDisplayed(false);
39103         }
39104         this.tabs = ts;
39105         ts.resizeTabs = this.config.resizeTabs === true;
39106         ts.minTabWidth = this.config.minTabWidth || 40;
39107         ts.maxTabWidth = this.config.maxTabWidth || 250;
39108         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39109         ts.monitorResize = false;
39110         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39111         ts.bodyEl.addClass('roo-layout-tabs-body');
39112         this.panels.each(this.initPanelAsTab, this);
39113     },
39114
39115     initPanelAsTab : function(panel){
39116         var ti = this.tabs.addTab(
39117             panel.getEl().id,
39118             panel.getTitle(),
39119             null,
39120             this.config.closeOnTab && panel.isClosable(),
39121             panel.tpl
39122         );
39123         if(panel.tabTip !== undefined){
39124             ti.setTooltip(panel.tabTip);
39125         }
39126         ti.on("activate", function(){
39127               this.setActivePanel(panel);
39128         }, this);
39129         
39130         if(this.config.closeOnTab){
39131             ti.on("beforeclose", function(t, e){
39132                 e.cancel = true;
39133                 this.remove(panel);
39134             }, this);
39135         }
39136         
39137         panel.tabItem = ti;
39138         
39139         return ti;
39140     },
39141
39142     updatePanelTitle : function(panel, title)
39143     {
39144         if(this.activePanel == panel){
39145             this.updateTitle(title);
39146         }
39147         if(this.tabs){
39148             var ti = this.tabs.getTab(panel.getEl().id);
39149             ti.setText(title);
39150             if(panel.tabTip !== undefined){
39151                 ti.setTooltip(panel.tabTip);
39152             }
39153         }
39154     },
39155
39156     updateTitle : function(title){
39157         if(this.titleTextEl && !this.config.title){
39158             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39159         }
39160     },
39161
39162     setActivePanel : function(panel)
39163     {
39164         panel = this.getPanel(panel);
39165         if(this.activePanel && this.activePanel != panel){
39166             if(this.activePanel.setActiveState(false) === false){
39167                 return;
39168             }
39169         }
39170         this.activePanel = panel;
39171         panel.setActiveState(true);
39172         if(this.panelSize){
39173             panel.setSize(this.panelSize.width, this.panelSize.height);
39174         }
39175         if(this.closeBtn){
39176             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39177         }
39178         this.updateTitle(panel.getTitle());
39179         if(this.tabs){
39180             this.fireEvent("invalidated", this);
39181         }
39182         this.fireEvent("panelactivated", this, panel);
39183     },
39184
39185     /**
39186      * Shows the specified panel.
39187      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39188      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39189      */
39190     showPanel : function(panel)
39191     {
39192         panel = this.getPanel(panel);
39193         if(panel){
39194             if(this.tabs){
39195                 var tab = this.tabs.getTab(panel.getEl().id);
39196                 if(tab.isHidden()){
39197                     this.tabs.unhideTab(tab.id);
39198                 }
39199                 tab.activate();
39200             }else{
39201                 this.setActivePanel(panel);
39202             }
39203         }
39204         return panel;
39205     },
39206
39207     /**
39208      * Get the active panel for this region.
39209      * @return {Roo.ContentPanel} The active panel or null
39210      */
39211     getActivePanel : function(){
39212         return this.activePanel;
39213     },
39214
39215     validateVisibility : function(){
39216         if(this.panels.getCount() < 1){
39217             this.updateTitle("&#160;");
39218             this.closeBtn.hide();
39219             this.hide();
39220         }else{
39221             if(!this.isVisible()){
39222                 this.show();
39223             }
39224         }
39225     },
39226
39227     /**
39228      * Adds the passed ContentPanel(s) to this region.
39229      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39230      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39231      */
39232     add : function(panel)
39233     {
39234         if(arguments.length > 1){
39235             for(var i = 0, len = arguments.length; i < len; i++) {
39236                 this.add(arguments[i]);
39237             }
39238             return null;
39239         }
39240         
39241         // if we have not been rendered yet, then we can not really do much of this..
39242         if (!this.bodyEl) {
39243             this.unrendered_panels.push(panel);
39244             return panel;
39245         }
39246         
39247         
39248         
39249         
39250         if(this.hasPanel(panel)){
39251             this.showPanel(panel);
39252             return panel;
39253         }
39254         panel.setRegion(this);
39255         this.panels.add(panel);
39256        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39257             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39258             // and hide them... ???
39259             this.bodyEl.dom.appendChild(panel.getEl().dom);
39260             if(panel.background !== true){
39261                 this.setActivePanel(panel);
39262             }
39263             this.fireEvent("paneladded", this, panel);
39264             return panel;
39265         }
39266         */
39267         if(!this.tabs){
39268             this.initTabs();
39269         }else{
39270             this.initPanelAsTab(panel);
39271         }
39272         
39273         
39274         if(panel.background !== true){
39275             this.tabs.activate(panel.getEl().id);
39276         }
39277         this.fireEvent("paneladded", this, panel);
39278         return panel;
39279     },
39280
39281     /**
39282      * Hides the tab for the specified panel.
39283      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39284      */
39285     hidePanel : function(panel){
39286         if(this.tabs && (panel = this.getPanel(panel))){
39287             this.tabs.hideTab(panel.getEl().id);
39288         }
39289     },
39290
39291     /**
39292      * Unhides the tab for a previously hidden panel.
39293      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39294      */
39295     unhidePanel : function(panel){
39296         if(this.tabs && (panel = this.getPanel(panel))){
39297             this.tabs.unhideTab(panel.getEl().id);
39298         }
39299     },
39300
39301     clearPanels : function(){
39302         while(this.panels.getCount() > 0){
39303              this.remove(this.panels.first());
39304         }
39305     },
39306
39307     /**
39308      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39309      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39310      * @param {Boolean} preservePanel Overrides the config preservePanel option
39311      * @return {Roo.ContentPanel} The panel that was removed
39312      */
39313     remove : function(panel, preservePanel)
39314     {
39315         panel = this.getPanel(panel);
39316         if(!panel){
39317             return null;
39318         }
39319         var e = {};
39320         this.fireEvent("beforeremove", this, panel, e);
39321         if(e.cancel === true){
39322             return null;
39323         }
39324         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39325         var panelId = panel.getId();
39326         this.panels.removeKey(panelId);
39327         if(preservePanel){
39328             document.body.appendChild(panel.getEl().dom);
39329         }
39330         if(this.tabs){
39331             this.tabs.removeTab(panel.getEl().id);
39332         }else if (!preservePanel){
39333             this.bodyEl.dom.removeChild(panel.getEl().dom);
39334         }
39335         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39336             var p = this.panels.first();
39337             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39338             tempEl.appendChild(p.getEl().dom);
39339             this.bodyEl.update("");
39340             this.bodyEl.dom.appendChild(p.getEl().dom);
39341             tempEl = null;
39342             this.updateTitle(p.getTitle());
39343             this.tabs = null;
39344             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39345             this.setActivePanel(p);
39346         }
39347         panel.setRegion(null);
39348         if(this.activePanel == panel){
39349             this.activePanel = null;
39350         }
39351         if(this.config.autoDestroy !== false && preservePanel !== true){
39352             try{panel.destroy();}catch(e){}
39353         }
39354         this.fireEvent("panelremoved", this, panel);
39355         return panel;
39356     },
39357
39358     /**
39359      * Returns the TabPanel component used by this region
39360      * @return {Roo.TabPanel}
39361      */
39362     getTabs : function(){
39363         return this.tabs;
39364     },
39365
39366     createTool : function(parentEl, className){
39367         var btn = Roo.DomHelper.append(parentEl, {
39368             tag: "div",
39369             cls: "x-layout-tools-button",
39370             children: [ {
39371                 tag: "div",
39372                 cls: "roo-layout-tools-button-inner " + className,
39373                 html: "&#160;"
39374             }]
39375         }, true);
39376         btn.addClassOnOver("roo-layout-tools-button-over");
39377         return btn;
39378     }
39379 });/*
39380  * Based on:
39381  * Ext JS Library 1.1.1
39382  * Copyright(c) 2006-2007, Ext JS, LLC.
39383  *
39384  * Originally Released Under LGPL - original licence link has changed is not relivant.
39385  *
39386  * Fork - LGPL
39387  * <script type="text/javascript">
39388  */
39389  
39390
39391
39392 /**
39393  * @class Roo.SplitLayoutRegion
39394  * @extends Roo.LayoutRegion
39395  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39396  */
39397 Roo.bootstrap.layout.Split = function(config){
39398     this.cursor = config.cursor;
39399     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39400 };
39401
39402 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39403 {
39404     splitTip : "Drag to resize.",
39405     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39406     useSplitTips : false,
39407
39408     applyConfig : function(config){
39409         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39410     },
39411     
39412     onRender : function(ctr,pos) {
39413         
39414         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39415         if(!this.config.split){
39416             return;
39417         }
39418         if(!this.split){
39419             
39420             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39421                             tag: "div",
39422                             id: this.el.id + "-split",
39423                             cls: "roo-layout-split roo-layout-split-"+this.position,
39424                             html: "&#160;"
39425             });
39426             /** The SplitBar for this region 
39427             * @type Roo.SplitBar */
39428             // does not exist yet...
39429             Roo.log([this.position, this.orientation]);
39430             
39431             this.split = new Roo.bootstrap.SplitBar({
39432                 dragElement : splitEl,
39433                 resizingElement: this.el,
39434                 orientation : this.orientation
39435             });
39436             
39437             this.split.on("moved", this.onSplitMove, this);
39438             this.split.useShim = this.config.useShim === true;
39439             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39440             if(this.useSplitTips){
39441                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39442             }
39443             //if(config.collapsible){
39444             //    this.split.el.on("dblclick", this.collapse,  this);
39445             //}
39446         }
39447         if(typeof this.config.minSize != "undefined"){
39448             this.split.minSize = this.config.minSize;
39449         }
39450         if(typeof this.config.maxSize != "undefined"){
39451             this.split.maxSize = this.config.maxSize;
39452         }
39453         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39454             this.hideSplitter();
39455         }
39456         
39457     },
39458
39459     getHMaxSize : function(){
39460          var cmax = this.config.maxSize || 10000;
39461          var center = this.mgr.getRegion("center");
39462          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39463     },
39464
39465     getVMaxSize : function(){
39466          var cmax = this.config.maxSize || 10000;
39467          var center = this.mgr.getRegion("center");
39468          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39469     },
39470
39471     onSplitMove : function(split, newSize){
39472         this.fireEvent("resized", this, newSize);
39473     },
39474     
39475     /** 
39476      * Returns the {@link Roo.SplitBar} for this region.
39477      * @return {Roo.SplitBar}
39478      */
39479     getSplitBar : function(){
39480         return this.split;
39481     },
39482     
39483     hide : function(){
39484         this.hideSplitter();
39485         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39486     },
39487
39488     hideSplitter : function(){
39489         if(this.split){
39490             this.split.el.setLocation(-2000,-2000);
39491             this.split.el.hide();
39492         }
39493     },
39494
39495     show : function(){
39496         if(this.split){
39497             this.split.el.show();
39498         }
39499         Roo.bootstrap.layout.Split.superclass.show.call(this);
39500     },
39501     
39502     beforeSlide: function(){
39503         if(Roo.isGecko){// firefox overflow auto bug workaround
39504             this.bodyEl.clip();
39505             if(this.tabs) {
39506                 this.tabs.bodyEl.clip();
39507             }
39508             if(this.activePanel){
39509                 this.activePanel.getEl().clip();
39510                 
39511                 if(this.activePanel.beforeSlide){
39512                     this.activePanel.beforeSlide();
39513                 }
39514             }
39515         }
39516     },
39517     
39518     afterSlide : function(){
39519         if(Roo.isGecko){// firefox overflow auto bug workaround
39520             this.bodyEl.unclip();
39521             if(this.tabs) {
39522                 this.tabs.bodyEl.unclip();
39523             }
39524             if(this.activePanel){
39525                 this.activePanel.getEl().unclip();
39526                 if(this.activePanel.afterSlide){
39527                     this.activePanel.afterSlide();
39528                 }
39529             }
39530         }
39531     },
39532
39533     initAutoHide : function(){
39534         if(this.autoHide !== false){
39535             if(!this.autoHideHd){
39536                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39537                 this.autoHideHd = {
39538                     "mouseout": function(e){
39539                         if(!e.within(this.el, true)){
39540                             st.delay(500);
39541                         }
39542                     },
39543                     "mouseover" : function(e){
39544                         st.cancel();
39545                     },
39546                     scope : this
39547                 };
39548             }
39549             this.el.on(this.autoHideHd);
39550         }
39551     },
39552
39553     clearAutoHide : function(){
39554         if(this.autoHide !== false){
39555             this.el.un("mouseout", this.autoHideHd.mouseout);
39556             this.el.un("mouseover", this.autoHideHd.mouseover);
39557         }
39558     },
39559
39560     clearMonitor : function(){
39561         Roo.get(document).un("click", this.slideInIf, this);
39562     },
39563
39564     // these names are backwards but not changed for compat
39565     slideOut : function(){
39566         if(this.isSlid || this.el.hasActiveFx()){
39567             return;
39568         }
39569         this.isSlid = true;
39570         if(this.collapseBtn){
39571             this.collapseBtn.hide();
39572         }
39573         this.closeBtnState = this.closeBtn.getStyle('display');
39574         this.closeBtn.hide();
39575         if(this.stickBtn){
39576             this.stickBtn.show();
39577         }
39578         this.el.show();
39579         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39580         this.beforeSlide();
39581         this.el.setStyle("z-index", 10001);
39582         this.el.slideIn(this.getSlideAnchor(), {
39583             callback: function(){
39584                 this.afterSlide();
39585                 this.initAutoHide();
39586                 Roo.get(document).on("click", this.slideInIf, this);
39587                 this.fireEvent("slideshow", this);
39588             },
39589             scope: this,
39590             block: true
39591         });
39592     },
39593
39594     afterSlideIn : function(){
39595         this.clearAutoHide();
39596         this.isSlid = false;
39597         this.clearMonitor();
39598         this.el.setStyle("z-index", "");
39599         if(this.collapseBtn){
39600             this.collapseBtn.show();
39601         }
39602         this.closeBtn.setStyle('display', this.closeBtnState);
39603         if(this.stickBtn){
39604             this.stickBtn.hide();
39605         }
39606         this.fireEvent("slidehide", this);
39607     },
39608
39609     slideIn : function(cb){
39610         if(!this.isSlid || this.el.hasActiveFx()){
39611             Roo.callback(cb);
39612             return;
39613         }
39614         this.isSlid = false;
39615         this.beforeSlide();
39616         this.el.slideOut(this.getSlideAnchor(), {
39617             callback: function(){
39618                 this.el.setLeftTop(-10000, -10000);
39619                 this.afterSlide();
39620                 this.afterSlideIn();
39621                 Roo.callback(cb);
39622             },
39623             scope: this,
39624             block: true
39625         });
39626     },
39627     
39628     slideInIf : function(e){
39629         if(!e.within(this.el)){
39630             this.slideIn();
39631         }
39632     },
39633
39634     animateCollapse : function(){
39635         this.beforeSlide();
39636         this.el.setStyle("z-index", 20000);
39637         var anchor = this.getSlideAnchor();
39638         this.el.slideOut(anchor, {
39639             callback : function(){
39640                 this.el.setStyle("z-index", "");
39641                 this.collapsedEl.slideIn(anchor, {duration:.3});
39642                 this.afterSlide();
39643                 this.el.setLocation(-10000,-10000);
39644                 this.el.hide();
39645                 this.fireEvent("collapsed", this);
39646             },
39647             scope: this,
39648             block: true
39649         });
39650     },
39651
39652     animateExpand : function(){
39653         this.beforeSlide();
39654         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39655         this.el.setStyle("z-index", 20000);
39656         this.collapsedEl.hide({
39657             duration:.1
39658         });
39659         this.el.slideIn(this.getSlideAnchor(), {
39660             callback : function(){
39661                 this.el.setStyle("z-index", "");
39662                 this.afterSlide();
39663                 if(this.split){
39664                     this.split.el.show();
39665                 }
39666                 this.fireEvent("invalidated", this);
39667                 this.fireEvent("expanded", this);
39668             },
39669             scope: this,
39670             block: true
39671         });
39672     },
39673
39674     anchors : {
39675         "west" : "left",
39676         "east" : "right",
39677         "north" : "top",
39678         "south" : "bottom"
39679     },
39680
39681     sanchors : {
39682         "west" : "l",
39683         "east" : "r",
39684         "north" : "t",
39685         "south" : "b"
39686     },
39687
39688     canchors : {
39689         "west" : "tl-tr",
39690         "east" : "tr-tl",
39691         "north" : "tl-bl",
39692         "south" : "bl-tl"
39693     },
39694
39695     getAnchor : function(){
39696         return this.anchors[this.position];
39697     },
39698
39699     getCollapseAnchor : function(){
39700         return this.canchors[this.position];
39701     },
39702
39703     getSlideAnchor : function(){
39704         return this.sanchors[this.position];
39705     },
39706
39707     getAlignAdj : function(){
39708         var cm = this.cmargins;
39709         switch(this.position){
39710             case "west":
39711                 return [0, 0];
39712             break;
39713             case "east":
39714                 return [0, 0];
39715             break;
39716             case "north":
39717                 return [0, 0];
39718             break;
39719             case "south":
39720                 return [0, 0];
39721             break;
39722         }
39723     },
39724
39725     getExpandAdj : function(){
39726         var c = this.collapsedEl, cm = this.cmargins;
39727         switch(this.position){
39728             case "west":
39729                 return [-(cm.right+c.getWidth()+cm.left), 0];
39730             break;
39731             case "east":
39732                 return [cm.right+c.getWidth()+cm.left, 0];
39733             break;
39734             case "north":
39735                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39736             break;
39737             case "south":
39738                 return [0, cm.top+cm.bottom+c.getHeight()];
39739             break;
39740         }
39741     }
39742 });/*
39743  * Based on:
39744  * Ext JS Library 1.1.1
39745  * Copyright(c) 2006-2007, Ext JS, LLC.
39746  *
39747  * Originally Released Under LGPL - original licence link has changed is not relivant.
39748  *
39749  * Fork - LGPL
39750  * <script type="text/javascript">
39751  */
39752 /*
39753  * These classes are private internal classes
39754  */
39755 Roo.bootstrap.layout.Center = function(config){
39756     config.region = "center";
39757     Roo.bootstrap.layout.Region.call(this, config);
39758     this.visible = true;
39759     this.minWidth = config.minWidth || 20;
39760     this.minHeight = config.minHeight || 20;
39761 };
39762
39763 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39764     hide : function(){
39765         // center panel can't be hidden
39766     },
39767     
39768     show : function(){
39769         // center panel can't be hidden
39770     },
39771     
39772     getMinWidth: function(){
39773         return this.minWidth;
39774     },
39775     
39776     getMinHeight: function(){
39777         return this.minHeight;
39778     }
39779 });
39780
39781
39782
39783
39784  
39785
39786
39787
39788
39789
39790
39791 Roo.bootstrap.layout.North = function(config)
39792 {
39793     config.region = 'north';
39794     config.cursor = 'n-resize';
39795     
39796     Roo.bootstrap.layout.Split.call(this, config);
39797     
39798     
39799     if(this.split){
39800         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39801         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39802         this.split.el.addClass("roo-layout-split-v");
39803     }
39804     //var size = config.initialSize || config.height;
39805     //if(this.el && typeof size != "undefined"){
39806     //    this.el.setHeight(size);
39807     //}
39808 };
39809 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39810 {
39811     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39812      
39813      
39814     onRender : function(ctr, pos)
39815     {
39816         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39817         var size = this.config.initialSize || this.config.height;
39818         if(this.el && typeof size != "undefined"){
39819             this.el.setHeight(size);
39820         }
39821     
39822     },
39823     
39824     getBox : function(){
39825         if(this.collapsed){
39826             return this.collapsedEl.getBox();
39827         }
39828         var box = this.el.getBox();
39829         if(this.split){
39830             box.height += this.split.el.getHeight();
39831         }
39832         return box;
39833     },
39834     
39835     updateBox : function(box){
39836         if(this.split && !this.collapsed){
39837             box.height -= this.split.el.getHeight();
39838             this.split.el.setLeft(box.x);
39839             this.split.el.setTop(box.y+box.height);
39840             this.split.el.setWidth(box.width);
39841         }
39842         if(this.collapsed){
39843             this.updateBody(box.width, null);
39844         }
39845         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39846     }
39847 });
39848
39849
39850
39851
39852
39853 Roo.bootstrap.layout.South = function(config){
39854     config.region = 'south';
39855     config.cursor = 's-resize';
39856     Roo.bootstrap.layout.Split.call(this, config);
39857     if(this.split){
39858         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39859         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39860         this.split.el.addClass("roo-layout-split-v");
39861     }
39862     
39863 };
39864
39865 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39866     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39867     
39868     onRender : function(ctr, pos)
39869     {
39870         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39871         var size = this.config.initialSize || this.config.height;
39872         if(this.el && typeof size != "undefined"){
39873             this.el.setHeight(size);
39874         }
39875     
39876     },
39877     
39878     getBox : function(){
39879         if(this.collapsed){
39880             return this.collapsedEl.getBox();
39881         }
39882         var box = this.el.getBox();
39883         if(this.split){
39884             var sh = this.split.el.getHeight();
39885             box.height += sh;
39886             box.y -= sh;
39887         }
39888         return box;
39889     },
39890     
39891     updateBox : function(box){
39892         if(this.split && !this.collapsed){
39893             var sh = this.split.el.getHeight();
39894             box.height -= sh;
39895             box.y += sh;
39896             this.split.el.setLeft(box.x);
39897             this.split.el.setTop(box.y-sh);
39898             this.split.el.setWidth(box.width);
39899         }
39900         if(this.collapsed){
39901             this.updateBody(box.width, null);
39902         }
39903         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39904     }
39905 });
39906
39907 Roo.bootstrap.layout.East = function(config){
39908     config.region = "east";
39909     config.cursor = "e-resize";
39910     Roo.bootstrap.layout.Split.call(this, config);
39911     if(this.split){
39912         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39913         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39914         this.split.el.addClass("roo-layout-split-h");
39915     }
39916     
39917 };
39918 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39919     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39920     
39921     onRender : function(ctr, pos)
39922     {
39923         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39924         var size = this.config.initialSize || this.config.width;
39925         if(this.el && typeof size != "undefined"){
39926             this.el.setWidth(size);
39927         }
39928     
39929     },
39930     
39931     getBox : function(){
39932         if(this.collapsed){
39933             return this.collapsedEl.getBox();
39934         }
39935         var box = this.el.getBox();
39936         if(this.split){
39937             var sw = this.split.el.getWidth();
39938             box.width += sw;
39939             box.x -= sw;
39940         }
39941         return box;
39942     },
39943
39944     updateBox : function(box){
39945         if(this.split && !this.collapsed){
39946             var sw = this.split.el.getWidth();
39947             box.width -= sw;
39948             this.split.el.setLeft(box.x);
39949             this.split.el.setTop(box.y);
39950             this.split.el.setHeight(box.height);
39951             box.x += sw;
39952         }
39953         if(this.collapsed){
39954             this.updateBody(null, box.height);
39955         }
39956         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39957     }
39958 });
39959
39960 Roo.bootstrap.layout.West = function(config){
39961     config.region = "west";
39962     config.cursor = "w-resize";
39963     
39964     Roo.bootstrap.layout.Split.call(this, config);
39965     if(this.split){
39966         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39967         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39968         this.split.el.addClass("roo-layout-split-h");
39969     }
39970     
39971 };
39972 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39973     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39974     
39975     onRender: function(ctr, pos)
39976     {
39977         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39978         var size = this.config.initialSize || this.config.width;
39979         if(typeof size != "undefined"){
39980             this.el.setWidth(size);
39981         }
39982     },
39983     
39984     getBox : function(){
39985         if(this.collapsed){
39986             return this.collapsedEl.getBox();
39987         }
39988         var box = this.el.getBox();
39989         if (box.width == 0) {
39990             box.width = this.config.width; // kludge?
39991         }
39992         if(this.split){
39993             box.width += this.split.el.getWidth();
39994         }
39995         return box;
39996     },
39997     
39998     updateBox : function(box){
39999         if(this.split && !this.collapsed){
40000             var sw = this.split.el.getWidth();
40001             box.width -= sw;
40002             this.split.el.setLeft(box.x+box.width);
40003             this.split.el.setTop(box.y);
40004             this.split.el.setHeight(box.height);
40005         }
40006         if(this.collapsed){
40007             this.updateBody(null, box.height);
40008         }
40009         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40010     }
40011 });Roo.namespace("Roo.bootstrap.panel");/*
40012  * Based on:
40013  * Ext JS Library 1.1.1
40014  * Copyright(c) 2006-2007, Ext JS, LLC.
40015  *
40016  * Originally Released Under LGPL - original licence link has changed is not relivant.
40017  *
40018  * Fork - LGPL
40019  * <script type="text/javascript">
40020  */
40021 /**
40022  * @class Roo.ContentPanel
40023  * @extends Roo.util.Observable
40024  * A basic ContentPanel element.
40025  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40026  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40027  * @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
40028  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40029  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40030  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40031  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40032  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40033  * @cfg {String} title          The title for this panel
40034  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40035  * @cfg {String} url            Calls {@link #setUrl} with this value
40036  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40037  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40038  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40039  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40040  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40041  * @cfg {Boolean} badges render the badges
40042  * @cfg {String} cls  extra classes to use  
40043  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40044
40045  * @constructor
40046  * Create a new ContentPanel.
40047  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40048  * @param {String/Object} config A string to set only the title or a config object
40049  * @param {String} content (optional) Set the HTML content for this panel
40050  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40051  */
40052 Roo.bootstrap.panel.Content = function( config){
40053     
40054     this.tpl = config.tpl || false;
40055     
40056     var el = config.el;
40057     var content = config.content;
40058
40059     if(config.autoCreate){ // xtype is available if this is called from factory
40060         el = Roo.id();
40061     }
40062     this.el = Roo.get(el);
40063     if(!this.el && config && config.autoCreate){
40064         if(typeof config.autoCreate == "object"){
40065             if(!config.autoCreate.id){
40066                 config.autoCreate.id = config.id||el;
40067             }
40068             this.el = Roo.DomHelper.append(document.body,
40069                         config.autoCreate, true);
40070         }else{
40071             var elcfg =  {
40072                 tag: "div",
40073                 cls: (config.cls || '') +
40074                     (config.background ? ' bg-' + config.background : '') +
40075                     " roo-layout-inactive-content",
40076                 id: config.id||el
40077             };
40078             if (config.iframe) {
40079                 elcfg.cn = [
40080                     {
40081                         tag : 'iframe',
40082                         style : 'border: 0px',
40083                         src : 'about:blank'
40084                     }
40085                 ];
40086             }
40087               
40088             if (config.html) {
40089                 elcfg.html = config.html;
40090                 
40091             }
40092                         
40093             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40094             if (config.iframe) {
40095                 this.iframeEl = this.el.select('iframe',true).first();
40096             }
40097             
40098         }
40099     } 
40100     this.closable = false;
40101     this.loaded = false;
40102     this.active = false;
40103    
40104       
40105     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40106         
40107         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40108         
40109         this.wrapEl = this.el; //this.el.wrap();
40110         var ti = [];
40111         if (config.toolbar.items) {
40112             ti = config.toolbar.items ;
40113             delete config.toolbar.items ;
40114         }
40115         
40116         var nitems = [];
40117         this.toolbar.render(this.wrapEl, 'before');
40118         for(var i =0;i < ti.length;i++) {
40119           //  Roo.log(['add child', items[i]]);
40120             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40121         }
40122         this.toolbar.items = nitems;
40123         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40124         delete config.toolbar;
40125         
40126     }
40127     /*
40128     // xtype created footer. - not sure if will work as we normally have to render first..
40129     if (this.footer && !this.footer.el && this.footer.xtype) {
40130         if (!this.wrapEl) {
40131             this.wrapEl = this.el.wrap();
40132         }
40133     
40134         this.footer.container = this.wrapEl.createChild();
40135          
40136         this.footer = Roo.factory(this.footer, Roo);
40137         
40138     }
40139     */
40140     
40141      if(typeof config == "string"){
40142         this.title = config;
40143     }else{
40144         Roo.apply(this, config);
40145     }
40146     
40147     if(this.resizeEl){
40148         this.resizeEl = Roo.get(this.resizeEl, true);
40149     }else{
40150         this.resizeEl = this.el;
40151     }
40152     // handle view.xtype
40153     
40154  
40155     
40156     
40157     this.addEvents({
40158         /**
40159          * @event activate
40160          * Fires when this panel is activated. 
40161          * @param {Roo.ContentPanel} this
40162          */
40163         "activate" : true,
40164         /**
40165          * @event deactivate
40166          * Fires when this panel is activated. 
40167          * @param {Roo.ContentPanel} this
40168          */
40169         "deactivate" : true,
40170
40171         /**
40172          * @event resize
40173          * Fires when this panel is resized if fitToFrame is true.
40174          * @param {Roo.ContentPanel} this
40175          * @param {Number} width The width after any component adjustments
40176          * @param {Number} height The height after any component adjustments
40177          */
40178         "resize" : true,
40179         
40180          /**
40181          * @event render
40182          * Fires when this tab is created
40183          * @param {Roo.ContentPanel} this
40184          */
40185         "render" : true
40186         
40187         
40188         
40189     });
40190     
40191
40192     
40193     
40194     if(this.autoScroll && !this.iframe){
40195         this.resizeEl.setStyle("overflow", "auto");
40196     } else {
40197         // fix randome scrolling
40198         //this.el.on('scroll', function() {
40199         //    Roo.log('fix random scolling');
40200         //    this.scrollTo('top',0); 
40201         //});
40202     }
40203     content = content || this.content;
40204     if(content){
40205         this.setContent(content);
40206     }
40207     if(config && config.url){
40208         this.setUrl(this.url, this.params, this.loadOnce);
40209     }
40210     
40211     
40212     
40213     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40214     
40215     if (this.view && typeof(this.view.xtype) != 'undefined') {
40216         this.view.el = this.el.appendChild(document.createElement("div"));
40217         this.view = Roo.factory(this.view); 
40218         this.view.render  &&  this.view.render(false, '');  
40219     }
40220     
40221     
40222     this.fireEvent('render', this);
40223 };
40224
40225 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40226     
40227     cls : '',
40228     background : '',
40229     
40230     tabTip : '',
40231     
40232     iframe : false,
40233     iframeEl : false,
40234     
40235     setRegion : function(region){
40236         this.region = region;
40237         this.setActiveClass(region && !this.background);
40238     },
40239     
40240     
40241     setActiveClass: function(state)
40242     {
40243         if(state){
40244            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40245            this.el.setStyle('position','relative');
40246         }else{
40247            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40248            this.el.setStyle('position', 'absolute');
40249         } 
40250     },
40251     
40252     /**
40253      * Returns the toolbar for this Panel if one was configured. 
40254      * @return {Roo.Toolbar} 
40255      */
40256     getToolbar : function(){
40257         return this.toolbar;
40258     },
40259     
40260     setActiveState : function(active)
40261     {
40262         this.active = active;
40263         this.setActiveClass(active);
40264         if(!active){
40265             if(this.fireEvent("deactivate", this) === false){
40266                 return false;
40267             }
40268             return true;
40269         }
40270         this.fireEvent("activate", this);
40271         return true;
40272     },
40273     /**
40274      * Updates this panel's element (not for iframe)
40275      * @param {String} content The new content
40276      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40277     */
40278     setContent : function(content, loadScripts){
40279         if (this.iframe) {
40280             return;
40281         }
40282         
40283         this.el.update(content, loadScripts);
40284     },
40285
40286     ignoreResize : function(w, h){
40287         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40288             return true;
40289         }else{
40290             this.lastSize = {width: w, height: h};
40291             return false;
40292         }
40293     },
40294     /**
40295      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40296      * @return {Roo.UpdateManager} The UpdateManager
40297      */
40298     getUpdateManager : function(){
40299         if (this.iframe) {
40300             return false;
40301         }
40302         return this.el.getUpdateManager();
40303     },
40304      /**
40305      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40306      * Does not work with IFRAME contents
40307      * @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:
40308 <pre><code>
40309 panel.load({
40310     url: "your-url.php",
40311     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40312     callback: yourFunction,
40313     scope: yourObject, //(optional scope)
40314     discardUrl: false,
40315     nocache: false,
40316     text: "Loading...",
40317     timeout: 30,
40318     scripts: false
40319 });
40320 </code></pre>
40321      
40322      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40323      * 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.
40324      * @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}
40325      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40326      * @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.
40327      * @return {Roo.ContentPanel} this
40328      */
40329     load : function(){
40330         
40331         if (this.iframe) {
40332             return this;
40333         }
40334         
40335         var um = this.el.getUpdateManager();
40336         um.update.apply(um, arguments);
40337         return this;
40338     },
40339
40340
40341     /**
40342      * 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.
40343      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40344      * @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)
40345      * @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)
40346      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40347      */
40348     setUrl : function(url, params, loadOnce){
40349         if (this.iframe) {
40350             this.iframeEl.dom.src = url;
40351             return false;
40352         }
40353         
40354         if(this.refreshDelegate){
40355             this.removeListener("activate", this.refreshDelegate);
40356         }
40357         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40358         this.on("activate", this.refreshDelegate);
40359         return this.el.getUpdateManager();
40360     },
40361     
40362     _handleRefresh : function(url, params, loadOnce){
40363         if(!loadOnce || !this.loaded){
40364             var updater = this.el.getUpdateManager();
40365             updater.update(url, params, this._setLoaded.createDelegate(this));
40366         }
40367     },
40368     
40369     _setLoaded : function(){
40370         this.loaded = true;
40371     }, 
40372     
40373     /**
40374      * Returns this panel's id
40375      * @return {String} 
40376      */
40377     getId : function(){
40378         return this.el.id;
40379     },
40380     
40381     /** 
40382      * Returns this panel's element - used by regiosn to add.
40383      * @return {Roo.Element} 
40384      */
40385     getEl : function(){
40386         return this.wrapEl || this.el;
40387     },
40388     
40389    
40390     
40391     adjustForComponents : function(width, height)
40392     {
40393         //Roo.log('adjustForComponents ');
40394         if(this.resizeEl != this.el){
40395             width -= this.el.getFrameWidth('lr');
40396             height -= this.el.getFrameWidth('tb');
40397         }
40398         if(this.toolbar){
40399             var te = this.toolbar.getEl();
40400             te.setWidth(width);
40401             height -= te.getHeight();
40402         }
40403         if(this.footer){
40404             var te = this.footer.getEl();
40405             te.setWidth(width);
40406             height -= te.getHeight();
40407         }
40408         
40409         
40410         if(this.adjustments){
40411             width += this.adjustments[0];
40412             height += this.adjustments[1];
40413         }
40414         return {"width": width, "height": height};
40415     },
40416     
40417     setSize : function(width, height){
40418         if(this.fitToFrame && !this.ignoreResize(width, height)){
40419             if(this.fitContainer && this.resizeEl != this.el){
40420                 this.el.setSize(width, height);
40421             }
40422             var size = this.adjustForComponents(width, height);
40423             if (this.iframe) {
40424                 this.iframeEl.setSize(width,height);
40425             }
40426             
40427             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40428             this.fireEvent('resize', this, size.width, size.height);
40429             
40430             
40431         }
40432     },
40433     
40434     /**
40435      * Returns this panel's title
40436      * @return {String} 
40437      */
40438     getTitle : function(){
40439         
40440         if (typeof(this.title) != 'object') {
40441             return this.title;
40442         }
40443         
40444         var t = '';
40445         for (var k in this.title) {
40446             if (!this.title.hasOwnProperty(k)) {
40447                 continue;
40448             }
40449             
40450             if (k.indexOf('-') >= 0) {
40451                 var s = k.split('-');
40452                 for (var i = 0; i<s.length; i++) {
40453                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40454                 }
40455             } else {
40456                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40457             }
40458         }
40459         return t;
40460     },
40461     
40462     /**
40463      * Set this panel's title
40464      * @param {String} title
40465      */
40466     setTitle : function(title){
40467         this.title = title;
40468         if(this.region){
40469             this.region.updatePanelTitle(this, title);
40470         }
40471     },
40472     
40473     /**
40474      * Returns true is this panel was configured to be closable
40475      * @return {Boolean} 
40476      */
40477     isClosable : function(){
40478         return this.closable;
40479     },
40480     
40481     beforeSlide : function(){
40482         this.el.clip();
40483         this.resizeEl.clip();
40484     },
40485     
40486     afterSlide : function(){
40487         this.el.unclip();
40488         this.resizeEl.unclip();
40489     },
40490     
40491     /**
40492      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40493      *   Will fail silently if the {@link #setUrl} method has not been called.
40494      *   This does not activate the panel, just updates its content.
40495      */
40496     refresh : function(){
40497         if(this.refreshDelegate){
40498            this.loaded = false;
40499            this.refreshDelegate();
40500         }
40501     },
40502     
40503     /**
40504      * Destroys this panel
40505      */
40506     destroy : function(){
40507         this.el.removeAllListeners();
40508         var tempEl = document.createElement("span");
40509         tempEl.appendChild(this.el.dom);
40510         tempEl.innerHTML = "";
40511         this.el.remove();
40512         this.el = null;
40513     },
40514     
40515     /**
40516      * form - if the content panel contains a form - this is a reference to it.
40517      * @type {Roo.form.Form}
40518      */
40519     form : false,
40520     /**
40521      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40522      *    This contains a reference to it.
40523      * @type {Roo.View}
40524      */
40525     view : false,
40526     
40527       /**
40528      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40529      * <pre><code>
40530
40531 layout.addxtype({
40532        xtype : 'Form',
40533        items: [ .... ]
40534    }
40535 );
40536
40537 </code></pre>
40538      * @param {Object} cfg Xtype definition of item to add.
40539      */
40540     
40541     
40542     getChildContainer: function () {
40543         return this.getEl();
40544     }
40545     
40546     
40547     /*
40548         var  ret = new Roo.factory(cfg);
40549         return ret;
40550         
40551         
40552         // add form..
40553         if (cfg.xtype.match(/^Form$/)) {
40554             
40555             var el;
40556             //if (this.footer) {
40557             //    el = this.footer.container.insertSibling(false, 'before');
40558             //} else {
40559                 el = this.el.createChild();
40560             //}
40561
40562             this.form = new  Roo.form.Form(cfg);
40563             
40564             
40565             if ( this.form.allItems.length) {
40566                 this.form.render(el.dom);
40567             }
40568             return this.form;
40569         }
40570         // should only have one of theses..
40571         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40572             // views.. should not be just added - used named prop 'view''
40573             
40574             cfg.el = this.el.appendChild(document.createElement("div"));
40575             // factory?
40576             
40577             var ret = new Roo.factory(cfg);
40578              
40579              ret.render && ret.render(false, ''); // render blank..
40580             this.view = ret;
40581             return ret;
40582         }
40583         return false;
40584     }
40585     \*/
40586 });
40587  
40588 /**
40589  * @class Roo.bootstrap.panel.Grid
40590  * @extends Roo.bootstrap.panel.Content
40591  * @constructor
40592  * Create a new GridPanel.
40593  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40594  * @param {Object} config A the config object
40595   
40596  */
40597
40598
40599
40600 Roo.bootstrap.panel.Grid = function(config)
40601 {
40602     
40603       
40604     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40605         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40606
40607     config.el = this.wrapper;
40608     //this.el = this.wrapper;
40609     
40610       if (config.container) {
40611         // ctor'ed from a Border/panel.grid
40612         
40613         
40614         this.wrapper.setStyle("overflow", "hidden");
40615         this.wrapper.addClass('roo-grid-container');
40616
40617     }
40618     
40619     
40620     if(config.toolbar){
40621         var tool_el = this.wrapper.createChild();    
40622         this.toolbar = Roo.factory(config.toolbar);
40623         var ti = [];
40624         if (config.toolbar.items) {
40625             ti = config.toolbar.items ;
40626             delete config.toolbar.items ;
40627         }
40628         
40629         var nitems = [];
40630         this.toolbar.render(tool_el);
40631         for(var i =0;i < ti.length;i++) {
40632           //  Roo.log(['add child', items[i]]);
40633             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40634         }
40635         this.toolbar.items = nitems;
40636         
40637         delete config.toolbar;
40638     }
40639     
40640     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40641     config.grid.scrollBody = true;;
40642     config.grid.monitorWindowResize = false; // turn off autosizing
40643     config.grid.autoHeight = false;
40644     config.grid.autoWidth = false;
40645     
40646     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40647     
40648     if (config.background) {
40649         // render grid on panel activation (if panel background)
40650         this.on('activate', function(gp) {
40651             if (!gp.grid.rendered) {
40652                 gp.grid.render(this.wrapper);
40653                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40654             }
40655         });
40656             
40657     } else {
40658         this.grid.render(this.wrapper);
40659         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40660
40661     }
40662     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40663     // ??? needed ??? config.el = this.wrapper;
40664     
40665     
40666     
40667   
40668     // xtype created footer. - not sure if will work as we normally have to render first..
40669     if (this.footer && !this.footer.el && this.footer.xtype) {
40670         
40671         var ctr = this.grid.getView().getFooterPanel(true);
40672         this.footer.dataSource = this.grid.dataSource;
40673         this.footer = Roo.factory(this.footer, Roo);
40674         this.footer.render(ctr);
40675         
40676     }
40677     
40678     
40679     
40680     
40681      
40682 };
40683
40684 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40685     getId : function(){
40686         return this.grid.id;
40687     },
40688     
40689     /**
40690      * Returns the grid for this panel
40691      * @return {Roo.bootstrap.Table} 
40692      */
40693     getGrid : function(){
40694         return this.grid;    
40695     },
40696     
40697     setSize : function(width, height){
40698         if(!this.ignoreResize(width, height)){
40699             var grid = this.grid;
40700             var size = this.adjustForComponents(width, height);
40701             // tfoot is not a footer?
40702           
40703             
40704             var gridel = grid.getGridEl();
40705             gridel.setSize(size.width, size.height);
40706             
40707             var tbd = grid.getGridEl().select('tbody', true).first();
40708             var thd = grid.getGridEl().select('thead',true).first();
40709             var tbf= grid.getGridEl().select('tfoot', true).first();
40710
40711             if (tbf) {
40712                 size.height -= tbf.getHeight();
40713             }
40714             if (thd) {
40715                 size.height -= thd.getHeight();
40716             }
40717             
40718             tbd.setSize(size.width, size.height );
40719             // this is for the account management tab -seems to work there.
40720             var thd = grid.getGridEl().select('thead',true).first();
40721             //if (tbd) {
40722             //    tbd.setSize(size.width, size.height - thd.getHeight());
40723             //}
40724              
40725             grid.autoSize();
40726         }
40727     },
40728      
40729     
40730     
40731     beforeSlide : function(){
40732         this.grid.getView().scroller.clip();
40733     },
40734     
40735     afterSlide : function(){
40736         this.grid.getView().scroller.unclip();
40737     },
40738     
40739     destroy : function(){
40740         this.grid.destroy();
40741         delete this.grid;
40742         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40743     }
40744 });
40745
40746 /**
40747  * @class Roo.bootstrap.panel.Nest
40748  * @extends Roo.bootstrap.panel.Content
40749  * @constructor
40750  * Create a new Panel, that can contain a layout.Border.
40751  * 
40752  * 
40753  * @param {Roo.BorderLayout} layout The layout for this panel
40754  * @param {String/Object} config A string to set only the title or a config object
40755  */
40756 Roo.bootstrap.panel.Nest = function(config)
40757 {
40758     // construct with only one argument..
40759     /* FIXME - implement nicer consturctors
40760     if (layout.layout) {
40761         config = layout;
40762         layout = config.layout;
40763         delete config.layout;
40764     }
40765     if (layout.xtype && !layout.getEl) {
40766         // then layout needs constructing..
40767         layout = Roo.factory(layout, Roo);
40768     }
40769     */
40770     
40771     config.el =  config.layout.getEl();
40772     
40773     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40774     
40775     config.layout.monitorWindowResize = false; // turn off autosizing
40776     this.layout = config.layout;
40777     this.layout.getEl().addClass("roo-layout-nested-layout");
40778     this.layout.parent = this;
40779     
40780     
40781     
40782     
40783 };
40784
40785 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40786
40787     setSize : function(width, height){
40788         if(!this.ignoreResize(width, height)){
40789             var size = this.adjustForComponents(width, height);
40790             var el = this.layout.getEl();
40791             if (size.height < 1) {
40792                 el.setWidth(size.width);   
40793             } else {
40794                 el.setSize(size.width, size.height);
40795             }
40796             var touch = el.dom.offsetWidth;
40797             this.layout.layout();
40798             // ie requires a double layout on the first pass
40799             if(Roo.isIE && !this.initialized){
40800                 this.initialized = true;
40801                 this.layout.layout();
40802             }
40803         }
40804     },
40805     
40806     // activate all subpanels if not currently active..
40807     
40808     setActiveState : function(active){
40809         this.active = active;
40810         this.setActiveClass(active);
40811         
40812         if(!active){
40813             this.fireEvent("deactivate", this);
40814             return;
40815         }
40816         
40817         this.fireEvent("activate", this);
40818         // not sure if this should happen before or after..
40819         if (!this.layout) {
40820             return; // should not happen..
40821         }
40822         var reg = false;
40823         for (var r in this.layout.regions) {
40824             reg = this.layout.getRegion(r);
40825             if (reg.getActivePanel()) {
40826                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40827                 reg.setActivePanel(reg.getActivePanel());
40828                 continue;
40829             }
40830             if (!reg.panels.length) {
40831                 continue;
40832             }
40833             reg.showPanel(reg.getPanel(0));
40834         }
40835         
40836         
40837         
40838         
40839     },
40840     
40841     /**
40842      * Returns the nested BorderLayout for this panel
40843      * @return {Roo.BorderLayout} 
40844      */
40845     getLayout : function(){
40846         return this.layout;
40847     },
40848     
40849      /**
40850      * Adds a xtype elements to the layout of the nested panel
40851      * <pre><code>
40852
40853 panel.addxtype({
40854        xtype : 'ContentPanel',
40855        region: 'west',
40856        items: [ .... ]
40857    }
40858 );
40859
40860 panel.addxtype({
40861         xtype : 'NestedLayoutPanel',
40862         region: 'west',
40863         layout: {
40864            center: { },
40865            west: { }   
40866         },
40867         items : [ ... list of content panels or nested layout panels.. ]
40868    }
40869 );
40870 </code></pre>
40871      * @param {Object} cfg Xtype definition of item to add.
40872      */
40873     addxtype : function(cfg) {
40874         return this.layout.addxtype(cfg);
40875     
40876     }
40877 });/*
40878  * Based on:
40879  * Ext JS Library 1.1.1
40880  * Copyright(c) 2006-2007, Ext JS, LLC.
40881  *
40882  * Originally Released Under LGPL - original licence link has changed is not relivant.
40883  *
40884  * Fork - LGPL
40885  * <script type="text/javascript">
40886  */
40887 /**
40888  * @class Roo.TabPanel
40889  * @extends Roo.util.Observable
40890  * A lightweight tab container.
40891  * <br><br>
40892  * Usage:
40893  * <pre><code>
40894 // basic tabs 1, built from existing content
40895 var tabs = new Roo.TabPanel("tabs1");
40896 tabs.addTab("script", "View Script");
40897 tabs.addTab("markup", "View Markup");
40898 tabs.activate("script");
40899
40900 // more advanced tabs, built from javascript
40901 var jtabs = new Roo.TabPanel("jtabs");
40902 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40903
40904 // set up the UpdateManager
40905 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40906 var updater = tab2.getUpdateManager();
40907 updater.setDefaultUrl("ajax1.htm");
40908 tab2.on('activate', updater.refresh, updater, true);
40909
40910 // Use setUrl for Ajax loading
40911 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40912 tab3.setUrl("ajax2.htm", null, true);
40913
40914 // Disabled tab
40915 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40916 tab4.disable();
40917
40918 jtabs.activate("jtabs-1");
40919  * </code></pre>
40920  * @constructor
40921  * Create a new TabPanel.
40922  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40923  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40924  */
40925 Roo.bootstrap.panel.Tabs = function(config){
40926     /**
40927     * The container element for this TabPanel.
40928     * @type Roo.Element
40929     */
40930     this.el = Roo.get(config.el);
40931     delete config.el;
40932     if(config){
40933         if(typeof config == "boolean"){
40934             this.tabPosition = config ? "bottom" : "top";
40935         }else{
40936             Roo.apply(this, config);
40937         }
40938     }
40939     
40940     if(this.tabPosition == "bottom"){
40941         // if tabs are at the bottom = create the body first.
40942         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40943         this.el.addClass("roo-tabs-bottom");
40944     }
40945     // next create the tabs holders
40946     
40947     if (this.tabPosition == "west"){
40948         
40949         var reg = this.region; // fake it..
40950         while (reg) {
40951             if (!reg.mgr.parent) {
40952                 break;
40953             }
40954             reg = reg.mgr.parent.region;
40955         }
40956         Roo.log("got nest?");
40957         Roo.log(reg);
40958         if (reg.mgr.getRegion('west')) {
40959             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40960             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40961             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40962             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40963             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40964         
40965             
40966         }
40967         
40968         
40969     } else {
40970      
40971         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40972         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40973         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40974         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40975     }
40976     
40977     
40978     if(Roo.isIE){
40979         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40980     }
40981     
40982     // finally - if tabs are at the top, then create the body last..
40983     if(this.tabPosition != "bottom"){
40984         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40985          * @type Roo.Element
40986          */
40987         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40988         this.el.addClass("roo-tabs-top");
40989     }
40990     this.items = [];
40991
40992     this.bodyEl.setStyle("position", "relative");
40993
40994     this.active = null;
40995     this.activateDelegate = this.activate.createDelegate(this);
40996
40997     this.addEvents({
40998         /**
40999          * @event tabchange
41000          * Fires when the active tab changes
41001          * @param {Roo.TabPanel} this
41002          * @param {Roo.TabPanelItem} activePanel The new active tab
41003          */
41004         "tabchange": true,
41005         /**
41006          * @event beforetabchange
41007          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41008          * @param {Roo.TabPanel} this
41009          * @param {Object} e Set cancel to true on this object to cancel the tab change
41010          * @param {Roo.TabPanelItem} tab The tab being changed to
41011          */
41012         "beforetabchange" : true
41013     });
41014
41015     Roo.EventManager.onWindowResize(this.onResize, this);
41016     this.cpad = this.el.getPadding("lr");
41017     this.hiddenCount = 0;
41018
41019
41020     // toolbar on the tabbar support...
41021     if (this.toolbar) {
41022         alert("no toolbar support yet");
41023         this.toolbar  = false;
41024         /*
41025         var tcfg = this.toolbar;
41026         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41027         this.toolbar = new Roo.Toolbar(tcfg);
41028         if (Roo.isSafari) {
41029             var tbl = tcfg.container.child('table', true);
41030             tbl.setAttribute('width', '100%');
41031         }
41032         */
41033         
41034     }
41035    
41036
41037
41038     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41039 };
41040
41041 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41042     /*
41043      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41044      */
41045     tabPosition : "top",
41046     /*
41047      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41048      */
41049     currentTabWidth : 0,
41050     /*
41051      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41052      */
41053     minTabWidth : 40,
41054     /*
41055      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41056      */
41057     maxTabWidth : 250,
41058     /*
41059      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41060      */
41061     preferredTabWidth : 175,
41062     /*
41063      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41064      */
41065     resizeTabs : false,
41066     /*
41067      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41068      */
41069     monitorResize : true,
41070     /*
41071      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41072      */
41073     toolbar : false,  // set by caller..
41074     
41075     region : false, /// set by caller
41076     
41077     disableTooltips : true, // not used yet...
41078
41079     /**
41080      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41081      * @param {String} id The id of the div to use <b>or create</b>
41082      * @param {String} text The text for the tab
41083      * @param {String} content (optional) Content to put in the TabPanelItem body
41084      * @param {Boolean} closable (optional) True to create a close icon on the tab
41085      * @return {Roo.TabPanelItem} The created TabPanelItem
41086      */
41087     addTab : function(id, text, content, closable, tpl)
41088     {
41089         var item = new Roo.bootstrap.panel.TabItem({
41090             panel: this,
41091             id : id,
41092             text : text,
41093             closable : closable,
41094             tpl : tpl
41095         });
41096         this.addTabItem(item);
41097         if(content){
41098             item.setContent(content);
41099         }
41100         return item;
41101     },
41102
41103     /**
41104      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41105      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41106      * @return {Roo.TabPanelItem}
41107      */
41108     getTab : function(id){
41109         return this.items[id];
41110     },
41111
41112     /**
41113      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41114      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41115      */
41116     hideTab : function(id){
41117         var t = this.items[id];
41118         if(!t.isHidden()){
41119            t.setHidden(true);
41120            this.hiddenCount++;
41121            this.autoSizeTabs();
41122         }
41123     },
41124
41125     /**
41126      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41127      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41128      */
41129     unhideTab : function(id){
41130         var t = this.items[id];
41131         if(t.isHidden()){
41132            t.setHidden(false);
41133            this.hiddenCount--;
41134            this.autoSizeTabs();
41135         }
41136     },
41137
41138     /**
41139      * Adds an existing {@link Roo.TabPanelItem}.
41140      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41141      */
41142     addTabItem : function(item)
41143     {
41144         this.items[item.id] = item;
41145         this.items.push(item);
41146         this.autoSizeTabs();
41147       //  if(this.resizeTabs){
41148     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41149   //         this.autoSizeTabs();
41150 //        }else{
41151 //            item.autoSize();
41152        // }
41153     },
41154
41155     /**
41156      * Removes a {@link Roo.TabPanelItem}.
41157      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41158      */
41159     removeTab : function(id){
41160         var items = this.items;
41161         var tab = items[id];
41162         if(!tab) { return; }
41163         var index = items.indexOf(tab);
41164         if(this.active == tab && items.length > 1){
41165             var newTab = this.getNextAvailable(index);
41166             if(newTab) {
41167                 newTab.activate();
41168             }
41169         }
41170         this.stripEl.dom.removeChild(tab.pnode.dom);
41171         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41172             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41173         }
41174         items.splice(index, 1);
41175         delete this.items[tab.id];
41176         tab.fireEvent("close", tab);
41177         tab.purgeListeners();
41178         this.autoSizeTabs();
41179     },
41180
41181     getNextAvailable : function(start){
41182         var items = this.items;
41183         var index = start;
41184         // look for a next tab that will slide over to
41185         // replace the one being removed
41186         while(index < items.length){
41187             var item = items[++index];
41188             if(item && !item.isHidden()){
41189                 return item;
41190             }
41191         }
41192         // if one isn't found select the previous tab (on the left)
41193         index = start;
41194         while(index >= 0){
41195             var item = items[--index];
41196             if(item && !item.isHidden()){
41197                 return item;
41198             }
41199         }
41200         return null;
41201     },
41202
41203     /**
41204      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41205      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41206      */
41207     disableTab : function(id){
41208         var tab = this.items[id];
41209         if(tab && this.active != tab){
41210             tab.disable();
41211         }
41212     },
41213
41214     /**
41215      * Enables a {@link Roo.TabPanelItem} that is disabled.
41216      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41217      */
41218     enableTab : function(id){
41219         var tab = this.items[id];
41220         tab.enable();
41221     },
41222
41223     /**
41224      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41225      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41226      * @return {Roo.TabPanelItem} The TabPanelItem.
41227      */
41228     activate : function(id)
41229     {
41230         //Roo.log('activite:'  + id);
41231         
41232         var tab = this.items[id];
41233         if(!tab){
41234             return null;
41235         }
41236         if(tab == this.active || tab.disabled){
41237             return tab;
41238         }
41239         var e = {};
41240         this.fireEvent("beforetabchange", this, e, tab);
41241         if(e.cancel !== true && !tab.disabled){
41242             if(this.active){
41243                 this.active.hide();
41244             }
41245             this.active = this.items[id];
41246             this.active.show();
41247             this.fireEvent("tabchange", this, this.active);
41248         }
41249         return tab;
41250     },
41251
41252     /**
41253      * Gets the active {@link Roo.TabPanelItem}.
41254      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41255      */
41256     getActiveTab : function(){
41257         return this.active;
41258     },
41259
41260     /**
41261      * Updates the tab body element to fit the height of the container element
41262      * for overflow scrolling
41263      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41264      */
41265     syncHeight : function(targetHeight){
41266         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41267         var bm = this.bodyEl.getMargins();
41268         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41269         this.bodyEl.setHeight(newHeight);
41270         return newHeight;
41271     },
41272
41273     onResize : function(){
41274         if(this.monitorResize){
41275             this.autoSizeTabs();
41276         }
41277     },
41278
41279     /**
41280      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41281      */
41282     beginUpdate : function(){
41283         this.updating = true;
41284     },
41285
41286     /**
41287      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41288      */
41289     endUpdate : function(){
41290         this.updating = false;
41291         this.autoSizeTabs();
41292     },
41293
41294     /**
41295      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41296      */
41297     autoSizeTabs : function()
41298     {
41299         var count = this.items.length;
41300         var vcount = count - this.hiddenCount;
41301         
41302         if (vcount < 2) {
41303             this.stripEl.hide();
41304         } else {
41305             this.stripEl.show();
41306         }
41307         
41308         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41309             return;
41310         }
41311         
41312         
41313         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41314         var availWidth = Math.floor(w / vcount);
41315         var b = this.stripBody;
41316         if(b.getWidth() > w){
41317             var tabs = this.items;
41318             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41319             if(availWidth < this.minTabWidth){
41320                 /*if(!this.sleft){    // incomplete scrolling code
41321                     this.createScrollButtons();
41322                 }
41323                 this.showScroll();
41324                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41325             }
41326         }else{
41327             if(this.currentTabWidth < this.preferredTabWidth){
41328                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41329             }
41330         }
41331     },
41332
41333     /**
41334      * Returns the number of tabs in this TabPanel.
41335      * @return {Number}
41336      */
41337      getCount : function(){
41338          return this.items.length;
41339      },
41340
41341     /**
41342      * Resizes all the tabs to the passed width
41343      * @param {Number} The new width
41344      */
41345     setTabWidth : function(width){
41346         this.currentTabWidth = width;
41347         for(var i = 0, len = this.items.length; i < len; i++) {
41348                 if(!this.items[i].isHidden()) {
41349                 this.items[i].setWidth(width);
41350             }
41351         }
41352     },
41353
41354     /**
41355      * Destroys this TabPanel
41356      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41357      */
41358     destroy : function(removeEl){
41359         Roo.EventManager.removeResizeListener(this.onResize, this);
41360         for(var i = 0, len = this.items.length; i < len; i++){
41361             this.items[i].purgeListeners();
41362         }
41363         if(removeEl === true){
41364             this.el.update("");
41365             this.el.remove();
41366         }
41367     },
41368     
41369     createStrip : function(container)
41370     {
41371         var strip = document.createElement("nav");
41372         strip.className = Roo.bootstrap.version == 4 ?
41373             "navbar-light bg-light" : 
41374             "navbar navbar-default"; //"x-tabs-wrap";
41375         container.appendChild(strip);
41376         return strip;
41377     },
41378     
41379     createStripList : function(strip)
41380     {
41381         // div wrapper for retard IE
41382         // returns the "tr" element.
41383         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41384         //'<div class="x-tabs-strip-wrap">'+
41385           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41386           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41387         return strip.firstChild; //.firstChild.firstChild.firstChild;
41388     },
41389     createBody : function(container)
41390     {
41391         var body = document.createElement("div");
41392         Roo.id(body, "tab-body");
41393         //Roo.fly(body).addClass("x-tabs-body");
41394         Roo.fly(body).addClass("tab-content");
41395         container.appendChild(body);
41396         return body;
41397     },
41398     createItemBody :function(bodyEl, id){
41399         var body = Roo.getDom(id);
41400         if(!body){
41401             body = document.createElement("div");
41402             body.id = id;
41403         }
41404         //Roo.fly(body).addClass("x-tabs-item-body");
41405         Roo.fly(body).addClass("tab-pane");
41406          bodyEl.insertBefore(body, bodyEl.firstChild);
41407         return body;
41408     },
41409     /** @private */
41410     createStripElements :  function(stripEl, text, closable, tpl)
41411     {
41412         var td = document.createElement("li"); // was td..
41413         td.className = 'nav-item';
41414         
41415         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41416         
41417         
41418         stripEl.appendChild(td);
41419         /*if(closable){
41420             td.className = "x-tabs-closable";
41421             if(!this.closeTpl){
41422                 this.closeTpl = new Roo.Template(
41423                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41424                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41425                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41426                 );
41427             }
41428             var el = this.closeTpl.overwrite(td, {"text": text});
41429             var close = el.getElementsByTagName("div")[0];
41430             var inner = el.getElementsByTagName("em")[0];
41431             return {"el": el, "close": close, "inner": inner};
41432         } else {
41433         */
41434         // not sure what this is..
41435 //            if(!this.tabTpl){
41436                 //this.tabTpl = new Roo.Template(
41437                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41438                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41439                 //);
41440 //                this.tabTpl = new Roo.Template(
41441 //                   '<a href="#">' +
41442 //                   '<span unselectable="on"' +
41443 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41444 //                            ' >{text}</span></a>'
41445 //                );
41446 //                
41447 //            }
41448
41449
41450             var template = tpl || this.tabTpl || false;
41451             
41452             if(!template){
41453                 template =  new Roo.Template(
41454                         Roo.bootstrap.version == 4 ? 
41455                             (
41456                                 '<a class="nav-link" href="#" unselectable="on"' +
41457                                      (this.disableTooltips ? '' : ' title="{text}"') +
41458                                      ' >{text}</a>'
41459                             ) : (
41460                                 '<a class="nav-link" href="#">' +
41461                                 '<span unselectable="on"' +
41462                                          (this.disableTooltips ? '' : ' title="{text}"') +
41463                                     ' >{text}</span></a>'
41464                             )
41465                 );
41466             }
41467             
41468             switch (typeof(template)) {
41469                 case 'object' :
41470                     break;
41471                 case 'string' :
41472                     template = new Roo.Template(template);
41473                     break;
41474                 default :
41475                     break;
41476             }
41477             
41478             var el = template.overwrite(td, {"text": text});
41479             
41480             var inner = el.getElementsByTagName("span")[0];
41481             
41482             return {"el": el, "inner": inner};
41483             
41484     }
41485         
41486     
41487 });
41488
41489 /**
41490  * @class Roo.TabPanelItem
41491  * @extends Roo.util.Observable
41492  * Represents an individual item (tab plus body) in a TabPanel.
41493  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41494  * @param {String} id The id of this TabPanelItem
41495  * @param {String} text The text for the tab of this TabPanelItem
41496  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41497  */
41498 Roo.bootstrap.panel.TabItem = function(config){
41499     /**
41500      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41501      * @type Roo.TabPanel
41502      */
41503     this.tabPanel = config.panel;
41504     /**
41505      * The id for this TabPanelItem
41506      * @type String
41507      */
41508     this.id = config.id;
41509     /** @private */
41510     this.disabled = false;
41511     /** @private */
41512     this.text = config.text;
41513     /** @private */
41514     this.loaded = false;
41515     this.closable = config.closable;
41516
41517     /**
41518      * The body element for this TabPanelItem.
41519      * @type Roo.Element
41520      */
41521     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41522     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41523     this.bodyEl.setStyle("display", "block");
41524     this.bodyEl.setStyle("zoom", "1");
41525     //this.hideAction();
41526
41527     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41528     /** @private */
41529     this.el = Roo.get(els.el);
41530     this.inner = Roo.get(els.inner, true);
41531      this.textEl = Roo.bootstrap.version == 4 ?
41532         this.el : Roo.get(this.el.dom.firstChild, true);
41533
41534     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41535     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41536
41537     
41538 //    this.el.on("mousedown", this.onTabMouseDown, this);
41539     this.el.on("click", this.onTabClick, this);
41540     /** @private */
41541     if(config.closable){
41542         var c = Roo.get(els.close, true);
41543         c.dom.title = this.closeText;
41544         c.addClassOnOver("close-over");
41545         c.on("click", this.closeClick, this);
41546      }
41547
41548     this.addEvents({
41549          /**
41550          * @event activate
41551          * Fires when this tab becomes the active tab.
41552          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41553          * @param {Roo.TabPanelItem} this
41554          */
41555         "activate": true,
41556         /**
41557          * @event beforeclose
41558          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41559          * @param {Roo.TabPanelItem} this
41560          * @param {Object} e Set cancel to true on this object to cancel the close.
41561          */
41562         "beforeclose": true,
41563         /**
41564          * @event close
41565          * Fires when this tab is closed.
41566          * @param {Roo.TabPanelItem} this
41567          */
41568          "close": true,
41569         /**
41570          * @event deactivate
41571          * Fires when this tab is no longer the active tab.
41572          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41573          * @param {Roo.TabPanelItem} this
41574          */
41575          "deactivate" : true
41576     });
41577     this.hidden = false;
41578
41579     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41580 };
41581
41582 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41583            {
41584     purgeListeners : function(){
41585        Roo.util.Observable.prototype.purgeListeners.call(this);
41586        this.el.removeAllListeners();
41587     },
41588     /**
41589      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41590      */
41591     show : function(){
41592         this.status_node.addClass("active");
41593         this.showAction();
41594         if(Roo.isOpera){
41595             this.tabPanel.stripWrap.repaint();
41596         }
41597         this.fireEvent("activate", this.tabPanel, this);
41598     },
41599
41600     /**
41601      * Returns true if this tab is the active tab.
41602      * @return {Boolean}
41603      */
41604     isActive : function(){
41605         return this.tabPanel.getActiveTab() == this;
41606     },
41607
41608     /**
41609      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41610      */
41611     hide : function(){
41612         this.status_node.removeClass("active");
41613         this.hideAction();
41614         this.fireEvent("deactivate", this.tabPanel, this);
41615     },
41616
41617     hideAction : function(){
41618         this.bodyEl.hide();
41619         this.bodyEl.setStyle("position", "absolute");
41620         this.bodyEl.setLeft("-20000px");
41621         this.bodyEl.setTop("-20000px");
41622     },
41623
41624     showAction : function(){
41625         this.bodyEl.setStyle("position", "relative");
41626         this.bodyEl.setTop("");
41627         this.bodyEl.setLeft("");
41628         this.bodyEl.show();
41629     },
41630
41631     /**
41632      * Set the tooltip for the tab.
41633      * @param {String} tooltip The tab's tooltip
41634      */
41635     setTooltip : function(text){
41636         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41637             this.textEl.dom.qtip = text;
41638             this.textEl.dom.removeAttribute('title');
41639         }else{
41640             this.textEl.dom.title = text;
41641         }
41642     },
41643
41644     onTabClick : function(e){
41645         e.preventDefault();
41646         this.tabPanel.activate(this.id);
41647     },
41648
41649     onTabMouseDown : function(e){
41650         e.preventDefault();
41651         this.tabPanel.activate(this.id);
41652     },
41653 /*
41654     getWidth : function(){
41655         return this.inner.getWidth();
41656     },
41657
41658     setWidth : function(width){
41659         var iwidth = width - this.linode.getPadding("lr");
41660         this.inner.setWidth(iwidth);
41661         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41662         this.linode.setWidth(width);
41663     },
41664 */
41665     /**
41666      * Show or hide the tab
41667      * @param {Boolean} hidden True to hide or false to show.
41668      */
41669     setHidden : function(hidden){
41670         this.hidden = hidden;
41671         this.linode.setStyle("display", hidden ? "none" : "");
41672     },
41673
41674     /**
41675      * Returns true if this tab is "hidden"
41676      * @return {Boolean}
41677      */
41678     isHidden : function(){
41679         return this.hidden;
41680     },
41681
41682     /**
41683      * Returns the text for this tab
41684      * @return {String}
41685      */
41686     getText : function(){
41687         return this.text;
41688     },
41689     /*
41690     autoSize : function(){
41691         //this.el.beginMeasure();
41692         this.textEl.setWidth(1);
41693         /*
41694          *  #2804 [new] Tabs in Roojs
41695          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41696          */
41697         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41698         //this.el.endMeasure();
41699     //},
41700
41701     /**
41702      * Sets the text for the tab (Note: this also sets the tooltip text)
41703      * @param {String} text The tab's text and tooltip
41704      */
41705     setText : function(text){
41706         this.text = text;
41707         this.textEl.update(text);
41708         this.setTooltip(text);
41709         //if(!this.tabPanel.resizeTabs){
41710         //    this.autoSize();
41711         //}
41712     },
41713     /**
41714      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41715      */
41716     activate : function(){
41717         this.tabPanel.activate(this.id);
41718     },
41719
41720     /**
41721      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41722      */
41723     disable : function(){
41724         if(this.tabPanel.active != this){
41725             this.disabled = true;
41726             this.status_node.addClass("disabled");
41727         }
41728     },
41729
41730     /**
41731      * Enables this TabPanelItem if it was previously disabled.
41732      */
41733     enable : function(){
41734         this.disabled = false;
41735         this.status_node.removeClass("disabled");
41736     },
41737
41738     /**
41739      * Sets the content for this TabPanelItem.
41740      * @param {String} content The content
41741      * @param {Boolean} loadScripts true to look for and load scripts
41742      */
41743     setContent : function(content, loadScripts){
41744         this.bodyEl.update(content, loadScripts);
41745     },
41746
41747     /**
41748      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41749      * @return {Roo.UpdateManager} The UpdateManager
41750      */
41751     getUpdateManager : function(){
41752         return this.bodyEl.getUpdateManager();
41753     },
41754
41755     /**
41756      * Set a URL to be used to load the content for this TabPanelItem.
41757      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41758      * @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)
41759      * @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)
41760      * @return {Roo.UpdateManager} The UpdateManager
41761      */
41762     setUrl : function(url, params, loadOnce){
41763         if(this.refreshDelegate){
41764             this.un('activate', this.refreshDelegate);
41765         }
41766         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41767         this.on("activate", this.refreshDelegate);
41768         return this.bodyEl.getUpdateManager();
41769     },
41770
41771     /** @private */
41772     _handleRefresh : function(url, params, loadOnce){
41773         if(!loadOnce || !this.loaded){
41774             var updater = this.bodyEl.getUpdateManager();
41775             updater.update(url, params, this._setLoaded.createDelegate(this));
41776         }
41777     },
41778
41779     /**
41780      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41781      *   Will fail silently if the setUrl method has not been called.
41782      *   This does not activate the panel, just updates its content.
41783      */
41784     refresh : function(){
41785         if(this.refreshDelegate){
41786            this.loaded = false;
41787            this.refreshDelegate();
41788         }
41789     },
41790
41791     /** @private */
41792     _setLoaded : function(){
41793         this.loaded = true;
41794     },
41795
41796     /** @private */
41797     closeClick : function(e){
41798         var o = {};
41799         e.stopEvent();
41800         this.fireEvent("beforeclose", this, o);
41801         if(o.cancel !== true){
41802             this.tabPanel.removeTab(this.id);
41803         }
41804     },
41805     /**
41806      * The text displayed in the tooltip for the close icon.
41807      * @type String
41808      */
41809     closeText : "Close this tab"
41810 });
41811 /**
41812 *    This script refer to:
41813 *    Title: International Telephone Input
41814 *    Author: Jack O'Connor
41815 *    Code version:  v12.1.12
41816 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41817 **/
41818
41819 Roo.bootstrap.PhoneInputData = function() {
41820     var d = [
41821       [
41822         "Afghanistan (‫افغانستان‬‎)",
41823         "af",
41824         "93"
41825       ],
41826       [
41827         "Albania (Shqipëri)",
41828         "al",
41829         "355"
41830       ],
41831       [
41832         "Algeria (‫الجزائر‬‎)",
41833         "dz",
41834         "213"
41835       ],
41836       [
41837         "American Samoa",
41838         "as",
41839         "1684"
41840       ],
41841       [
41842         "Andorra",
41843         "ad",
41844         "376"
41845       ],
41846       [
41847         "Angola",
41848         "ao",
41849         "244"
41850       ],
41851       [
41852         "Anguilla",
41853         "ai",
41854         "1264"
41855       ],
41856       [
41857         "Antigua and Barbuda",
41858         "ag",
41859         "1268"
41860       ],
41861       [
41862         "Argentina",
41863         "ar",
41864         "54"
41865       ],
41866       [
41867         "Armenia (Հայաստան)",
41868         "am",
41869         "374"
41870       ],
41871       [
41872         "Aruba",
41873         "aw",
41874         "297"
41875       ],
41876       [
41877         "Australia",
41878         "au",
41879         "61",
41880         0
41881       ],
41882       [
41883         "Austria (Österreich)",
41884         "at",
41885         "43"
41886       ],
41887       [
41888         "Azerbaijan (Azərbaycan)",
41889         "az",
41890         "994"
41891       ],
41892       [
41893         "Bahamas",
41894         "bs",
41895         "1242"
41896       ],
41897       [
41898         "Bahrain (‫البحرين‬‎)",
41899         "bh",
41900         "973"
41901       ],
41902       [
41903         "Bangladesh (বাংলাদেশ)",
41904         "bd",
41905         "880"
41906       ],
41907       [
41908         "Barbados",
41909         "bb",
41910         "1246"
41911       ],
41912       [
41913         "Belarus (Беларусь)",
41914         "by",
41915         "375"
41916       ],
41917       [
41918         "Belgium (België)",
41919         "be",
41920         "32"
41921       ],
41922       [
41923         "Belize",
41924         "bz",
41925         "501"
41926       ],
41927       [
41928         "Benin (Bénin)",
41929         "bj",
41930         "229"
41931       ],
41932       [
41933         "Bermuda",
41934         "bm",
41935         "1441"
41936       ],
41937       [
41938         "Bhutan (འབྲུག)",
41939         "bt",
41940         "975"
41941       ],
41942       [
41943         "Bolivia",
41944         "bo",
41945         "591"
41946       ],
41947       [
41948         "Bosnia and Herzegovina (Босна и Херцеговина)",
41949         "ba",
41950         "387"
41951       ],
41952       [
41953         "Botswana",
41954         "bw",
41955         "267"
41956       ],
41957       [
41958         "Brazil (Brasil)",
41959         "br",
41960         "55"
41961       ],
41962       [
41963         "British Indian Ocean Territory",
41964         "io",
41965         "246"
41966       ],
41967       [
41968         "British Virgin Islands",
41969         "vg",
41970         "1284"
41971       ],
41972       [
41973         "Brunei",
41974         "bn",
41975         "673"
41976       ],
41977       [
41978         "Bulgaria (България)",
41979         "bg",
41980         "359"
41981       ],
41982       [
41983         "Burkina Faso",
41984         "bf",
41985         "226"
41986       ],
41987       [
41988         "Burundi (Uburundi)",
41989         "bi",
41990         "257"
41991       ],
41992       [
41993         "Cambodia (កម្ពុជា)",
41994         "kh",
41995         "855"
41996       ],
41997       [
41998         "Cameroon (Cameroun)",
41999         "cm",
42000         "237"
42001       ],
42002       [
42003         "Canada",
42004         "ca",
42005         "1",
42006         1,
42007         ["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"]
42008       ],
42009       [
42010         "Cape Verde (Kabu Verdi)",
42011         "cv",
42012         "238"
42013       ],
42014       [
42015         "Caribbean Netherlands",
42016         "bq",
42017         "599",
42018         1
42019       ],
42020       [
42021         "Cayman Islands",
42022         "ky",
42023         "1345"
42024       ],
42025       [
42026         "Central African Republic (République centrafricaine)",
42027         "cf",
42028         "236"
42029       ],
42030       [
42031         "Chad (Tchad)",
42032         "td",
42033         "235"
42034       ],
42035       [
42036         "Chile",
42037         "cl",
42038         "56"
42039       ],
42040       [
42041         "China (中国)",
42042         "cn",
42043         "86"
42044       ],
42045       [
42046         "Christmas Island",
42047         "cx",
42048         "61",
42049         2
42050       ],
42051       [
42052         "Cocos (Keeling) Islands",
42053         "cc",
42054         "61",
42055         1
42056       ],
42057       [
42058         "Colombia",
42059         "co",
42060         "57"
42061       ],
42062       [
42063         "Comoros (‫جزر القمر‬‎)",
42064         "km",
42065         "269"
42066       ],
42067       [
42068         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42069         "cd",
42070         "243"
42071       ],
42072       [
42073         "Congo (Republic) (Congo-Brazzaville)",
42074         "cg",
42075         "242"
42076       ],
42077       [
42078         "Cook Islands",
42079         "ck",
42080         "682"
42081       ],
42082       [
42083         "Costa Rica",
42084         "cr",
42085         "506"
42086       ],
42087       [
42088         "Côte d’Ivoire",
42089         "ci",
42090         "225"
42091       ],
42092       [
42093         "Croatia (Hrvatska)",
42094         "hr",
42095         "385"
42096       ],
42097       [
42098         "Cuba",
42099         "cu",
42100         "53"
42101       ],
42102       [
42103         "Curaçao",
42104         "cw",
42105         "599",
42106         0
42107       ],
42108       [
42109         "Cyprus (Κύπρος)",
42110         "cy",
42111         "357"
42112       ],
42113       [
42114         "Czech Republic (Česká republika)",
42115         "cz",
42116         "420"
42117       ],
42118       [
42119         "Denmark (Danmark)",
42120         "dk",
42121         "45"
42122       ],
42123       [
42124         "Djibouti",
42125         "dj",
42126         "253"
42127       ],
42128       [
42129         "Dominica",
42130         "dm",
42131         "1767"
42132       ],
42133       [
42134         "Dominican Republic (República Dominicana)",
42135         "do",
42136         "1",
42137         2,
42138         ["809", "829", "849"]
42139       ],
42140       [
42141         "Ecuador",
42142         "ec",
42143         "593"
42144       ],
42145       [
42146         "Egypt (‫مصر‬‎)",
42147         "eg",
42148         "20"
42149       ],
42150       [
42151         "El Salvador",
42152         "sv",
42153         "503"
42154       ],
42155       [
42156         "Equatorial Guinea (Guinea Ecuatorial)",
42157         "gq",
42158         "240"
42159       ],
42160       [
42161         "Eritrea",
42162         "er",
42163         "291"
42164       ],
42165       [
42166         "Estonia (Eesti)",
42167         "ee",
42168         "372"
42169       ],
42170       [
42171         "Ethiopia",
42172         "et",
42173         "251"
42174       ],
42175       [
42176         "Falkland Islands (Islas Malvinas)",
42177         "fk",
42178         "500"
42179       ],
42180       [
42181         "Faroe Islands (Føroyar)",
42182         "fo",
42183         "298"
42184       ],
42185       [
42186         "Fiji",
42187         "fj",
42188         "679"
42189       ],
42190       [
42191         "Finland (Suomi)",
42192         "fi",
42193         "358",
42194         0
42195       ],
42196       [
42197         "France",
42198         "fr",
42199         "33"
42200       ],
42201       [
42202         "French Guiana (Guyane française)",
42203         "gf",
42204         "594"
42205       ],
42206       [
42207         "French Polynesia (Polynésie française)",
42208         "pf",
42209         "689"
42210       ],
42211       [
42212         "Gabon",
42213         "ga",
42214         "241"
42215       ],
42216       [
42217         "Gambia",
42218         "gm",
42219         "220"
42220       ],
42221       [
42222         "Georgia (საქართველო)",
42223         "ge",
42224         "995"
42225       ],
42226       [
42227         "Germany (Deutschland)",
42228         "de",
42229         "49"
42230       ],
42231       [
42232         "Ghana (Gaana)",
42233         "gh",
42234         "233"
42235       ],
42236       [
42237         "Gibraltar",
42238         "gi",
42239         "350"
42240       ],
42241       [
42242         "Greece (Ελλάδα)",
42243         "gr",
42244         "30"
42245       ],
42246       [
42247         "Greenland (Kalaallit Nunaat)",
42248         "gl",
42249         "299"
42250       ],
42251       [
42252         "Grenada",
42253         "gd",
42254         "1473"
42255       ],
42256       [
42257         "Guadeloupe",
42258         "gp",
42259         "590",
42260         0
42261       ],
42262       [
42263         "Guam",
42264         "gu",
42265         "1671"
42266       ],
42267       [
42268         "Guatemala",
42269         "gt",
42270         "502"
42271       ],
42272       [
42273         "Guernsey",
42274         "gg",
42275         "44",
42276         1
42277       ],
42278       [
42279         "Guinea (Guinée)",
42280         "gn",
42281         "224"
42282       ],
42283       [
42284         "Guinea-Bissau (Guiné Bissau)",
42285         "gw",
42286         "245"
42287       ],
42288       [
42289         "Guyana",
42290         "gy",
42291         "592"
42292       ],
42293       [
42294         "Haiti",
42295         "ht",
42296         "509"
42297       ],
42298       [
42299         "Honduras",
42300         "hn",
42301         "504"
42302       ],
42303       [
42304         "Hong Kong (香港)",
42305         "hk",
42306         "852"
42307       ],
42308       [
42309         "Hungary (Magyarország)",
42310         "hu",
42311         "36"
42312       ],
42313       [
42314         "Iceland (Ísland)",
42315         "is",
42316         "354"
42317       ],
42318       [
42319         "India (भारत)",
42320         "in",
42321         "91"
42322       ],
42323       [
42324         "Indonesia",
42325         "id",
42326         "62"
42327       ],
42328       [
42329         "Iran (‫ایران‬‎)",
42330         "ir",
42331         "98"
42332       ],
42333       [
42334         "Iraq (‫العراق‬‎)",
42335         "iq",
42336         "964"
42337       ],
42338       [
42339         "Ireland",
42340         "ie",
42341         "353"
42342       ],
42343       [
42344         "Isle of Man",
42345         "im",
42346         "44",
42347         2
42348       ],
42349       [
42350         "Israel (‫ישראל‬‎)",
42351         "il",
42352         "972"
42353       ],
42354       [
42355         "Italy (Italia)",
42356         "it",
42357         "39",
42358         0
42359       ],
42360       [
42361         "Jamaica",
42362         "jm",
42363         "1876"
42364       ],
42365       [
42366         "Japan (日本)",
42367         "jp",
42368         "81"
42369       ],
42370       [
42371         "Jersey",
42372         "je",
42373         "44",
42374         3
42375       ],
42376       [
42377         "Jordan (‫الأردن‬‎)",
42378         "jo",
42379         "962"
42380       ],
42381       [
42382         "Kazakhstan (Казахстан)",
42383         "kz",
42384         "7",
42385         1
42386       ],
42387       [
42388         "Kenya",
42389         "ke",
42390         "254"
42391       ],
42392       [
42393         "Kiribati",
42394         "ki",
42395         "686"
42396       ],
42397       [
42398         "Kosovo",
42399         "xk",
42400         "383"
42401       ],
42402       [
42403         "Kuwait (‫الكويت‬‎)",
42404         "kw",
42405         "965"
42406       ],
42407       [
42408         "Kyrgyzstan (Кыргызстан)",
42409         "kg",
42410         "996"
42411       ],
42412       [
42413         "Laos (ລາວ)",
42414         "la",
42415         "856"
42416       ],
42417       [
42418         "Latvia (Latvija)",
42419         "lv",
42420         "371"
42421       ],
42422       [
42423         "Lebanon (‫لبنان‬‎)",
42424         "lb",
42425         "961"
42426       ],
42427       [
42428         "Lesotho",
42429         "ls",
42430         "266"
42431       ],
42432       [
42433         "Liberia",
42434         "lr",
42435         "231"
42436       ],
42437       [
42438         "Libya (‫ليبيا‬‎)",
42439         "ly",
42440         "218"
42441       ],
42442       [
42443         "Liechtenstein",
42444         "li",
42445         "423"
42446       ],
42447       [
42448         "Lithuania (Lietuva)",
42449         "lt",
42450         "370"
42451       ],
42452       [
42453         "Luxembourg",
42454         "lu",
42455         "352"
42456       ],
42457       [
42458         "Macau (澳門)",
42459         "mo",
42460         "853"
42461       ],
42462       [
42463         "Macedonia (FYROM) (Македонија)",
42464         "mk",
42465         "389"
42466       ],
42467       [
42468         "Madagascar (Madagasikara)",
42469         "mg",
42470         "261"
42471       ],
42472       [
42473         "Malawi",
42474         "mw",
42475         "265"
42476       ],
42477       [
42478         "Malaysia",
42479         "my",
42480         "60"
42481       ],
42482       [
42483         "Maldives",
42484         "mv",
42485         "960"
42486       ],
42487       [
42488         "Mali",
42489         "ml",
42490         "223"
42491       ],
42492       [
42493         "Malta",
42494         "mt",
42495         "356"
42496       ],
42497       [
42498         "Marshall Islands",
42499         "mh",
42500         "692"
42501       ],
42502       [
42503         "Martinique",
42504         "mq",
42505         "596"
42506       ],
42507       [
42508         "Mauritania (‫موريتانيا‬‎)",
42509         "mr",
42510         "222"
42511       ],
42512       [
42513         "Mauritius (Moris)",
42514         "mu",
42515         "230"
42516       ],
42517       [
42518         "Mayotte",
42519         "yt",
42520         "262",
42521         1
42522       ],
42523       [
42524         "Mexico (México)",
42525         "mx",
42526         "52"
42527       ],
42528       [
42529         "Micronesia",
42530         "fm",
42531         "691"
42532       ],
42533       [
42534         "Moldova (Republica Moldova)",
42535         "md",
42536         "373"
42537       ],
42538       [
42539         "Monaco",
42540         "mc",
42541         "377"
42542       ],
42543       [
42544         "Mongolia (Монгол)",
42545         "mn",
42546         "976"
42547       ],
42548       [
42549         "Montenegro (Crna Gora)",
42550         "me",
42551         "382"
42552       ],
42553       [
42554         "Montserrat",
42555         "ms",
42556         "1664"
42557       ],
42558       [
42559         "Morocco (‫المغرب‬‎)",
42560         "ma",
42561         "212",
42562         0
42563       ],
42564       [
42565         "Mozambique (Moçambique)",
42566         "mz",
42567         "258"
42568       ],
42569       [
42570         "Myanmar (Burma) (မြန်မာ)",
42571         "mm",
42572         "95"
42573       ],
42574       [
42575         "Namibia (Namibië)",
42576         "na",
42577         "264"
42578       ],
42579       [
42580         "Nauru",
42581         "nr",
42582         "674"
42583       ],
42584       [
42585         "Nepal (नेपाल)",
42586         "np",
42587         "977"
42588       ],
42589       [
42590         "Netherlands (Nederland)",
42591         "nl",
42592         "31"
42593       ],
42594       [
42595         "New Caledonia (Nouvelle-Calédonie)",
42596         "nc",
42597         "687"
42598       ],
42599       [
42600         "New Zealand",
42601         "nz",
42602         "64"
42603       ],
42604       [
42605         "Nicaragua",
42606         "ni",
42607         "505"
42608       ],
42609       [
42610         "Niger (Nijar)",
42611         "ne",
42612         "227"
42613       ],
42614       [
42615         "Nigeria",
42616         "ng",
42617         "234"
42618       ],
42619       [
42620         "Niue",
42621         "nu",
42622         "683"
42623       ],
42624       [
42625         "Norfolk Island",
42626         "nf",
42627         "672"
42628       ],
42629       [
42630         "North Korea (조선 민주주의 인민 공화국)",
42631         "kp",
42632         "850"
42633       ],
42634       [
42635         "Northern Mariana Islands",
42636         "mp",
42637         "1670"
42638       ],
42639       [
42640         "Norway (Norge)",
42641         "no",
42642         "47",
42643         0
42644       ],
42645       [
42646         "Oman (‫عُمان‬‎)",
42647         "om",
42648         "968"
42649       ],
42650       [
42651         "Pakistan (‫پاکستان‬‎)",
42652         "pk",
42653         "92"
42654       ],
42655       [
42656         "Palau",
42657         "pw",
42658         "680"
42659       ],
42660       [
42661         "Palestine (‫فلسطين‬‎)",
42662         "ps",
42663         "970"
42664       ],
42665       [
42666         "Panama (Panamá)",
42667         "pa",
42668         "507"
42669       ],
42670       [
42671         "Papua New Guinea",
42672         "pg",
42673         "675"
42674       ],
42675       [
42676         "Paraguay",
42677         "py",
42678         "595"
42679       ],
42680       [
42681         "Peru (Perú)",
42682         "pe",
42683         "51"
42684       ],
42685       [
42686         "Philippines",
42687         "ph",
42688         "63"
42689       ],
42690       [
42691         "Poland (Polska)",
42692         "pl",
42693         "48"
42694       ],
42695       [
42696         "Portugal",
42697         "pt",
42698         "351"
42699       ],
42700       [
42701         "Puerto Rico",
42702         "pr",
42703         "1",
42704         3,
42705         ["787", "939"]
42706       ],
42707       [
42708         "Qatar (‫قطر‬‎)",
42709         "qa",
42710         "974"
42711       ],
42712       [
42713         "Réunion (La Réunion)",
42714         "re",
42715         "262",
42716         0
42717       ],
42718       [
42719         "Romania (România)",
42720         "ro",
42721         "40"
42722       ],
42723       [
42724         "Russia (Россия)",
42725         "ru",
42726         "7",
42727         0
42728       ],
42729       [
42730         "Rwanda",
42731         "rw",
42732         "250"
42733       ],
42734       [
42735         "Saint Barthélemy",
42736         "bl",
42737         "590",
42738         1
42739       ],
42740       [
42741         "Saint Helena",
42742         "sh",
42743         "290"
42744       ],
42745       [
42746         "Saint Kitts and Nevis",
42747         "kn",
42748         "1869"
42749       ],
42750       [
42751         "Saint Lucia",
42752         "lc",
42753         "1758"
42754       ],
42755       [
42756         "Saint Martin (Saint-Martin (partie française))",
42757         "mf",
42758         "590",
42759         2
42760       ],
42761       [
42762         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42763         "pm",
42764         "508"
42765       ],
42766       [
42767         "Saint Vincent and the Grenadines",
42768         "vc",
42769         "1784"
42770       ],
42771       [
42772         "Samoa",
42773         "ws",
42774         "685"
42775       ],
42776       [
42777         "San Marino",
42778         "sm",
42779         "378"
42780       ],
42781       [
42782         "São Tomé and Príncipe (São Tomé e Príncipe)",
42783         "st",
42784         "239"
42785       ],
42786       [
42787         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42788         "sa",
42789         "966"
42790       ],
42791       [
42792         "Senegal (Sénégal)",
42793         "sn",
42794         "221"
42795       ],
42796       [
42797         "Serbia (Србија)",
42798         "rs",
42799         "381"
42800       ],
42801       [
42802         "Seychelles",
42803         "sc",
42804         "248"
42805       ],
42806       [
42807         "Sierra Leone",
42808         "sl",
42809         "232"
42810       ],
42811       [
42812         "Singapore",
42813         "sg",
42814         "65"
42815       ],
42816       [
42817         "Sint Maarten",
42818         "sx",
42819         "1721"
42820       ],
42821       [
42822         "Slovakia (Slovensko)",
42823         "sk",
42824         "421"
42825       ],
42826       [
42827         "Slovenia (Slovenija)",
42828         "si",
42829         "386"
42830       ],
42831       [
42832         "Solomon Islands",
42833         "sb",
42834         "677"
42835       ],
42836       [
42837         "Somalia (Soomaaliya)",
42838         "so",
42839         "252"
42840       ],
42841       [
42842         "South Africa",
42843         "za",
42844         "27"
42845       ],
42846       [
42847         "South Korea (대한민국)",
42848         "kr",
42849         "82"
42850       ],
42851       [
42852         "South Sudan (‫جنوب السودان‬‎)",
42853         "ss",
42854         "211"
42855       ],
42856       [
42857         "Spain (España)",
42858         "es",
42859         "34"
42860       ],
42861       [
42862         "Sri Lanka (ශ්‍රී ලංකාව)",
42863         "lk",
42864         "94"
42865       ],
42866       [
42867         "Sudan (‫السودان‬‎)",
42868         "sd",
42869         "249"
42870       ],
42871       [
42872         "Suriname",
42873         "sr",
42874         "597"
42875       ],
42876       [
42877         "Svalbard and Jan Mayen",
42878         "sj",
42879         "47",
42880         1
42881       ],
42882       [
42883         "Swaziland",
42884         "sz",
42885         "268"
42886       ],
42887       [
42888         "Sweden (Sverige)",
42889         "se",
42890         "46"
42891       ],
42892       [
42893         "Switzerland (Schweiz)",
42894         "ch",
42895         "41"
42896       ],
42897       [
42898         "Syria (‫سوريا‬‎)",
42899         "sy",
42900         "963"
42901       ],
42902       [
42903         "Taiwan (台灣)",
42904         "tw",
42905         "886"
42906       ],
42907       [
42908         "Tajikistan",
42909         "tj",
42910         "992"
42911       ],
42912       [
42913         "Tanzania",
42914         "tz",
42915         "255"
42916       ],
42917       [
42918         "Thailand (ไทย)",
42919         "th",
42920         "66"
42921       ],
42922       [
42923         "Timor-Leste",
42924         "tl",
42925         "670"
42926       ],
42927       [
42928         "Togo",
42929         "tg",
42930         "228"
42931       ],
42932       [
42933         "Tokelau",
42934         "tk",
42935         "690"
42936       ],
42937       [
42938         "Tonga",
42939         "to",
42940         "676"
42941       ],
42942       [
42943         "Trinidad and Tobago",
42944         "tt",
42945         "1868"
42946       ],
42947       [
42948         "Tunisia (‫تونس‬‎)",
42949         "tn",
42950         "216"
42951       ],
42952       [
42953         "Turkey (Türkiye)",
42954         "tr",
42955         "90"
42956       ],
42957       [
42958         "Turkmenistan",
42959         "tm",
42960         "993"
42961       ],
42962       [
42963         "Turks and Caicos Islands",
42964         "tc",
42965         "1649"
42966       ],
42967       [
42968         "Tuvalu",
42969         "tv",
42970         "688"
42971       ],
42972       [
42973         "U.S. Virgin Islands",
42974         "vi",
42975         "1340"
42976       ],
42977       [
42978         "Uganda",
42979         "ug",
42980         "256"
42981       ],
42982       [
42983         "Ukraine (Україна)",
42984         "ua",
42985         "380"
42986       ],
42987       [
42988         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42989         "ae",
42990         "971"
42991       ],
42992       [
42993         "United Kingdom",
42994         "gb",
42995         "44",
42996         0
42997       ],
42998       [
42999         "United States",
43000         "us",
43001         "1",
43002         0
43003       ],
43004       [
43005         "Uruguay",
43006         "uy",
43007         "598"
43008       ],
43009       [
43010         "Uzbekistan (Oʻzbekiston)",
43011         "uz",
43012         "998"
43013       ],
43014       [
43015         "Vanuatu",
43016         "vu",
43017         "678"
43018       ],
43019       [
43020         "Vatican City (Città del Vaticano)",
43021         "va",
43022         "39",
43023         1
43024       ],
43025       [
43026         "Venezuela",
43027         "ve",
43028         "58"
43029       ],
43030       [
43031         "Vietnam (Việt Nam)",
43032         "vn",
43033         "84"
43034       ],
43035       [
43036         "Wallis and Futuna (Wallis-et-Futuna)",
43037         "wf",
43038         "681"
43039       ],
43040       [
43041         "Western Sahara (‫الصحراء الغربية‬‎)",
43042         "eh",
43043         "212",
43044         1
43045       ],
43046       [
43047         "Yemen (‫اليمن‬‎)",
43048         "ye",
43049         "967"
43050       ],
43051       [
43052         "Zambia",
43053         "zm",
43054         "260"
43055       ],
43056       [
43057         "Zimbabwe",
43058         "zw",
43059         "263"
43060       ],
43061       [
43062         "Åland Islands",
43063         "ax",
43064         "358",
43065         1
43066       ]
43067   ];
43068   
43069   return d;
43070 }/**
43071 *    This script refer to:
43072 *    Title: International Telephone Input
43073 *    Author: Jack O'Connor
43074 *    Code version:  v12.1.12
43075 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43076 **/
43077
43078 /**
43079  * @class Roo.bootstrap.PhoneInput
43080  * @extends Roo.bootstrap.TriggerField
43081  * An input with International dial-code selection
43082  
43083  * @cfg {String} defaultDialCode default '+852'
43084  * @cfg {Array} preferedCountries default []
43085   
43086  * @constructor
43087  * Create a new PhoneInput.
43088  * @param {Object} config Configuration options
43089  */
43090
43091 Roo.bootstrap.PhoneInput = function(config) {
43092     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43093 };
43094
43095 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43096         
43097         listWidth: undefined,
43098         
43099         selectedClass: 'active',
43100         
43101         invalidClass : "has-warning",
43102         
43103         validClass: 'has-success',
43104         
43105         allowed: '0123456789',
43106         
43107         max_length: 15,
43108         
43109         /**
43110          * @cfg {String} defaultDialCode The default dial code when initializing the input
43111          */
43112         defaultDialCode: '+852',
43113         
43114         /**
43115          * @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
43116          */
43117         preferedCountries: false,
43118         
43119         getAutoCreate : function()
43120         {
43121             var data = Roo.bootstrap.PhoneInputData();
43122             var align = this.labelAlign || this.parentLabelAlign();
43123             var id = Roo.id();
43124             
43125             this.allCountries = [];
43126             this.dialCodeMapping = [];
43127             
43128             for (var i = 0; i < data.length; i++) {
43129               var c = data[i];
43130               this.allCountries[i] = {
43131                 name: c[0],
43132                 iso2: c[1],
43133                 dialCode: c[2],
43134                 priority: c[3] || 0,
43135                 areaCodes: c[4] || null
43136               };
43137               this.dialCodeMapping[c[2]] = {
43138                   name: c[0],
43139                   iso2: c[1],
43140                   priority: c[3] || 0,
43141                   areaCodes: c[4] || null
43142               };
43143             }
43144             
43145             var cfg = {
43146                 cls: 'form-group',
43147                 cn: []
43148             };
43149             
43150             var input =  {
43151                 tag: 'input',
43152                 id : id,
43153                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43154                 maxlength: this.max_length,
43155                 cls : 'form-control tel-input',
43156                 autocomplete: 'new-password'
43157             };
43158             
43159             var hiddenInput = {
43160                 tag: 'input',
43161                 type: 'hidden',
43162                 cls: 'hidden-tel-input'
43163             };
43164             
43165             if (this.name) {
43166                 hiddenInput.name = this.name;
43167             }
43168             
43169             if (this.disabled) {
43170                 input.disabled = true;
43171             }
43172             
43173             var flag_container = {
43174                 tag: 'div',
43175                 cls: 'flag-box',
43176                 cn: [
43177                     {
43178                         tag: 'div',
43179                         cls: 'flag'
43180                     },
43181                     {
43182                         tag: 'div',
43183                         cls: 'caret'
43184                     }
43185                 ]
43186             };
43187             
43188             var box = {
43189                 tag: 'div',
43190                 cls: this.hasFeedback ? 'has-feedback' : '',
43191                 cn: [
43192                     hiddenInput,
43193                     input,
43194                     {
43195                         tag: 'input',
43196                         cls: 'dial-code-holder',
43197                         disabled: true
43198                     }
43199                 ]
43200             };
43201             
43202             var container = {
43203                 cls: 'roo-select2-container input-group',
43204                 cn: [
43205                     flag_container,
43206                     box
43207                 ]
43208             };
43209             
43210             if (this.fieldLabel.length) {
43211                 var indicator = {
43212                     tag: 'i',
43213                     tooltip: 'This field is required'
43214                 };
43215                 
43216                 var label = {
43217                     tag: 'label',
43218                     'for':  id,
43219                     cls: 'control-label',
43220                     cn: []
43221                 };
43222                 
43223                 var label_text = {
43224                     tag: 'span',
43225                     html: this.fieldLabel
43226                 };
43227                 
43228                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43229                 label.cn = [
43230                     indicator,
43231                     label_text
43232                 ];
43233                 
43234                 if(this.indicatorpos == 'right') {
43235                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43236                     label.cn = [
43237                         label_text,
43238                         indicator
43239                     ];
43240                 }
43241                 
43242                 if(align == 'left') {
43243                     container = {
43244                         tag: 'div',
43245                         cn: [
43246                             container
43247                         ]
43248                     };
43249                     
43250                     if(this.labelWidth > 12){
43251                         label.style = "width: " + this.labelWidth + 'px';
43252                     }
43253                     if(this.labelWidth < 13 && this.labelmd == 0){
43254                         this.labelmd = this.labelWidth;
43255                     }
43256                     if(this.labellg > 0){
43257                         label.cls += ' col-lg-' + this.labellg;
43258                         input.cls += ' col-lg-' + (12 - this.labellg);
43259                     }
43260                     if(this.labelmd > 0){
43261                         label.cls += ' col-md-' + this.labelmd;
43262                         container.cls += ' col-md-' + (12 - this.labelmd);
43263                     }
43264                     if(this.labelsm > 0){
43265                         label.cls += ' col-sm-' + this.labelsm;
43266                         container.cls += ' col-sm-' + (12 - this.labelsm);
43267                     }
43268                     if(this.labelxs > 0){
43269                         label.cls += ' col-xs-' + this.labelxs;
43270                         container.cls += ' col-xs-' + (12 - this.labelxs);
43271                     }
43272                 }
43273             }
43274             
43275             cfg.cn = [
43276                 label,
43277                 container
43278             ];
43279             
43280             var settings = this;
43281             
43282             ['xs','sm','md','lg'].map(function(size){
43283                 if (settings[size]) {
43284                     cfg.cls += ' col-' + size + '-' + settings[size];
43285                 }
43286             });
43287             
43288             this.store = new Roo.data.Store({
43289                 proxy : new Roo.data.MemoryProxy({}),
43290                 reader : new Roo.data.JsonReader({
43291                     fields : [
43292                         {
43293                             'name' : 'name',
43294                             'type' : 'string'
43295                         },
43296                         {
43297                             'name' : 'iso2',
43298                             'type' : 'string'
43299                         },
43300                         {
43301                             'name' : 'dialCode',
43302                             'type' : 'string'
43303                         },
43304                         {
43305                             'name' : 'priority',
43306                             'type' : 'string'
43307                         },
43308                         {
43309                             'name' : 'areaCodes',
43310                             'type' : 'string'
43311                         }
43312                     ]
43313                 })
43314             });
43315             
43316             if(!this.preferedCountries) {
43317                 this.preferedCountries = [
43318                     'hk',
43319                     'gb',
43320                     'us'
43321                 ];
43322             }
43323             
43324             var p = this.preferedCountries.reverse();
43325             
43326             if(p) {
43327                 for (var i = 0; i < p.length; i++) {
43328                     for (var j = 0; j < this.allCountries.length; j++) {
43329                         if(this.allCountries[j].iso2 == p[i]) {
43330                             var t = this.allCountries[j];
43331                             this.allCountries.splice(j,1);
43332                             this.allCountries.unshift(t);
43333                         }
43334                     } 
43335                 }
43336             }
43337             
43338             this.store.proxy.data = {
43339                 success: true,
43340                 data: this.allCountries
43341             };
43342             
43343             return cfg;
43344         },
43345         
43346         initEvents : function()
43347         {
43348             this.createList();
43349             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43350             
43351             this.indicator = this.indicatorEl();
43352             this.flag = this.flagEl();
43353             this.dialCodeHolder = this.dialCodeHolderEl();
43354             
43355             this.trigger = this.el.select('div.flag-box',true).first();
43356             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43357             
43358             var _this = this;
43359             
43360             (function(){
43361                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43362                 _this.list.setWidth(lw);
43363             }).defer(100);
43364             
43365             this.list.on('mouseover', this.onViewOver, this);
43366             this.list.on('mousemove', this.onViewMove, this);
43367             this.inputEl().on("keyup", this.onKeyUp, this);
43368             this.inputEl().on("keypress", this.onKeyPress, this);
43369             
43370             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43371
43372             this.view = new Roo.View(this.list, this.tpl, {
43373                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43374             });
43375             
43376             this.view.on('click', this.onViewClick, this);
43377             this.setValue(this.defaultDialCode);
43378         },
43379         
43380         onTriggerClick : function(e)
43381         {
43382             Roo.log('trigger click');
43383             if(this.disabled){
43384                 return;
43385             }
43386             
43387             if(this.isExpanded()){
43388                 this.collapse();
43389                 this.hasFocus = false;
43390             }else {
43391                 this.store.load({});
43392                 this.hasFocus = true;
43393                 this.expand();
43394             }
43395         },
43396         
43397         isExpanded : function()
43398         {
43399             return this.list.isVisible();
43400         },
43401         
43402         collapse : function()
43403         {
43404             if(!this.isExpanded()){
43405                 return;
43406             }
43407             this.list.hide();
43408             Roo.get(document).un('mousedown', this.collapseIf, this);
43409             Roo.get(document).un('mousewheel', this.collapseIf, this);
43410             this.fireEvent('collapse', this);
43411             this.validate();
43412         },
43413         
43414         expand : function()
43415         {
43416             Roo.log('expand');
43417
43418             if(this.isExpanded() || !this.hasFocus){
43419                 return;
43420             }
43421             
43422             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43423             this.list.setWidth(lw);
43424             
43425             this.list.show();
43426             this.restrictHeight();
43427             
43428             Roo.get(document).on('mousedown', this.collapseIf, this);
43429             Roo.get(document).on('mousewheel', this.collapseIf, this);
43430             
43431             this.fireEvent('expand', this);
43432         },
43433         
43434         restrictHeight : function()
43435         {
43436             this.list.alignTo(this.inputEl(), this.listAlign);
43437             this.list.alignTo(this.inputEl(), this.listAlign);
43438         },
43439         
43440         onViewOver : function(e, t)
43441         {
43442             if(this.inKeyMode){
43443                 return;
43444             }
43445             var item = this.view.findItemFromChild(t);
43446             
43447             if(item){
43448                 var index = this.view.indexOf(item);
43449                 this.select(index, false);
43450             }
43451         },
43452
43453         // private
43454         onViewClick : function(view, doFocus, el, e)
43455         {
43456             var index = this.view.getSelectedIndexes()[0];
43457             
43458             var r = this.store.getAt(index);
43459             
43460             if(r){
43461                 this.onSelect(r, index);
43462             }
43463             if(doFocus !== false && !this.blockFocus){
43464                 this.inputEl().focus();
43465             }
43466         },
43467         
43468         onViewMove : function(e, t)
43469         {
43470             this.inKeyMode = false;
43471         },
43472         
43473         select : function(index, scrollIntoView)
43474         {
43475             this.selectedIndex = index;
43476             this.view.select(index);
43477             if(scrollIntoView !== false){
43478                 var el = this.view.getNode(index);
43479                 if(el){
43480                     this.list.scrollChildIntoView(el, false);
43481                 }
43482             }
43483         },
43484         
43485         createList : function()
43486         {
43487             this.list = Roo.get(document.body).createChild({
43488                 tag: 'ul',
43489                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43490                 style: 'display:none'
43491             });
43492             
43493             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43494         },
43495         
43496         collapseIf : function(e)
43497         {
43498             var in_combo  = e.within(this.el);
43499             var in_list =  e.within(this.list);
43500             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43501             
43502             if (in_combo || in_list || is_list) {
43503                 return;
43504             }
43505             this.collapse();
43506         },
43507         
43508         onSelect : function(record, index)
43509         {
43510             if(this.fireEvent('beforeselect', this, record, index) !== false){
43511                 
43512                 this.setFlagClass(record.data.iso2);
43513                 this.setDialCode(record.data.dialCode);
43514                 this.hasFocus = false;
43515                 this.collapse();
43516                 this.fireEvent('select', this, record, index);
43517             }
43518         },
43519         
43520         flagEl : function()
43521         {
43522             var flag = this.el.select('div.flag',true).first();
43523             if(!flag){
43524                 return false;
43525             }
43526             return flag;
43527         },
43528         
43529         dialCodeHolderEl : function()
43530         {
43531             var d = this.el.select('input.dial-code-holder',true).first();
43532             if(!d){
43533                 return false;
43534             }
43535             return d;
43536         },
43537         
43538         setDialCode : function(v)
43539         {
43540             this.dialCodeHolder.dom.value = '+'+v;
43541         },
43542         
43543         setFlagClass : function(n)
43544         {
43545             this.flag.dom.className = 'flag '+n;
43546         },
43547         
43548         getValue : function()
43549         {
43550             var v = this.inputEl().getValue();
43551             if(this.dialCodeHolder) {
43552                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43553             }
43554             return v;
43555         },
43556         
43557         setValue : function(v)
43558         {
43559             var d = this.getDialCode(v);
43560             
43561             //invalid dial code
43562             if(v.length == 0 || !d || d.length == 0) {
43563                 if(this.rendered){
43564                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43565                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43566                 }
43567                 return;
43568             }
43569             
43570             //valid dial code
43571             this.setFlagClass(this.dialCodeMapping[d].iso2);
43572             this.setDialCode(d);
43573             this.inputEl().dom.value = v.replace('+'+d,'');
43574             this.hiddenEl().dom.value = this.getValue();
43575             
43576             this.validate();
43577         },
43578         
43579         getDialCode : function(v)
43580         {
43581             v = v ||  '';
43582             
43583             if (v.length == 0) {
43584                 return this.dialCodeHolder.dom.value;
43585             }
43586             
43587             var dialCode = "";
43588             if (v.charAt(0) != "+") {
43589                 return false;
43590             }
43591             var numericChars = "";
43592             for (var i = 1; i < v.length; i++) {
43593               var c = v.charAt(i);
43594               if (!isNaN(c)) {
43595                 numericChars += c;
43596                 if (this.dialCodeMapping[numericChars]) {
43597                   dialCode = v.substr(1, i);
43598                 }
43599                 if (numericChars.length == 4) {
43600                   break;
43601                 }
43602               }
43603             }
43604             return dialCode;
43605         },
43606         
43607         reset : function()
43608         {
43609             this.setValue(this.defaultDialCode);
43610             this.validate();
43611         },
43612         
43613         hiddenEl : function()
43614         {
43615             return this.el.select('input.hidden-tel-input',true).first();
43616         },
43617         
43618         // after setting val
43619         onKeyUp : function(e){
43620             this.setValue(this.getValue());
43621         },
43622         
43623         onKeyPress : function(e){
43624             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43625                 e.stopEvent();
43626             }
43627         }
43628         
43629 });
43630 /**
43631  * @class Roo.bootstrap.MoneyField
43632  * @extends Roo.bootstrap.ComboBox
43633  * Bootstrap MoneyField class
43634  * 
43635  * @constructor
43636  * Create a new MoneyField.
43637  * @param {Object} config Configuration options
43638  */
43639
43640 Roo.bootstrap.MoneyField = function(config) {
43641     
43642     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43643     
43644 };
43645
43646 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43647     
43648     /**
43649      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43650      */
43651     allowDecimals : true,
43652     /**
43653      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43654      */
43655     decimalSeparator : ".",
43656     /**
43657      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43658      */
43659     decimalPrecision : 0,
43660     /**
43661      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43662      */
43663     allowNegative : true,
43664     /**
43665      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43666      */
43667     allowZero: true,
43668     /**
43669      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43670      */
43671     minValue : Number.NEGATIVE_INFINITY,
43672     /**
43673      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43674      */
43675     maxValue : Number.MAX_VALUE,
43676     /**
43677      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43678      */
43679     minText : "The minimum value for this field is {0}",
43680     /**
43681      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43682      */
43683     maxText : "The maximum value for this field is {0}",
43684     /**
43685      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43686      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43687      */
43688     nanText : "{0} is not a valid number",
43689     /**
43690      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43691      */
43692     castInt : true,
43693     /**
43694      * @cfg {String} defaults currency of the MoneyField
43695      * value should be in lkey
43696      */
43697     defaultCurrency : false,
43698     /**
43699      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43700      */
43701     thousandsDelimiter : false,
43702     /**
43703      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43704      */
43705     max_length: false,
43706     
43707     inputlg : 9,
43708     inputmd : 9,
43709     inputsm : 9,
43710     inputxs : 6,
43711     
43712     store : false,
43713     
43714     getAutoCreate : function()
43715     {
43716         var align = this.labelAlign || this.parentLabelAlign();
43717         
43718         var id = Roo.id();
43719
43720         var cfg = {
43721             cls: 'form-group',
43722             cn: []
43723         };
43724
43725         var input =  {
43726             tag: 'input',
43727             id : id,
43728             cls : 'form-control roo-money-amount-input',
43729             autocomplete: 'new-password'
43730         };
43731         
43732         var hiddenInput = {
43733             tag: 'input',
43734             type: 'hidden',
43735             id: Roo.id(),
43736             cls: 'hidden-number-input'
43737         };
43738         
43739         if(this.max_length) {
43740             input.maxlength = this.max_length; 
43741         }
43742         
43743         if (this.name) {
43744             hiddenInput.name = this.name;
43745         }
43746
43747         if (this.disabled) {
43748             input.disabled = true;
43749         }
43750
43751         var clg = 12 - this.inputlg;
43752         var cmd = 12 - this.inputmd;
43753         var csm = 12 - this.inputsm;
43754         var cxs = 12 - this.inputxs;
43755         
43756         var container = {
43757             tag : 'div',
43758             cls : 'row roo-money-field',
43759             cn : [
43760                 {
43761                     tag : 'div',
43762                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43763                     cn : [
43764                         {
43765                             tag : 'div',
43766                             cls: 'roo-select2-container input-group',
43767                             cn: [
43768                                 {
43769                                     tag : 'input',
43770                                     cls : 'form-control roo-money-currency-input',
43771                                     autocomplete: 'new-password',
43772                                     readOnly : 1,
43773                                     name : this.currencyName
43774                                 },
43775                                 {
43776                                     tag :'span',
43777                                     cls : 'input-group-addon',
43778                                     cn : [
43779                                         {
43780                                             tag: 'span',
43781                                             cls: 'caret'
43782                                         }
43783                                     ]
43784                                 }
43785                             ]
43786                         }
43787                     ]
43788                 },
43789                 {
43790                     tag : 'div',
43791                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43792                     cn : [
43793                         {
43794                             tag: 'div',
43795                             cls: this.hasFeedback ? 'has-feedback' : '',
43796                             cn: [
43797                                 input
43798                             ]
43799                         }
43800                     ]
43801                 }
43802             ]
43803             
43804         };
43805         
43806         if (this.fieldLabel.length) {
43807             var indicator = {
43808                 tag: 'i',
43809                 tooltip: 'This field is required'
43810             };
43811
43812             var label = {
43813                 tag: 'label',
43814                 'for':  id,
43815                 cls: 'control-label',
43816                 cn: []
43817             };
43818
43819             var label_text = {
43820                 tag: 'span',
43821                 html: this.fieldLabel
43822             };
43823
43824             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43825             label.cn = [
43826                 indicator,
43827                 label_text
43828             ];
43829
43830             if(this.indicatorpos == 'right') {
43831                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43832                 label.cn = [
43833                     label_text,
43834                     indicator
43835                 ];
43836             }
43837
43838             if(align == 'left') {
43839                 container = {
43840                     tag: 'div',
43841                     cn: [
43842                         container
43843                     ]
43844                 };
43845
43846                 if(this.labelWidth > 12){
43847                     label.style = "width: " + this.labelWidth + 'px';
43848                 }
43849                 if(this.labelWidth < 13 && this.labelmd == 0){
43850                     this.labelmd = this.labelWidth;
43851                 }
43852                 if(this.labellg > 0){
43853                     label.cls += ' col-lg-' + this.labellg;
43854                     input.cls += ' col-lg-' + (12 - this.labellg);
43855                 }
43856                 if(this.labelmd > 0){
43857                     label.cls += ' col-md-' + this.labelmd;
43858                     container.cls += ' col-md-' + (12 - this.labelmd);
43859                 }
43860                 if(this.labelsm > 0){
43861                     label.cls += ' col-sm-' + this.labelsm;
43862                     container.cls += ' col-sm-' + (12 - this.labelsm);
43863                 }
43864                 if(this.labelxs > 0){
43865                     label.cls += ' col-xs-' + this.labelxs;
43866                     container.cls += ' col-xs-' + (12 - this.labelxs);
43867                 }
43868             }
43869         }
43870
43871         cfg.cn = [
43872             label,
43873             container,
43874             hiddenInput
43875         ];
43876         
43877         var settings = this;
43878
43879         ['xs','sm','md','lg'].map(function(size){
43880             if (settings[size]) {
43881                 cfg.cls += ' col-' + size + '-' + settings[size];
43882             }
43883         });
43884         
43885         return cfg;
43886     },
43887     
43888     initEvents : function()
43889     {
43890         this.indicator = this.indicatorEl();
43891         
43892         this.initCurrencyEvent();
43893         
43894         this.initNumberEvent();
43895     },
43896     
43897     initCurrencyEvent : function()
43898     {
43899         if (!this.store) {
43900             throw "can not find store for combo";
43901         }
43902         
43903         this.store = Roo.factory(this.store, Roo.data);
43904         this.store.parent = this;
43905         
43906         this.createList();
43907         
43908         this.triggerEl = this.el.select('.input-group-addon', true).first();
43909         
43910         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43911         
43912         var _this = this;
43913         
43914         (function(){
43915             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43916             _this.list.setWidth(lw);
43917         }).defer(100);
43918         
43919         this.list.on('mouseover', this.onViewOver, this);
43920         this.list.on('mousemove', this.onViewMove, this);
43921         this.list.on('scroll', this.onViewScroll, this);
43922         
43923         if(!this.tpl){
43924             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43925         }
43926         
43927         this.view = new Roo.View(this.list, this.tpl, {
43928             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43929         });
43930         
43931         this.view.on('click', this.onViewClick, this);
43932         
43933         this.store.on('beforeload', this.onBeforeLoad, this);
43934         this.store.on('load', this.onLoad, this);
43935         this.store.on('loadexception', this.onLoadException, this);
43936         
43937         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43938             "up" : function(e){
43939                 this.inKeyMode = true;
43940                 this.selectPrev();
43941             },
43942
43943             "down" : function(e){
43944                 if(!this.isExpanded()){
43945                     this.onTriggerClick();
43946                 }else{
43947                     this.inKeyMode = true;
43948                     this.selectNext();
43949                 }
43950             },
43951
43952             "enter" : function(e){
43953                 this.collapse();
43954                 
43955                 if(this.fireEvent("specialkey", this, e)){
43956                     this.onViewClick(false);
43957                 }
43958                 
43959                 return true;
43960             },
43961
43962             "esc" : function(e){
43963                 this.collapse();
43964             },
43965
43966             "tab" : function(e){
43967                 this.collapse();
43968                 
43969                 if(this.fireEvent("specialkey", this, e)){
43970                     this.onViewClick(false);
43971                 }
43972                 
43973                 return true;
43974             },
43975
43976             scope : this,
43977
43978             doRelay : function(foo, bar, hname){
43979                 if(hname == 'down' || this.scope.isExpanded()){
43980                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43981                 }
43982                 return true;
43983             },
43984
43985             forceKeyDown: true
43986         });
43987         
43988         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43989         
43990     },
43991     
43992     initNumberEvent : function(e)
43993     {
43994         this.inputEl().on("keydown" , this.fireKey,  this);
43995         this.inputEl().on("focus", this.onFocus,  this);
43996         this.inputEl().on("blur", this.onBlur,  this);
43997         
43998         this.inputEl().relayEvent('keyup', this);
43999         
44000         if(this.indicator){
44001             this.indicator.addClass('invisible');
44002         }
44003  
44004         this.originalValue = this.getValue();
44005         
44006         if(this.validationEvent == 'keyup'){
44007             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44008             this.inputEl().on('keyup', this.filterValidation, this);
44009         }
44010         else if(this.validationEvent !== false){
44011             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44012         }
44013         
44014         if(this.selectOnFocus){
44015             this.on("focus", this.preFocus, this);
44016             
44017         }
44018         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44019             this.inputEl().on("keypress", this.filterKeys, this);
44020         } else {
44021             this.inputEl().relayEvent('keypress', this);
44022         }
44023         
44024         var allowed = "0123456789";
44025         
44026         if(this.allowDecimals){
44027             allowed += this.decimalSeparator;
44028         }
44029         
44030         if(this.allowNegative){
44031             allowed += "-";
44032         }
44033         
44034         if(this.thousandsDelimiter) {
44035             allowed += ",";
44036         }
44037         
44038         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44039         
44040         var keyPress = function(e){
44041             
44042             var k = e.getKey();
44043             
44044             var c = e.getCharCode();
44045             
44046             if(
44047                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44048                     allowed.indexOf(String.fromCharCode(c)) === -1
44049             ){
44050                 e.stopEvent();
44051                 return;
44052             }
44053             
44054             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44055                 return;
44056             }
44057             
44058             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44059                 e.stopEvent();
44060             }
44061         };
44062         
44063         this.inputEl().on("keypress", keyPress, this);
44064         
44065     },
44066     
44067     onTriggerClick : function(e)
44068     {   
44069         if(this.disabled){
44070             return;
44071         }
44072         
44073         this.page = 0;
44074         this.loadNext = false;
44075         
44076         if(this.isExpanded()){
44077             this.collapse();
44078             return;
44079         }
44080         
44081         this.hasFocus = true;
44082         
44083         if(this.triggerAction == 'all') {
44084             this.doQuery(this.allQuery, true);
44085             return;
44086         }
44087         
44088         this.doQuery(this.getRawValue());
44089     },
44090     
44091     getCurrency : function()
44092     {   
44093         var v = this.currencyEl().getValue();
44094         
44095         return v;
44096     },
44097     
44098     restrictHeight : function()
44099     {
44100         this.list.alignTo(this.currencyEl(), this.listAlign);
44101         this.list.alignTo(this.currencyEl(), this.listAlign);
44102     },
44103     
44104     onViewClick : function(view, doFocus, el, e)
44105     {
44106         var index = this.view.getSelectedIndexes()[0];
44107         
44108         var r = this.store.getAt(index);
44109         
44110         if(r){
44111             this.onSelect(r, index);
44112         }
44113     },
44114     
44115     onSelect : function(record, index){
44116         
44117         if(this.fireEvent('beforeselect', this, record, index) !== false){
44118         
44119             this.setFromCurrencyData(index > -1 ? record.data : false);
44120             
44121             this.collapse();
44122             
44123             this.fireEvent('select', this, record, index);
44124         }
44125     },
44126     
44127     setFromCurrencyData : function(o)
44128     {
44129         var currency = '';
44130         
44131         this.lastCurrency = o;
44132         
44133         if (this.currencyField) {
44134             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44135         } else {
44136             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44137         }
44138         
44139         this.lastSelectionText = currency;
44140         
44141         //setting default currency
44142         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44143             this.setCurrency(this.defaultCurrency);
44144             return;
44145         }
44146         
44147         this.setCurrency(currency);
44148     },
44149     
44150     setFromData : function(o)
44151     {
44152         var c = {};
44153         
44154         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44155         
44156         this.setFromCurrencyData(c);
44157         
44158         var value = '';
44159         
44160         if (this.name) {
44161             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44162         } else {
44163             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44164         }
44165         
44166         this.setValue(value);
44167         
44168     },
44169     
44170     setCurrency : function(v)
44171     {   
44172         this.currencyValue = v;
44173         
44174         if(this.rendered){
44175             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44176             this.validate();
44177         }
44178     },
44179     
44180     setValue : function(v)
44181     {
44182         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44183         
44184         this.value = v;
44185         
44186         if(this.rendered){
44187             
44188             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44189             
44190             this.inputEl().dom.value = (v == '') ? '' :
44191                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44192             
44193             if(!this.allowZero && v === '0') {
44194                 this.hiddenEl().dom.value = '';
44195                 this.inputEl().dom.value = '';
44196             }
44197             
44198             this.validate();
44199         }
44200     },
44201     
44202     getRawValue : function()
44203     {
44204         var v = this.inputEl().getValue();
44205         
44206         return v;
44207     },
44208     
44209     getValue : function()
44210     {
44211         return this.fixPrecision(this.parseValue(this.getRawValue()));
44212     },
44213     
44214     parseValue : function(value)
44215     {
44216         if(this.thousandsDelimiter) {
44217             value += "";
44218             r = new RegExp(",", "g");
44219             value = value.replace(r, "");
44220         }
44221         
44222         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44223         return isNaN(value) ? '' : value;
44224         
44225     },
44226     
44227     fixPrecision : function(value)
44228     {
44229         if(this.thousandsDelimiter) {
44230             value += "";
44231             r = new RegExp(",", "g");
44232             value = value.replace(r, "");
44233         }
44234         
44235         var nan = isNaN(value);
44236         
44237         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44238             return nan ? '' : value;
44239         }
44240         return parseFloat(value).toFixed(this.decimalPrecision);
44241     },
44242     
44243     decimalPrecisionFcn : function(v)
44244     {
44245         return Math.floor(v);
44246     },
44247     
44248     validateValue : function(value)
44249     {
44250         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44251             return false;
44252         }
44253         
44254         var num = this.parseValue(value);
44255         
44256         if(isNaN(num)){
44257             this.markInvalid(String.format(this.nanText, value));
44258             return false;
44259         }
44260         
44261         if(num < this.minValue){
44262             this.markInvalid(String.format(this.minText, this.minValue));
44263             return false;
44264         }
44265         
44266         if(num > this.maxValue){
44267             this.markInvalid(String.format(this.maxText, this.maxValue));
44268             return false;
44269         }
44270         
44271         return true;
44272     },
44273     
44274     validate : function()
44275     {
44276         if(this.disabled || this.allowBlank){
44277             this.markValid();
44278             return true;
44279         }
44280         
44281         var currency = this.getCurrency();
44282         
44283         if(this.validateValue(this.getRawValue()) && currency.length){
44284             this.markValid();
44285             return true;
44286         }
44287         
44288         this.markInvalid();
44289         return false;
44290     },
44291     
44292     getName: function()
44293     {
44294         return this.name;
44295     },
44296     
44297     beforeBlur : function()
44298     {
44299         if(!this.castInt){
44300             return;
44301         }
44302         
44303         var v = this.parseValue(this.getRawValue());
44304         
44305         if(v || v == 0){
44306             this.setValue(v);
44307         }
44308     },
44309     
44310     onBlur : function()
44311     {
44312         this.beforeBlur();
44313         
44314         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44315             //this.el.removeClass(this.focusClass);
44316         }
44317         
44318         this.hasFocus = false;
44319         
44320         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44321             this.validate();
44322         }
44323         
44324         var v = this.getValue();
44325         
44326         if(String(v) !== String(this.startValue)){
44327             this.fireEvent('change', this, v, this.startValue);
44328         }
44329         
44330         this.fireEvent("blur", this);
44331     },
44332     
44333     inputEl : function()
44334     {
44335         return this.el.select('.roo-money-amount-input', true).first();
44336     },
44337     
44338     currencyEl : function()
44339     {
44340         return this.el.select('.roo-money-currency-input', true).first();
44341     },
44342     
44343     hiddenEl : function()
44344     {
44345         return this.el.select('input.hidden-number-input',true).first();
44346     }
44347     
44348 });/**
44349  * @class Roo.bootstrap.BezierSignature
44350  * @extends Roo.bootstrap.Component
44351  * Bootstrap BezierSignature class
44352  * This script refer to:
44353  *    Title: Signature Pad
44354  *    Author: szimek
44355  *    Availability: https://github.com/szimek/signature_pad
44356  *
44357  * @constructor
44358  * Create a new BezierSignature
44359  * @param {Object} config The config object
44360  */
44361
44362 Roo.bootstrap.BezierSignature = function(config){
44363     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44364     this.addEvents({
44365         "resize" : true
44366     });
44367 };
44368
44369 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44370 {
44371      
44372     curve_data: [],
44373     
44374     is_empty: true,
44375     
44376     mouse_btn_down: true,
44377     
44378     /**
44379      * @cfg {int} canvas height
44380      */
44381     canvas_height: '200px',
44382     
44383     /**
44384      * @cfg {float|function} Radius of a single dot.
44385      */ 
44386     dot_size: false,
44387     
44388     /**
44389      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44390      */
44391     min_width: 0.5,
44392     
44393     /**
44394      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44395      */
44396     max_width: 2.5,
44397     
44398     /**
44399      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44400      */
44401     throttle: 16,
44402     
44403     /**
44404      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44405      */
44406     min_distance: 5,
44407     
44408     /**
44409      * @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.
44410      */
44411     bg_color: 'rgba(0, 0, 0, 0)',
44412     
44413     /**
44414      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44415      */
44416     dot_color: 'black',
44417     
44418     /**
44419      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44420      */ 
44421     velocity_filter_weight: 0.7,
44422     
44423     /**
44424      * @cfg {function} Callback when stroke begin. 
44425      */
44426     onBegin: false,
44427     
44428     /**
44429      * @cfg {function} Callback when stroke end.
44430      */
44431     onEnd: false,
44432     
44433     getAutoCreate : function()
44434     {
44435         var cls = 'roo-signature column';
44436         
44437         if(this.cls){
44438             cls += ' ' + this.cls;
44439         }
44440         
44441         var col_sizes = [
44442             'lg',
44443             'md',
44444             'sm',
44445             'xs'
44446         ];
44447         
44448         for(var i = 0; i < col_sizes.length; i++) {
44449             if(this[col_sizes[i]]) {
44450                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44451             }
44452         }
44453         
44454         var cfg = {
44455             tag: 'div',
44456             cls: cls,
44457             cn: [
44458                 {
44459                     tag: 'div',
44460                     cls: 'roo-signature-body',
44461                     cn: [
44462                         {
44463                             tag: 'canvas',
44464                             cls: 'roo-signature-body-canvas',
44465                             height: this.canvas_height,
44466                             width: this.canvas_width
44467                         }
44468                     ]
44469                 },
44470                 {
44471                     tag: 'input',
44472                     type: 'file',
44473                     style: 'display: none'
44474                 }
44475             ]
44476         };
44477         
44478         return cfg;
44479     },
44480     
44481     initEvents: function() 
44482     {
44483         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44484         
44485         var canvas = this.canvasEl();
44486         
44487         // mouse && touch event swapping...
44488         canvas.dom.style.touchAction = 'none';
44489         canvas.dom.style.msTouchAction = 'none';
44490         
44491         this.mouse_btn_down = false;
44492         canvas.on('mousedown', this._handleMouseDown, this);
44493         canvas.on('mousemove', this._handleMouseMove, this);
44494         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44495         
44496         if (window.PointerEvent) {
44497             canvas.on('pointerdown', this._handleMouseDown, this);
44498             canvas.on('pointermove', this._handleMouseMove, this);
44499             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44500         }
44501         
44502         if ('ontouchstart' in window) {
44503             canvas.on('touchstart', this._handleTouchStart, this);
44504             canvas.on('touchmove', this._handleTouchMove, this);
44505             canvas.on('touchend', this._handleTouchEnd, this);
44506         }
44507         
44508         Roo.EventManager.onWindowResize(this.resize, this, true);
44509         
44510         // file input event
44511         this.fileEl().on('change', this.uploadImage, this);
44512         
44513         this.clear();
44514         
44515         this.resize();
44516     },
44517     
44518     resize: function(){
44519         
44520         var canvas = this.canvasEl().dom;
44521         var ctx = this.canvasElCtx();
44522         var img_data = false;
44523         
44524         if(canvas.width > 0) {
44525             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44526         }
44527         // setting canvas width will clean img data
44528         canvas.width = 0;
44529         
44530         var style = window.getComputedStyle ? 
44531             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44532             
44533         var padding_left = parseInt(style.paddingLeft) || 0;
44534         var padding_right = parseInt(style.paddingRight) || 0;
44535         
44536         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44537         
44538         if(img_data) {
44539             ctx.putImageData(img_data, 0, 0);
44540         }
44541     },
44542     
44543     _handleMouseDown: function(e)
44544     {
44545         if (e.browserEvent.which === 1) {
44546             this.mouse_btn_down = true;
44547             this.strokeBegin(e);
44548         }
44549     },
44550     
44551     _handleMouseMove: function (e)
44552     {
44553         if (this.mouse_btn_down) {
44554             this.strokeMoveUpdate(e);
44555         }
44556     },
44557     
44558     _handleMouseUp: function (e)
44559     {
44560         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44561             this.mouse_btn_down = false;
44562             this.strokeEnd(e);
44563         }
44564     },
44565     
44566     _handleTouchStart: function (e) {
44567         
44568         e.preventDefault();
44569         if (e.browserEvent.targetTouches.length === 1) {
44570             // var touch = e.browserEvent.changedTouches[0];
44571             // this.strokeBegin(touch);
44572             
44573              this.strokeBegin(e); // assume e catching the correct xy...
44574         }
44575     },
44576     
44577     _handleTouchMove: function (e) {
44578         e.preventDefault();
44579         // var touch = event.targetTouches[0];
44580         // _this._strokeMoveUpdate(touch);
44581         this.strokeMoveUpdate(e);
44582     },
44583     
44584     _handleTouchEnd: function (e) {
44585         var wasCanvasTouched = e.target === this.canvasEl().dom;
44586         if (wasCanvasTouched) {
44587             e.preventDefault();
44588             // var touch = event.changedTouches[0];
44589             // _this._strokeEnd(touch);
44590             this.strokeEnd(e);
44591         }
44592     },
44593     
44594     reset: function () {
44595         this._lastPoints = [];
44596         this._lastVelocity = 0;
44597         this._lastWidth = (this.min_width + this.max_width) / 2;
44598         this.canvasElCtx().fillStyle = this.dot_color;
44599     },
44600     
44601     strokeMoveUpdate: function(e)
44602     {
44603         this.strokeUpdate(e);
44604         
44605         if (this.throttle) {
44606             this.throttleStroke(this.strokeUpdate, this.throttle);
44607         }
44608         else {
44609             this.strokeUpdate(e);
44610         }
44611     },
44612     
44613     strokeBegin: function(e)
44614     {
44615         var newPointGroup = {
44616             color: this.dot_color,
44617             points: []
44618         };
44619         
44620         if (typeof this.onBegin === 'function') {
44621             this.onBegin(e);
44622         }
44623         
44624         this.curve_data.push(newPointGroup);
44625         this.reset();
44626         this.strokeUpdate(e);
44627     },
44628     
44629     strokeUpdate: function(e)
44630     {
44631         var rect = this.canvasEl().dom.getBoundingClientRect();
44632         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44633         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44634         var lastPoints = lastPointGroup.points;
44635         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44636         var isLastPointTooClose = lastPoint
44637             ? point.distanceTo(lastPoint) <= this.min_distance
44638             : false;
44639         var color = lastPointGroup.color;
44640         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44641             var curve = this.addPoint(point);
44642             if (!lastPoint) {
44643                 this.drawDot({color: color, point: point});
44644             }
44645             else if (curve) {
44646                 this.drawCurve({color: color, curve: curve});
44647             }
44648             lastPoints.push({
44649                 time: point.time,
44650                 x: point.x,
44651                 y: point.y
44652             });
44653         }
44654     },
44655     
44656     strokeEnd: function(e)
44657     {
44658         this.strokeUpdate(e);
44659         if (typeof this.onEnd === 'function') {
44660             this.onEnd(e);
44661         }
44662     },
44663     
44664     addPoint:  function (point) {
44665         var _lastPoints = this._lastPoints;
44666         _lastPoints.push(point);
44667         if (_lastPoints.length > 2) {
44668             if (_lastPoints.length === 3) {
44669                 _lastPoints.unshift(_lastPoints[0]);
44670             }
44671             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44672             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44673             _lastPoints.shift();
44674             return curve;
44675         }
44676         return null;
44677     },
44678     
44679     calculateCurveWidths: function (startPoint, endPoint) {
44680         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44681             (1 - this.velocity_filter_weight) * this._lastVelocity;
44682
44683         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44684         var widths = {
44685             end: newWidth,
44686             start: this._lastWidth
44687         };
44688         
44689         this._lastVelocity = velocity;
44690         this._lastWidth = newWidth;
44691         return widths;
44692     },
44693     
44694     drawDot: function (_a) {
44695         var color = _a.color, point = _a.point;
44696         var ctx = this.canvasElCtx();
44697         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44698         ctx.beginPath();
44699         this.drawCurveSegment(point.x, point.y, width);
44700         ctx.closePath();
44701         ctx.fillStyle = color;
44702         ctx.fill();
44703     },
44704     
44705     drawCurve: function (_a) {
44706         var color = _a.color, curve = _a.curve;
44707         var ctx = this.canvasElCtx();
44708         var widthDelta = curve.endWidth - curve.startWidth;
44709         var drawSteps = Math.floor(curve.length()) * 2;
44710         ctx.beginPath();
44711         ctx.fillStyle = color;
44712         for (var i = 0; i < drawSteps; i += 1) {
44713         var t = i / drawSteps;
44714         var tt = t * t;
44715         var ttt = tt * t;
44716         var u = 1 - t;
44717         var uu = u * u;
44718         var uuu = uu * u;
44719         var x = uuu * curve.startPoint.x;
44720         x += 3 * uu * t * curve.control1.x;
44721         x += 3 * u * tt * curve.control2.x;
44722         x += ttt * curve.endPoint.x;
44723         var y = uuu * curve.startPoint.y;
44724         y += 3 * uu * t * curve.control1.y;
44725         y += 3 * u * tt * curve.control2.y;
44726         y += ttt * curve.endPoint.y;
44727         var width = curve.startWidth + ttt * widthDelta;
44728         this.drawCurveSegment(x, y, width);
44729         }
44730         ctx.closePath();
44731         ctx.fill();
44732     },
44733     
44734     drawCurveSegment: function (x, y, width) {
44735         var ctx = this.canvasElCtx();
44736         ctx.moveTo(x, y);
44737         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44738         this.is_empty = false;
44739     },
44740     
44741     clear: function()
44742     {
44743         var ctx = this.canvasElCtx();
44744         var canvas = this.canvasEl().dom;
44745         ctx.fillStyle = this.bg_color;
44746         ctx.clearRect(0, 0, canvas.width, canvas.height);
44747         ctx.fillRect(0, 0, canvas.width, canvas.height);
44748         this.curve_data = [];
44749         this.reset();
44750         this.is_empty = true;
44751     },
44752     
44753     fileEl: function()
44754     {
44755         return  this.el.select('input',true).first();
44756     },
44757     
44758     canvasEl: function()
44759     {
44760         return this.el.select('canvas',true).first();
44761     },
44762     
44763     canvasElCtx: function()
44764     {
44765         return this.el.select('canvas',true).first().dom.getContext('2d');
44766     },
44767     
44768     getImage: function(type)
44769     {
44770         if(this.is_empty) {
44771             return false;
44772         }
44773         
44774         // encryption ?
44775         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44776     },
44777     
44778     drawFromImage: function(img_src)
44779     {
44780         var img = new Image();
44781         
44782         img.onload = function(){
44783             this.canvasElCtx().drawImage(img, 0, 0);
44784         }.bind(this);
44785         
44786         img.src = img_src;
44787         
44788         this.is_empty = false;
44789     },
44790     
44791     selectImage: function()
44792     {
44793         this.fileEl().dom.click();
44794     },
44795     
44796     uploadImage: function(e)
44797     {
44798         var reader = new FileReader();
44799         
44800         reader.onload = function(e){
44801             var img = new Image();
44802             img.onload = function(){
44803                 this.reset();
44804                 this.canvasElCtx().drawImage(img, 0, 0);
44805             }.bind(this);
44806             img.src = e.target.result;
44807         }.bind(this);
44808         
44809         reader.readAsDataURL(e.target.files[0]);
44810     },
44811     
44812     // Bezier Point Constructor
44813     Point: (function () {
44814         function Point(x, y, time) {
44815             this.x = x;
44816             this.y = y;
44817             this.time = time || Date.now();
44818         }
44819         Point.prototype.distanceTo = function (start) {
44820             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44821         };
44822         Point.prototype.equals = function (other) {
44823             return this.x === other.x && this.y === other.y && this.time === other.time;
44824         };
44825         Point.prototype.velocityFrom = function (start) {
44826             return this.time !== start.time
44827             ? this.distanceTo(start) / (this.time - start.time)
44828             : 0;
44829         };
44830         return Point;
44831     }()),
44832     
44833     
44834     // Bezier Constructor
44835     Bezier: (function () {
44836         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44837             this.startPoint = startPoint;
44838             this.control2 = control2;
44839             this.control1 = control1;
44840             this.endPoint = endPoint;
44841             this.startWidth = startWidth;
44842             this.endWidth = endWidth;
44843         }
44844         Bezier.fromPoints = function (points, widths, scope) {
44845             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44846             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44847             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44848         };
44849         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44850             var dx1 = s1.x - s2.x;
44851             var dy1 = s1.y - s2.y;
44852             var dx2 = s2.x - s3.x;
44853             var dy2 = s2.y - s3.y;
44854             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44855             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44856             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44857             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44858             var dxm = m1.x - m2.x;
44859             var dym = m1.y - m2.y;
44860             var k = l2 / (l1 + l2);
44861             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44862             var tx = s2.x - cm.x;
44863             var ty = s2.y - cm.y;
44864             return {
44865                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44866                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44867             };
44868         };
44869         Bezier.prototype.length = function () {
44870             var steps = 10;
44871             var length = 0;
44872             var px;
44873             var py;
44874             for (var i = 0; i <= steps; i += 1) {
44875                 var t = i / steps;
44876                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44877                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44878                 if (i > 0) {
44879                     var xdiff = cx - px;
44880                     var ydiff = cy - py;
44881                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44882                 }
44883                 px = cx;
44884                 py = cy;
44885             }
44886             return length;
44887         };
44888         Bezier.prototype.point = function (t, start, c1, c2, end) {
44889             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44890             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44891             + (3.0 * c2 * (1.0 - t) * t * t)
44892             + (end * t * t * t);
44893         };
44894         return Bezier;
44895     }()),
44896     
44897     throttleStroke: function(fn, wait) {
44898       if (wait === void 0) { wait = 250; }
44899       var previous = 0;
44900       var timeout = null;
44901       var result;
44902       var storedContext;
44903       var storedArgs;
44904       var later = function () {
44905           previous = Date.now();
44906           timeout = null;
44907           result = fn.apply(storedContext, storedArgs);
44908           if (!timeout) {
44909               storedContext = null;
44910               storedArgs = [];
44911           }
44912       };
44913       return function wrapper() {
44914           var args = [];
44915           for (var _i = 0; _i < arguments.length; _i++) {
44916               args[_i] = arguments[_i];
44917           }
44918           var now = Date.now();
44919           var remaining = wait - (now - previous);
44920           storedContext = this;
44921           storedArgs = args;
44922           if (remaining <= 0 || remaining > wait) {
44923               if (timeout) {
44924                   clearTimeout(timeout);
44925                   timeout = null;
44926               }
44927               previous = now;
44928               result = fn.apply(storedContext, storedArgs);
44929               if (!timeout) {
44930                   storedContext = null;
44931                   storedArgs = [];
44932               }
44933           }
44934           else if (!timeout) {
44935               timeout = window.setTimeout(later, remaining);
44936           }
44937           return result;
44938       };
44939   }
44940   
44941 });
44942
44943  
44944
44945