roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<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>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * @builder-top
852  * @children Roo.bootstrap.Component
853  * @parent none
854  * Bootstrap Body class
855  *
856  * @constructor
857  * Create a new body
858  * @param {Object} config The config object
859  */
860
861 Roo.bootstrap.Body = function(config){
862
863     config = config || {};
864
865     Roo.bootstrap.Body.superclass.constructor.call(this, config);
866     this.el = Roo.get(config.el ? config.el : document.body );
867     if (this.cls && this.cls.length) {
868         Roo.get(document.body).addClass(this.cls);
869     }
870 };
871
872 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
873
874     is_body : true,// just to make sure it's constructed?
875
876         autoCreate : {
877         cls: 'container'
878     },
879     onRender : function(ct, position)
880     {
881        /* Roo.log("Roo.bootstrap.Body - onRender");
882         if (this.cls && this.cls.length) {
883             Roo.get(document.body).addClass(this.cls);
884         }
885         // style??? xttr???
886         */
887     }
888
889
890
891
892 });
893 /*
894  * - LGPL
895  *
896  * button group
897  * 
898  */
899
900
901 /**
902  * @class Roo.bootstrap.ButtonGroup
903  * @extends Roo.bootstrap.Component
904  * Bootstrap ButtonGroup class
905  * @cfg {String} size lg | sm | xs (default empty normal)
906  * @cfg {String} align vertical | justified  (default none)
907  * @cfg {String} direction up | down (default down)
908  * @cfg {Boolean} toolbar false | true
909  * @cfg {Boolean} btn true | false
910  * 
911  * 
912  * @constructor
913  * Create a new Input
914  * @param {Object} config The config object
915  */
916
917 Roo.bootstrap.ButtonGroup = function(config){
918     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
919 };
920
921 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
922     
923     size: '',
924     align: '',
925     direction: '',
926     toolbar: false,
927     btn: true,
928
929     getAutoCreate : function(){
930         var cfg = {
931             cls: 'btn-group',
932             html : null
933         };
934         
935         cfg.html = this.html || cfg.html;
936         
937         if (this.toolbar) {
938             cfg = {
939                 cls: 'btn-toolbar',
940                 html: null
941             };
942             
943             return cfg;
944         }
945         
946         if (['vertical','justified'].indexOf(this.align)!==-1) {
947             cfg.cls = 'btn-group-' + this.align;
948             
949             if (this.align == 'justified') {
950                 console.log(this.items);
951             }
952         }
953         
954         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
955             cfg.cls += ' btn-group-' + this.size;
956         }
957         
958         if (this.direction == 'up') {
959             cfg.cls += ' dropup' ;
960         }
961         
962         return cfg;
963     },
964     /**
965      * Add a button to the group (similar to NavItem API.)
966      */
967     addItem : function(cfg)
968     {
969         var cn = new Roo.bootstrap.Button(cfg);
970         //this.register(cn);
971         cn.parentId = this.id;
972         cn.onRender(this.el, null);
973         return cn;
974     }
975    
976 });
977
978  /*
979  * - LGPL
980  *
981  * button
982  * 
983  */
984
985 /**
986  * @class Roo.bootstrap.Button
987  * @extends Roo.bootstrap.Component
988  * Bootstrap Button class
989  * @cfg {String} html The button content
990  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
991  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
992  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
993  * @cfg {String} size (lg|sm|xs)
994  * @cfg {String} tag (a|input|submit)
995  * @cfg {String} href empty or href
996  * @cfg {Boolean} disabled default false;
997  * @cfg {Boolean} isClose default false;
998  * @cfg {String} glyphicon depricated - use fa
999  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1000  * @cfg {String} badge text for badge
1001  * @cfg {String} theme (default|glow)  
1002  * @cfg {Boolean} inverse dark themed version
1003  * @cfg {Boolean} toggle is it a slidy toggle button
1004  * @cfg {Boolean} pressed   default null - if the button ahs active state
1005  * @cfg {String} ontext text for on slidy toggle state
1006  * @cfg {String} offtext text for off slidy toggle state
1007  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1008  * @cfg {Boolean} removeClass remove the standard class..
1009  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1010  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1011  * 
1012  * @constructor
1013  * Create a new button
1014  * @param {Object} config The config object
1015  */
1016
1017
1018 Roo.bootstrap.Button = function(config){
1019     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1020     
1021     this.addEvents({
1022         // raw events
1023         /**
1024          * @event click
1025          * When a button is pressed
1026          * @param {Roo.bootstrap.Button} btn
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true,
1030         /**
1031          * @event dblclick
1032          * When a button is double clicked
1033          * @param {Roo.bootstrap.Button} btn
1034          * @param {Roo.EventObject} e
1035          */
1036         "dblclick" : true,
1037          /**
1038          * @event toggle
1039          * After the button has been toggles
1040          * @param {Roo.bootstrap.Button} btn
1041          * @param {Roo.EventObject} e
1042          * @param {boolean} pressed (also available as button.pressed)
1043          */
1044         "toggle" : true
1045     });
1046 };
1047
1048 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1049     html: false,
1050     active: false,
1051     weight: '',
1052     badge_weight: '',
1053     outline : false,
1054     size: '',
1055     tag: 'button',
1056     href: '',
1057     disabled: false,
1058     isClose: false,
1059     glyphicon: '',
1060     fa: '',
1061     badge: '',
1062     theme: 'default',
1063     inverse: false,
1064     
1065     toggle: false,
1066     ontext: 'ON',
1067     offtext: 'OFF',
1068     defaulton: true,
1069     preventDefault: true,
1070     removeClass: false,
1071     name: false,
1072     target: false,
1073     group : false,
1074      
1075     pressed : null,
1076      
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : 'button',
1082             cls : 'roo-button',
1083             html: ''
1084         };
1085         
1086         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1087             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1088             this.tag = 'button';
1089         } else {
1090             cfg.tag = this.tag;
1091         }
1092         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1093         
1094         if (this.toggle == true) {
1095             cfg={
1096                 tag: 'div',
1097                 cls: 'slider-frame roo-button',
1098                 cn: [
1099                     {
1100                         tag: 'span',
1101                         'data-on-text':'ON',
1102                         'data-off-text':'OFF',
1103                         cls: 'slider-button',
1104                         html: this.offtext
1105                     }
1106                 ]
1107             };
1108             // why are we validating the weights?
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 cfg.cls +=  ' ' + this.weight;
1111             }
1112             
1113             return cfg;
1114         }
1115         
1116         if (this.isClose) {
1117             cfg.cls += ' close';
1118             
1119             cfg["aria-hidden"] = true;
1120             
1121             cfg.html = "&times;";
1122             
1123             return cfg;
1124         }
1125              
1126         
1127         if (this.theme==='default') {
1128             cfg.cls = 'btn roo-button';
1129             
1130             //if (this.parentType != 'Navbar') {
1131             this.weight = this.weight.length ?  this.weight : 'default';
1132             //}
1133             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1134                 
1135                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1136                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1137                 cfg.cls += ' btn-' + outline + weight;
1138                 if (this.weight == 'default') {
1139                     // BC
1140                     cfg.cls += ' btn-' + this.weight;
1141                 }
1142             }
1143         } else if (this.theme==='glow') {
1144             
1145             cfg.tag = 'a';
1146             cfg.cls = 'btn-glow roo-button';
1147             
1148             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1149                 
1150                 cfg.cls += ' ' + this.weight;
1151             }
1152         }
1153    
1154         
1155         if (this.inverse) {
1156             this.cls += ' inverse';
1157         }
1158         
1159         
1160         if (this.active || this.pressed === true) {
1161             cfg.cls += ' active';
1162         }
1163         
1164         if (this.disabled) {
1165             cfg.disabled = 'disabled';
1166         }
1167         
1168         if (this.items) {
1169             Roo.log('changing to ul' );
1170             cfg.tag = 'ul';
1171             this.glyphicon = 'caret';
1172             if (Roo.bootstrap.version == 4) {
1173                 this.fa = 'caret-down';
1174             }
1175             
1176         }
1177         
1178         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1179          
1180         //gsRoo.log(this.parentType);
1181         if (this.parentType === 'Navbar' && !this.parent().bar) {
1182             Roo.log('changing to li?');
1183             
1184             cfg.tag = 'li';
1185             
1186             cfg.cls = '';
1187             cfg.cn =  [{
1188                 tag : 'a',
1189                 cls : 'roo-button',
1190                 html : this.html,
1191                 href : this.href || '#'
1192             }];
1193             if (this.menu) {
1194                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1195                 cfg.cls += ' dropdown';
1196             }   
1197             
1198             delete cfg.html;
1199             
1200         }
1201         
1202        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1203         
1204         if (this.glyphicon) {
1205             cfg.html = ' ' + cfg.html;
1206             
1207             cfg.cn = [
1208                 {
1209                     tag: 'span',
1210                     cls: 'glyphicon glyphicon-' + this.glyphicon
1211                 }
1212             ];
1213         }
1214         if (this.fa) {
1215             cfg.html = ' ' + cfg.html;
1216             
1217             cfg.cn = [
1218                 {
1219                     tag: 'i',
1220                     cls: 'fa fas fa-' + this.fa
1221                 }
1222             ];
1223         }
1224         
1225         if (this.badge) {
1226             cfg.html += ' ';
1227             
1228             cfg.tag = 'a';
1229             
1230 //            cfg.cls='btn roo-button';
1231             
1232             cfg.href=this.href;
1233             
1234             var value = cfg.html;
1235             
1236             if(this.glyphicon){
1237                 value = {
1238                     tag: 'span',
1239                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1240                     html: this.html
1241                 };
1242             }
1243             if(this.fa){
1244                 value = {
1245                     tag: 'i',
1246                     cls: 'fa fas fa-' + this.fa,
1247                     html: this.html
1248                 };
1249             }
1250             
1251             var bw = this.badge_weight.length ? this.badge_weight :
1252                 (this.weight.length ? this.weight : 'secondary');
1253             bw = bw == 'default' ? 'secondary' : bw;
1254             
1255             cfg.cn = [
1256                 value,
1257                 {
1258                     tag: 'span',
1259                     cls: 'badge badge-' + bw,
1260                     html: this.badge
1261                 }
1262             ];
1263             
1264             cfg.html='';
1265         }
1266         
1267         if (this.menu) {
1268             cfg.cls += ' dropdown';
1269             cfg.html = typeof(cfg.html) != 'undefined' ?
1270                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1271         }
1272         
1273         if (cfg.tag !== 'a' && this.href !== '') {
1274             throw "Tag must be a to set href.";
1275         } else if (this.href.length > 0) {
1276             cfg.href = this.href;
1277         }
1278         
1279         if(this.removeClass){
1280             cfg.cls = '';
1281         }
1282         
1283         if(this.target){
1284             cfg.target = this.target;
1285         }
1286         
1287         return cfg;
1288     },
1289     initEvents: function() {
1290        // Roo.log('init events?');
1291 //        Roo.log(this.el.dom);
1292         // add the menu...
1293         
1294         if (typeof (this.menu) != 'undefined') {
1295             this.menu.parentType = this.xtype;
1296             this.menu.triggerEl = this.el;
1297             this.addxtype(Roo.apply({}, this.menu));
1298         }
1299
1300
1301         if (this.el.hasClass('roo-button')) {
1302              this.el.on('click', this.onClick, this);
1303              this.el.on('dblclick', this.onDblClick, this);
1304         } else {
1305              this.el.select('.roo-button').on('click', this.onClick, this);
1306              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1307              
1308         }
1309         // why?
1310         if(this.removeClass){
1311             this.el.on('click', this.onClick, this);
1312         }
1313         
1314         if (this.group === true) {
1315              if (this.pressed === false || this.pressed === true) {
1316                 // nothing
1317             } else {
1318                 this.pressed = false;
1319                 this.setActive(this.pressed);
1320             }
1321             
1322         }
1323         
1324         this.el.enableDisplayMode();
1325         
1326     },
1327     onClick : function(e)
1328     {
1329         if (this.disabled) {
1330             return;
1331         }
1332         
1333         Roo.log('button on click ');
1334         if(this.preventDefault){
1335             e.preventDefault();
1336         }
1337         
1338         if (this.group) {
1339             if (this.pressed) {
1340                 // do nothing -
1341                 return;
1342             }
1343             this.setActive(true);
1344             var pi = this.parent().items;
1345             for (var i = 0;i < pi.length;i++) {
1346                 if (this == pi[i]) {
1347                     continue;
1348                 }
1349                 if (pi[i].el.hasClass('roo-button')) {
1350                     pi[i].setActive(false);
1351                 }
1352             }
1353             this.fireEvent('click', this, e);            
1354             return;
1355         }
1356         
1357         if (this.pressed === true || this.pressed === false) {
1358             this.toggleActive(e);
1359         }
1360         
1361         
1362         this.fireEvent('click', this, e);
1363     },
1364     onDblClick: function(e)
1365     {
1366         if (this.disabled) {
1367             return;
1368         }
1369         if(this.preventDefault){
1370             e.preventDefault();
1371         }
1372         this.fireEvent('dblclick', this, e);
1373     },
1374     /**
1375      * Enables this button
1376      */
1377     enable : function()
1378     {
1379         this.disabled = false;
1380         this.el.removeClass('disabled');
1381         this.el.dom.removeAttribute("disabled");
1382     },
1383     
1384     /**
1385      * Disable this button
1386      */
1387     disable : function()
1388     {
1389         this.disabled = true;
1390         this.el.addClass('disabled');
1391         this.el.attr("disabled", "disabled")
1392     },
1393      /**
1394      * sets the active state on/off, 
1395      * @param {Boolean} state (optional) Force a particular state
1396      */
1397     setActive : function(v) {
1398         
1399         this.el[v ? 'addClass' : 'removeClass']('active');
1400         this.pressed = v;
1401     },
1402      /**
1403      * toggles the current active state 
1404      */
1405     toggleActive : function(e)
1406     {
1407         this.setActive(!this.pressed); // this modifies pressed...
1408         this.fireEvent('toggle', this, e, this.pressed);
1409     },
1410      /**
1411      * get the current active state
1412      * @return {boolean} true if it's active
1413      */
1414     isActive : function()
1415     {
1416         return this.el.hasClass('active');
1417     },
1418     /**
1419      * set the text of the first selected button
1420      */
1421     setText : function(str)
1422     {
1423         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1424     },
1425     /**
1426      * get the text of the first selected button
1427      */
1428     getText : function()
1429     {
1430         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1431     },
1432     
1433     setWeight : function(str)
1434     {
1435         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1436         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1437         this.weight = str;
1438         var outline = this.outline ? 'outline-' : '';
1439         if (str == 'default') {
1440             this.el.addClass('btn-default btn-outline-secondary');        
1441             return;
1442         }
1443         this.el.addClass('btn-' + outline + str);        
1444     }
1445     
1446     
1447 });
1448 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1449
1450 Roo.bootstrap.Button.weights = [
1451     'default',
1452     'secondary' ,
1453     'primary',
1454     'success',
1455     'info',
1456     'warning',
1457     'danger',
1458     'link',
1459     'light',
1460     'dark'              
1461    
1462 ];/*
1463  * - LGPL
1464  *
1465  * column
1466  * 
1467  */
1468
1469 /**
1470  * @class Roo.bootstrap.Column
1471  * @extends Roo.bootstrap.Component
1472  * Bootstrap Column class
1473  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1477  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1478  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1479  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1480  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1481  *
1482  * 
1483  * @cfg {Boolean} hidden (true|false) hide the element
1484  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1485  * @cfg {String} fa (ban|check|...) font awesome icon
1486  * @cfg {Number} fasize (1|2|....) font awsome size
1487
1488  * @cfg {String} icon (info-sign|check|...) glyphicon name
1489
1490  * @cfg {String} html content of column.
1491  * 
1492  * @constructor
1493  * Create a new Column
1494  * @param {Object} config The config object
1495  */
1496
1497 Roo.bootstrap.Column = function(config){
1498     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1502     
1503     xs: false,
1504     sm: false,
1505     md: false,
1506     lg: false,
1507     xsoff: false,
1508     smoff: false,
1509     mdoff: false,
1510     lgoff: false,
1511     html: '',
1512     offset: 0,
1513     alert: false,
1514     fa: false,
1515     icon : false,
1516     hidden : false,
1517     fasize : 1,
1518     
1519     getAutoCreate : function(){
1520         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1521         
1522         cfg = {
1523             tag: 'div',
1524             cls: 'column'
1525         };
1526         
1527         var settings=this;
1528         var sizes =   ['xs','sm','md','lg'];
1529         sizes.map(function(size ,ix){
1530             //Roo.log( size + ':' + settings[size]);
1531             
1532             if (settings[size+'off'] !== false) {
1533                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1534             }
1535             
1536             if (settings[size] === false) {
1537                 return;
1538             }
1539             
1540             if (!settings[size]) { // 0 = hidden
1541                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1542                 // bootsrap4
1543                 for (var i = ix; i > -1; i--) {
1544                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1545                 }
1546                 
1547                 
1548                 return;
1549             }
1550             cfg.cls += ' col-' + size + '-' + settings[size] + (
1551                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1552             );
1553             
1554         });
1555         
1556         if (this.hidden) {
1557             cfg.cls += ' hidden';
1558         }
1559         
1560         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1561             cfg.cls +=' alert alert-' + this.alert;
1562         }
1563         
1564         
1565         if (this.html.length) {
1566             cfg.html = this.html;
1567         }
1568         if (this.fa) {
1569             var fasize = '';
1570             if (this.fasize > 1) {
1571                 fasize = ' fa-' + this.fasize + 'x';
1572             }
1573             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574             
1575             
1576         }
1577         if (this.icon) {
1578             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1579         }
1580         
1581         return cfg;
1582     }
1583    
1584 });
1585
1586  
1587
1588  /*
1589  * - LGPL
1590  *
1591  * page container.
1592  * 
1593  */
1594
1595
1596 /**
1597  * @class Roo.bootstrap.Container
1598  * @extends Roo.bootstrap.Component
1599  * @builder-top
1600  * Bootstrap Container class
1601  * @cfg {Boolean} jumbotron is it a jumbotron element
1602  * @cfg {String} html content of element
1603  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1604  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1605  * @cfg {String} header content of header (for panel)
1606  * @cfg {String} footer content of footer (for panel)
1607  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1608  * @cfg {String} tag (header|aside|section) type of HTML tag.
1609  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1610  * @cfg {String} fa font awesome icon
1611  * @cfg {String} icon (info-sign|check|...) glyphicon name
1612  * @cfg {Boolean} hidden (true|false) hide the element
1613  * @cfg {Boolean} expandable (true|false) default false
1614  * @cfg {Boolean} expanded (true|false) default true
1615  * @cfg {String} rheader contet on the right of header
1616  * @cfg {Boolean} clickable (true|false) default false
1617
1618  *     
1619  * @constructor
1620  * Create a new Container
1621  * @param {Object} config The config object
1622  */
1623
1624 Roo.bootstrap.Container = function(config){
1625     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626     
1627     this.addEvents({
1628         // raw events
1629          /**
1630          * @event expand
1631          * After the panel has been expand
1632          * 
1633          * @param {Roo.bootstrap.Container} this
1634          */
1635         "expand" : true,
1636         /**
1637          * @event collapse
1638          * After the panel has been collapsed
1639          * 
1640          * @param {Roo.bootstrap.Container} this
1641          */
1642         "collapse" : true,
1643         /**
1644          * @event click
1645          * When a element is chick
1646          * @param {Roo.bootstrap.Container} this
1647          * @param {Roo.EventObject} e
1648          */
1649         "click" : true
1650     });
1651 };
1652
1653 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1654     
1655     jumbotron : false,
1656     well: '',
1657     panel : '',
1658     header: '',
1659     footer : '',
1660     sticky: '',
1661     tag : false,
1662     alert : false,
1663     fa: false,
1664     icon : false,
1665     expandable : false,
1666     rheader : '',
1667     expanded : true,
1668     clickable: false,
1669   
1670      
1671     getChildContainer : function() {
1672         
1673         if(!this.el){
1674             return false;
1675         }
1676         
1677         if (this.panel.length) {
1678             return this.el.select('.panel-body',true).first();
1679         }
1680         
1681         return this.el;
1682     },
1683     
1684     
1685     getAutoCreate : function(){
1686         
1687         var cfg = {
1688             tag : this.tag || 'div',
1689             html : '',
1690             cls : ''
1691         };
1692         if (this.jumbotron) {
1693             cfg.cls = 'jumbotron';
1694         }
1695         
1696         
1697         
1698         // - this is applied by the parent..
1699         //if (this.cls) {
1700         //    cfg.cls = this.cls + '';
1701         //}
1702         
1703         if (this.sticky.length) {
1704             
1705             var bd = Roo.get(document.body);
1706             if (!bd.hasClass('bootstrap-sticky')) {
1707                 bd.addClass('bootstrap-sticky');
1708                 Roo.select('html',true).setStyle('height', '100%');
1709             }
1710              
1711             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1712         }
1713         
1714         
1715         if (this.well.length) {
1716             switch (this.well) {
1717                 case 'lg':
1718                 case 'sm':
1719                     cfg.cls +=' well well-' +this.well;
1720                     break;
1721                 default:
1722                     cfg.cls +=' well';
1723                     break;
1724             }
1725         }
1726         
1727         if (this.hidden) {
1728             cfg.cls += ' hidden';
1729         }
1730         
1731         
1732         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1733             cfg.cls +=' alert alert-' + this.alert;
1734         }
1735         
1736         var body = cfg;
1737         
1738         if (this.panel.length) {
1739             cfg.cls += ' panel panel-' + this.panel;
1740             cfg.cn = [];
1741             if (this.header.length) {
1742                 
1743                 var h = [];
1744                 
1745                 if(this.expandable){
1746                     
1747                     cfg.cls = cfg.cls + ' expandable';
1748                     
1749                     h.push({
1750                         tag: 'i',
1751                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1752                     });
1753                     
1754                 }
1755                 
1756                 h.push(
1757                     {
1758                         tag: 'span',
1759                         cls : 'panel-title',
1760                         html : (this.expandable ? '&nbsp;' : '') + this.header
1761                     },
1762                     {
1763                         tag: 'span',
1764                         cls: 'panel-header-right',
1765                         html: this.rheader
1766                     }
1767                 );
1768                 
1769                 cfg.cn.push({
1770                     cls : 'panel-heading',
1771                     style : this.expandable ? 'cursor: pointer' : '',
1772                     cn : h
1773                 });
1774                 
1775             }
1776             
1777             body = false;
1778             cfg.cn.push({
1779                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1780                 html : this.html
1781             });
1782             
1783             
1784             if (this.footer.length) {
1785                 cfg.cn.push({
1786                     cls : 'panel-footer',
1787                     html : this.footer
1788                     
1789                 });
1790             }
1791             
1792         }
1793         
1794         if (body) {
1795             body.html = this.html || cfg.html;
1796             // prefix with the icons..
1797             if (this.fa) {
1798                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1799             }
1800             if (this.icon) {
1801                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1802             }
1803             
1804             
1805         }
1806         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1807             cfg.cls =  'container';
1808         }
1809         
1810         return cfg;
1811     },
1812     
1813     initEvents: function() 
1814     {
1815         if(this.expandable){
1816             var headerEl = this.headerEl();
1817         
1818             if(headerEl){
1819                 headerEl.on('click', this.onToggleClick, this);
1820             }
1821         }
1822         
1823         if(this.clickable){
1824             this.el.on('click', this.onClick, this);
1825         }
1826         
1827     },
1828     
1829     onToggleClick : function()
1830     {
1831         var headerEl = this.headerEl();
1832         
1833         if(!headerEl){
1834             return;
1835         }
1836         
1837         if(this.expanded){
1838             this.collapse();
1839             return;
1840         }
1841         
1842         this.expand();
1843     },
1844     
1845     expand : function()
1846     {
1847         if(this.fireEvent('expand', this)) {
1848             
1849             this.expanded = true;
1850             
1851             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1852             
1853             this.el.select('.panel-body',true).first().removeClass('hide');
1854             
1855             var toggleEl = this.toggleEl();
1856
1857             if(!toggleEl){
1858                 return;
1859             }
1860
1861             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1862         }
1863         
1864     },
1865     
1866     collapse : function()
1867     {
1868         if(this.fireEvent('collapse', this)) {
1869             
1870             this.expanded = false;
1871             
1872             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1873             this.el.select('.panel-body',true).first().addClass('hide');
1874         
1875             var toggleEl = this.toggleEl();
1876
1877             if(!toggleEl){
1878                 return;
1879             }
1880
1881             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1882         }
1883     },
1884     
1885     toggleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-heading .fa',true).first();
1892     },
1893     
1894     headerEl : function()
1895     {
1896         if(!this.el || !this.panel.length || !this.header.length){
1897             return;
1898         }
1899         
1900         return this.el.select('.panel-heading',true).first()
1901     },
1902     
1903     bodyEl : function()
1904     {
1905         if(!this.el || !this.panel.length){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-body',true).first()
1910     },
1911     
1912     titleEl : function()
1913     {
1914         if(!this.el || !this.panel.length || !this.header.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-title',true).first();
1919     },
1920     
1921     setTitle : function(v)
1922     {
1923         var titleEl = this.titleEl();
1924         
1925         if(!titleEl){
1926             return;
1927         }
1928         
1929         titleEl.dom.innerHTML = v;
1930     },
1931     
1932     getTitle : function()
1933     {
1934         
1935         var titleEl = this.titleEl();
1936         
1937         if(!titleEl){
1938             return '';
1939         }
1940         
1941         return titleEl.dom.innerHTML;
1942     },
1943     
1944     setRightTitle : function(v)
1945     {
1946         var t = this.el.select('.panel-header-right',true).first();
1947         
1948         if(!t){
1949             return;
1950         }
1951         
1952         t.dom.innerHTML = v;
1953     },
1954     
1955     onClick : function(e)
1956     {
1957         e.preventDefault();
1958         
1959         this.fireEvent('click', this, e);
1960     }
1961 });
1962
1963  /*
1964  *  - LGPL
1965  *
1966  *  This is BS4's Card element.. - similar to our containers probably..
1967  * 
1968  */
1969 /**
1970  * @class Roo.bootstrap.Card
1971  * @extends Roo.bootstrap.Component
1972  * Bootstrap Card class
1973  *
1974  *
1975  * possible... may not be implemented..
1976  * @cfg {String} header_image  src url of image.
1977  * @cfg {String|Object} header
1978  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1979  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1980  * 
1981  * @cfg {String} title
1982  * @cfg {String} subtitle
1983  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1984  * @cfg {String} footer
1985  
1986  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1987  * 
1988  * @cfg {String} margin (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1991  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1992  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1993  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1995  *
1996  * @cfg {String} padding (0|1|2|3|4|5)
1997  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1998  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1999  * @cfg {String} padding_left (0|1|2|3|4|5)
2000  * @cfg {String} padding_right (0|1|2|3|4|5)
2001  * @cfg {String} padding_x (0|1|2|3|4|5)
2002  * @cfg {String} padding_y (0|1|2|3|4|5)
2003  *
2004  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2008  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2009  
2010  * @config {Boolean} dragable  if this card can be dragged.
2011  * @config {String} drag_group  group for drag
2012  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2013  * @config {String} drop_group  group for drag
2014  * 
2015  * @config {Boolean} collapsable can the body be collapsed.
2016  * @config {Boolean} collapsed is the body collapsed when rendered...
2017  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2018  * @config {Boolean} rotated is the body rotated when rendered...
2019  * 
2020  * @constructor
2021  * Create a new Container
2022  * @param {Object} config The config object
2023  */
2024
2025 Roo.bootstrap.Card = function(config){
2026     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027     
2028     this.addEvents({
2029          // raw events
2030         /**
2031          * @event drop
2032          * When a element a card is dropped
2033          * @param {Roo.bootstrap.Card} this
2034          *
2035          * 
2036          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2037          * @param {String} position 'above' or 'below'
2038          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039         
2040          */
2041         'drop' : true,
2042          /**
2043          * @event rotate
2044          * When a element a card is rotate
2045          * @param {Roo.bootstrap.Card} this
2046          * @param {Roo.Element} n the node being dropped?
2047          * @param {Boolean} rotate status
2048          */
2049         'rotate' : true,
2050         /**
2051          * @event cardover
2052          * When a card element is dragged over ready to drop (return false to block dropable)
2053          * @param {Roo.bootstrap.Card} this
2054          * @param {Object} data from dragdrop 
2055          */
2056          'cardover' : true
2057          
2058     });
2059 };
2060
2061
2062 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2063     
2064     
2065     weight : '',
2066     
2067     margin: '', /// may be better in component?
2068     margin_top: '', 
2069     margin_bottom: '', 
2070     margin_left: '',
2071     margin_right: '',
2072     margin_x: '',
2073     margin_y: '',
2074     
2075     padding : '',
2076     padding_top: '', 
2077     padding_bottom: '', 
2078     padding_left: '',
2079     padding_right: '',
2080     padding_x: '',
2081     padding_y: '',
2082     
2083     display: '', 
2084     display_xs: '', 
2085     display_sm: '', 
2086     display_lg: '',
2087     display_xl: '',
2088  
2089     header_image  : '',
2090     header : '',
2091     header_size : 0,
2092     title : '',
2093     subtitle : '',
2094     html : '',
2095     footer: '',
2096
2097     collapsable : false,
2098     collapsed : false,
2099     rotateable : false,
2100     rotated : false,
2101     
2102     dragable : false,
2103     drag_group : false,
2104     dropable : false,
2105     drop_group : false,
2106     childContainer : false,
2107     dropEl : false, /// the dom placeholde element that indicates drop location.
2108     containerEl: false, // body container
2109     bodyEl: false, // card-body
2110     headerContainerEl : false, //
2111     headerEl : false,
2112     header_imageEl : false,
2113     
2114     
2115     layoutCls : function()
2116     {
2117         var cls = '';
2118         var t = this;
2119         Roo.log(this.margin_bottom.length);
2120         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2121             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2122             
2123             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2124                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2125             }
2126             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2127                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2128             }
2129         });
2130         
2131         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2132             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2133                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2134             }
2135         });
2136         
2137         // more generic support?
2138         if (this.hidden) {
2139             cls += ' d-none';
2140         }
2141         
2142         return cls;
2143     },
2144  
2145        // Roo.log("Call onRender: " + this.xtype);
2146         /*  We are looking at something like this.
2147 <div class="card">
2148     <img src="..." class="card-img-top" alt="...">
2149     <div class="card-body">
2150         <h5 class="card-title">Card title</h5>
2151          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2152
2153         >> this bit is really the body...
2154         <div> << we will ad dthis in hopefully it will not break shit.
2155         
2156         ** card text does not actually have any styling...
2157         
2158             <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>
2159         
2160         </div> <<
2161           <a href="#" class="card-link">Card link</a>
2162           
2163     </div>
2164     <div class="card-footer">
2165         <small class="text-muted">Last updated 3 mins ago</small>
2166     </div>
2167 </div>
2168          */
2169     getAutoCreate : function(){
2170         
2171         var cfg = {
2172             tag : 'div',
2173             cls : 'card',
2174             cn : [ ]
2175         };
2176         
2177         if (this.weight.length && this.weight != 'light') {
2178             cfg.cls += ' text-white';
2179         } else {
2180             cfg.cls += ' text-dark'; // need as it's nested..
2181         }
2182         if (this.weight.length) {
2183             cfg.cls += ' bg-' + this.weight;
2184         }
2185         
2186         cfg.cls += ' ' + this.layoutCls(); 
2187         
2188         var hdr = false;
2189         var hdr_ctr = false;
2190         if (this.header.length) {
2191             hdr = {
2192                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2193                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2194                 cn : []
2195             };
2196             cfg.cn.push(hdr);
2197             hdr_ctr = hdr;
2198         } else {
2199             hdr = {
2200                 tag : 'div',
2201                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202                 cn : []
2203             };
2204             cfg.cn.push(hdr);
2205             hdr_ctr = hdr;
2206         }
2207         if (this.collapsable) {
2208             hdr_ctr = {
2209             tag : 'a',
2210             cls : 'd-block user-select-none',
2211             cn: [
2212                     {
2213                         tag: 'i',
2214                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2215                     }
2216                    
2217                 ]
2218             };
2219             hdr.cn.push(hdr_ctr);
2220         }
2221         
2222         hdr_ctr.cn.push(        {
2223             tag: 'span',
2224             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2225             html : this.header
2226         });
2227         
2228         
2229         if (this.header_image.length) {
2230             cfg.cn.push({
2231                 tag : 'img',
2232                 cls : 'card-img-top',
2233                 src: this.header_image // escape?
2234             });
2235         } else {
2236             cfg.cn.push({
2237                     tag : 'div',
2238                     cls : 'card-img-top d-none' 
2239                 });
2240         }
2241             
2242         var body = {
2243             tag : 'div',
2244             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2245             cn : []
2246         };
2247         var obody = body;
2248         if (this.collapsable || this.rotateable) {
2249             obody = {
2250                 tag: 'div',
2251                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2252                 cn : [  body ]
2253             };
2254         }
2255         
2256         cfg.cn.push(obody);
2257         
2258         if (this.title.length) {
2259             body.cn.push({
2260                 tag : 'div',
2261                 cls : 'card-title',
2262                 src: this.title // escape?
2263             });
2264         }  
2265         
2266         if (this.subtitle.length) {
2267             body.cn.push({
2268                 tag : 'div',
2269                 cls : 'card-title',
2270                 src: this.subtitle // escape?
2271             });
2272         }
2273         
2274         body.cn.push({
2275             tag : 'div',
2276             cls : 'roo-card-body-ctr'
2277         });
2278         
2279         if (this.html.length) {
2280             body.cn.push({
2281                 tag: 'div',
2282                 html : this.html
2283             });
2284         }
2285         // fixme ? handle objects?
2286         
2287         if (this.footer.length) {
2288            
2289             cfg.cn.push({
2290                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2291                 html : this.footer
2292             });
2293             
2294         } else {
2295             cfg.cn.push({cls : 'card-footer d-none'});
2296         }
2297         
2298         // footer...
2299         
2300         return cfg;
2301     },
2302     
2303     
2304     getCardHeader : function()
2305     {
2306         var  ret = this.el.select('.card-header',true).first();
2307         if (ret.hasClass('d-none')) {
2308             ret.removeClass('d-none');
2309         }
2310         
2311         return ret;
2312     },
2313     getCardFooter : function()
2314     {
2315         var  ret = this.el.select('.card-footer',true).first();
2316         if (ret.hasClass('d-none')) {
2317             ret.removeClass('d-none');
2318         }
2319         
2320         return ret;
2321     },
2322     getCardImageTop : function()
2323     {
2324         var  ret = this.header_imageEl;
2325         if (ret.hasClass('d-none')) {
2326             ret.removeClass('d-none');
2327         }
2328             
2329         return ret;
2330     },
2331     
2332     getChildContainer : function()
2333     {
2334         
2335         if(!this.el){
2336             return false;
2337         }
2338         return this.el.select('.roo-card-body-ctr',true).first();    
2339     },
2340     
2341     initEvents: function() 
2342     {
2343         this.bodyEl = this.el.select('.card-body',true).first(); 
2344         this.containerEl = this.getChildContainer();
2345         if(this.dragable){
2346             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2347                     containerScroll: true,
2348                     ddGroup: this.drag_group || 'default_card_drag_group'
2349             });
2350             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2351         }
2352         if (this.dropable) {
2353             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2354                 containerScroll: true,
2355                 ddGroup: this.drop_group || 'default_card_drag_group'
2356             });
2357             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2358             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2359             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2360             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2361             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2362         }
2363         
2364         if (this.collapsable) {
2365             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2366         }
2367         if (this.rotateable) {
2368             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2369         }
2370         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2371          
2372         this.footerEl = this.el.select('.card-footer',true).first();
2373         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2374         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2375         this.headerEl = this.el.select('.card-header',true).first();
2376         
2377         if (this.rotated) {
2378             this.el.addClass('roo-card-rotated');
2379             this.fireEvent('rotate', this, true);
2380         }
2381         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2382         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2383         
2384     },
2385     getDragData : function(e)
2386     {
2387         var target = this.getEl();
2388         if (target) {
2389             //this.handleSelection(e);
2390             
2391             var dragData = {
2392                 source: this,
2393                 copy: false,
2394                 nodes: this.getEl(),
2395                 records: []
2396             };
2397             
2398             
2399             dragData.ddel = target.dom ;    // the div element
2400             Roo.log(target.getWidth( ));
2401             dragData.ddel.style.width = target.getWidth() + 'px';
2402             
2403             return dragData;
2404         }
2405         return false;
2406     },
2407     /**
2408     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2409     *    whole Element becomes the target, and this causes the drop gesture to append.
2410     *
2411     *    Returns an object:
2412     *     {
2413            
2414            position : 'below' or 'above'
2415            card  : relateive to card OBJECT (or true for no cards listed)
2416            items_n : relative to nth item in list
2417            card_n : relative to  nth card in list
2418     }
2419     *
2420     *    
2421     */
2422     getTargetFromEvent : function(e, dragged_card_el)
2423     {
2424         var target = e.getTarget();
2425         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2426             target = target.parentNode;
2427         }
2428         
2429         var ret = {
2430             position: '',
2431             cards : [],
2432             card_n : -1,
2433             items_n : -1,
2434             card : false 
2435         };
2436         
2437         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2438         // see if target is one of the 'cards'...
2439         
2440         
2441         //Roo.log(this.items.length);
2442         var pos = false;
2443         
2444         var last_card_n = 0;
2445         var cards_len  = 0;
2446         for (var i = 0;i< this.items.length;i++) {
2447             
2448             if (!this.items[i].el.hasClass('card')) {
2449                  continue;
2450             }
2451             pos = this.getDropPoint(e, this.items[i].el.dom);
2452             
2453             cards_len = ret.cards.length;
2454             //Roo.log(this.items[i].el.dom.id);
2455             ret.cards.push(this.items[i]);
2456             last_card_n  = i;
2457             if (ret.card_n < 0 && pos == 'above') {
2458                 ret.position = cards_len > 0 ? 'below' : pos;
2459                 ret.items_n = i > 0 ? i - 1 : 0;
2460                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2461                 ret.card = ret.cards[ret.card_n];
2462             }
2463         }
2464         if (!ret.cards.length) {
2465             ret.card = true;
2466             ret.position = 'below';
2467             ret.items_n;
2468             return ret;
2469         }
2470         // could not find a card.. stick it at the end..
2471         if (ret.card_n < 0) {
2472             ret.card_n = last_card_n;
2473             ret.card = ret.cards[last_card_n];
2474             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2475             ret.position = 'below';
2476         }
2477         
2478         if (this.items[ret.items_n].el == dragged_card_el) {
2479             return false;
2480         }
2481         
2482         if (ret.position == 'below') {
2483             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2484             
2485             if (card_after  && card_after.el == dragged_card_el) {
2486                 return false;
2487             }
2488             return ret;
2489         }
2490         
2491         // its's after ..
2492         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2493         
2494         if (card_before  && card_before.el == dragged_card_el) {
2495             return false;
2496         }
2497         
2498         return ret;
2499     },
2500     
2501     onNodeEnter : function(n, dd, e, data){
2502         return false;
2503     },
2504     onNodeOver : function(n, dd, e, data)
2505     {
2506        
2507         var target_info = this.getTargetFromEvent(e,data.source.el);
2508         if (target_info === false) {
2509             this.dropPlaceHolder('hide');
2510             return false;
2511         }
2512         Roo.log(['getTargetFromEvent', target_info ]);
2513         
2514         
2515         if (this.fireEvent('cardover', this, [ data ]) === false) {
2516             return false;
2517         }
2518         
2519         this.dropPlaceHolder('show', target_info,data);
2520         
2521         return false; 
2522     },
2523     onNodeOut : function(n, dd, e, data){
2524         this.dropPlaceHolder('hide');
2525      
2526     },
2527     onNodeDrop : function(n, dd, e, data)
2528     {
2529         
2530         // call drop - return false if
2531         
2532         // this could actually fail - if the Network drops..
2533         // we will ignore this at present..- client should probably reload
2534         // the whole set of cards if stuff like that fails.
2535         
2536         
2537         var info = this.getTargetFromEvent(e,data.source.el);
2538         if (info === false) {
2539             return false;
2540         }
2541         this.dropPlaceHolder('hide');
2542   
2543           
2544     
2545         this.acceptCard(data.source, info.position, info.card, info.items_n);
2546         return true;
2547          
2548     },
2549     firstChildCard : function()
2550     {
2551         for (var i = 0;i< this.items.length;i++) {
2552             
2553             if (!this.items[i].el.hasClass('card')) {
2554                  continue;
2555             }
2556             return this.items[i];
2557         }
2558         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2559     },
2560     /**
2561      * accept card
2562      *
2563      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2564      */
2565     acceptCard : function(move_card,  position, next_to_card )
2566     {
2567         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2568             return false;
2569         }
2570         
2571         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2572         
2573         move_card.parent().removeCard(move_card);
2574         
2575         
2576         var dom = move_card.el.dom;
2577         dom.style.width = ''; // clear with - which is set by drag.
2578         
2579         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2580             var cardel = next_to_card.el.dom;
2581             
2582             if (position == 'above' ) {
2583                 cardel.parentNode.insertBefore(dom, cardel);
2584             } else if (cardel.nextSibling) {
2585                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2586             } else {
2587                 cardel.parentNode.append(dom);
2588             }
2589         } else {
2590             // card container???
2591             this.containerEl.dom.append(dom);
2592         }
2593         
2594         //FIXME HANDLE card = true 
2595         
2596         // add this to the correct place in items.
2597         
2598         // remove Card from items.
2599         
2600        
2601         if (this.items.length) {
2602             var nitems = [];
2603             //Roo.log([info.items_n, info.position, this.items.length]);
2604             for (var i =0; i < this.items.length; i++) {
2605                 if (i == to_items_n && position == 'above') {
2606                     nitems.push(move_card);
2607                 }
2608                 nitems.push(this.items[i]);
2609                 if (i == to_items_n && position == 'below') {
2610                     nitems.push(move_card);
2611                 }
2612             }
2613             this.items = nitems;
2614             Roo.log(this.items);
2615         } else {
2616             this.items.push(move_card);
2617         }
2618         
2619         move_card.parentId = this.id;
2620         
2621         return true;
2622         
2623         
2624     },
2625     removeCard : function(c)
2626     {
2627         this.items = this.items.filter(function(e) { return e != c });
2628  
2629         var dom = c.el.dom;
2630         dom.parentNode.removeChild(dom);
2631         dom.style.width = ''; // clear with - which is set by drag.
2632         c.parentId = false;
2633         
2634     },
2635     
2636     /**    Decide whether to drop above or below a View node. */
2637     getDropPoint : function(e, n, dd)
2638     {
2639         if (dd) {
2640              return false;
2641         }
2642         if (n == this.containerEl.dom) {
2643             return "above";
2644         }
2645         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2646         var c = t + (b - t) / 2;
2647         var y = Roo.lib.Event.getPageY(e);
2648         if(y <= c) {
2649             return "above";
2650         }else{
2651             return "below";
2652         }
2653     },
2654     onToggleCollapse : function(e)
2655         {
2656         if (this.collapsed) {
2657             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2658             this.collapsableEl.addClass('show');
2659             this.collapsed = false;
2660             return;
2661         }
2662         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2663         this.collapsableEl.removeClass('show');
2664         this.collapsed = true;
2665         
2666     
2667     },
2668     
2669     onToggleRotate : function(e)
2670     {
2671         this.collapsableEl.removeClass('show');
2672         this.footerEl.removeClass('d-none');
2673         this.el.removeClass('roo-card-rotated');
2674         this.el.removeClass('d-none');
2675         if (this.rotated) {
2676             
2677             this.collapsableEl.addClass('show');
2678             this.rotated = false;
2679             this.fireEvent('rotate', this, this.rotated);
2680             return;
2681         }
2682         this.el.addClass('roo-card-rotated');
2683         this.footerEl.addClass('d-none');
2684         this.el.select('.roo-collapsable').removeClass('show');
2685         
2686         this.rotated = true;
2687         this.fireEvent('rotate', this, this.rotated);
2688     
2689     },
2690     
2691     dropPlaceHolder: function (action, info, data)
2692     {
2693         if (this.dropEl === false) {
2694             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2695             cls : 'd-none'
2696             },true);
2697         }
2698         this.dropEl.removeClass(['d-none', 'd-block']);        
2699         if (action == 'hide') {
2700             
2701             this.dropEl.addClass('d-none');
2702             return;
2703         }
2704         // FIXME - info.card == true!!!
2705         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2706         
2707         if (info.card !== true) {
2708             var cardel = info.card.el.dom;
2709             
2710             if (info.position == 'above') {
2711                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2712             } else if (cardel.nextSibling) {
2713                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2714             } else {
2715                 cardel.parentNode.append(this.dropEl.dom);
2716             }
2717         } else {
2718             // card container???
2719             this.containerEl.dom.append(this.dropEl.dom);
2720         }
2721         
2722         this.dropEl.addClass('d-block roo-card-dropzone');
2723         
2724         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2725         
2726         
2727     
2728     
2729     
2730     },
2731     setHeaderText: function(html)
2732     {
2733         this.header = html;
2734         if (this.headerContainerEl) {
2735             this.headerContainerEl.dom.innerHTML = html;
2736         }
2737     },
2738     onHeaderImageLoad : function(ev, he)
2739     {
2740         if (!this.header_image_fit_square) {
2741             return;
2742         }
2743         
2744         var hw = he.naturalHeight / he.naturalWidth;
2745         // wide image = < 0
2746         // tall image = > 1
2747         //var w = he.dom.naturalWidth;
2748         var ww = he.width;
2749         he.style.left =  0;
2750         he.style.position =  'relative';
2751         if (hw > 1) {
2752             var nw = (ww * (1/hw));
2753             Roo.get(he).setSize( ww * (1/hw),  ww);
2754             he.style.left =  ((ww - nw)/ 2) + 'px';
2755             he.style.position =  'relative';
2756         }
2757
2758     }
2759
2760     
2761 });
2762
2763 /*
2764  * - LGPL
2765  *
2766  * Card header - holder for the card header elements.
2767  * 
2768  */
2769
2770 /**
2771  * @class Roo.bootstrap.CardHeader
2772  * @extends Roo.bootstrap.Element
2773  * Bootstrap CardHeader class
2774  * @constructor
2775  * Create a new Card Header - that you can embed children into
2776  * @param {Object} config The config object
2777  */
2778
2779 Roo.bootstrap.CardHeader = function(config){
2780     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2781 };
2782
2783 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2784     
2785     
2786     container_method : 'getCardHeader' 
2787     
2788      
2789     
2790     
2791    
2792 });
2793
2794  
2795
2796  /*
2797  * - LGPL
2798  *
2799  * Card footer - holder for the card footer elements.
2800  * 
2801  */
2802
2803 /**
2804  * @class Roo.bootstrap.CardFooter
2805  * @extends Roo.bootstrap.Element
2806  * Bootstrap CardFooter class
2807  * @constructor
2808  * Create a new Card Footer - that you can embed children into
2809  * @param {Object} config The config object
2810  */
2811
2812 Roo.bootstrap.CardFooter = function(config){
2813     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2814 };
2815
2816 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2817     
2818     
2819     container_method : 'getCardFooter' 
2820     
2821      
2822     
2823     
2824    
2825 });
2826
2827  
2828
2829  /*
2830  * - LGPL
2831  *
2832  * Card header - holder for the card header elements.
2833  * 
2834  */
2835
2836 /**
2837  * @class Roo.bootstrap.CardImageTop
2838  * @extends Roo.bootstrap.Element
2839  * Bootstrap CardImageTop class
2840  * @constructor
2841  * Create a new Card Image Top container
2842  * @param {Object} config The config object
2843  */
2844
2845 Roo.bootstrap.CardImageTop = function(config){
2846     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2847 };
2848
2849 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2850     
2851    
2852     container_method : 'getCardImageTop' 
2853     
2854      
2855     
2856    
2857 });
2858
2859  
2860
2861  
2862 /*
2863 * Licence: LGPL
2864 */
2865
2866 /**
2867  * @class Roo.bootstrap.ButtonUploader
2868  * @extends Roo.bootstrap.Button
2869  * Bootstrap Button Uploader class - it's a button which when you add files to it
2870  *
2871  * 
2872  * @cfg {Number} errorTimeout default 3000
2873  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2874  * @cfg {Array}  html The button text.
2875  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2876  *
2877  * @constructor
2878  * Create a new CardUploader
2879  * @param {Object} config The config object
2880  */
2881
2882 Roo.bootstrap.ButtonUploader = function(config){
2883     
2884  
2885     
2886     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887     
2888      
2889      this.addEvents({
2890          // raw events
2891         /**
2892          * @event beforeselect
2893          * When button is pressed, before show upload files dialog is shown
2894          * @param {Roo.bootstrap.UploaderButton} this
2895          *
2896          */
2897         'beforeselect' : true,
2898          /**
2899          * @event fired when files have been selected, 
2900          * When a the download link is clicked
2901          * @param {Roo.bootstrap.UploaderButton} this
2902          * @param {Array} Array of files that have been uploaded
2903          */
2904         'uploaded' : true
2905         
2906     });
2907 };
2908  
2909 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2910     
2911      
2912     errorTimeout : 3000,
2913      
2914     images : false,
2915    
2916     fileCollection : false,
2917     allowBlank : true,
2918     
2919     multiple : true,
2920     
2921     getAutoCreate : function()
2922     {
2923         var im = {
2924             tag: 'input',
2925             type : 'file',
2926             cls : 'd-none  roo-card-upload-selector' 
2927           
2928         };
2929         if (this.multiple) {
2930             im.multiple = 'multiple';
2931         }
2932         
2933         return  {
2934             cls :'div' ,
2935             cn : [
2936                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2937                 im
2938
2939             ]
2940         };
2941            
2942          
2943     },
2944      
2945    
2946     initEvents : function()
2947     {
2948         
2949         Roo.bootstrap.Button.prototype.initEvents.call(this);
2950         
2951         
2952         
2953         
2954         
2955         this.urlAPI = (window.createObjectURL && window) || 
2956                                 (window.URL && URL.revokeObjectURL && URL) || 
2957                                 (window.webkitURL && webkitURL);
2958                         
2959          
2960          
2961          
2962         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2963         
2964         this.selectorEl.on('change', this.onFileSelected, this);
2965          
2966          
2967        
2968     },
2969     
2970    
2971     onClick : function(e)
2972     {
2973         e.preventDefault();
2974         
2975         if ( this.fireEvent('beforeselect', this) === false) {
2976             return;
2977         }
2978          
2979         this.selectorEl.dom.click();
2980          
2981     },
2982     
2983     onFileSelected : function(e)
2984     {
2985         e.preventDefault();
2986         
2987         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2988             return;
2989         }
2990         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2991         this.selectorEl.dom.value  = '';// hopefully reset..
2992         
2993         this.fireEvent('uploaded', this,  files );
2994         
2995     },
2996     
2997        
2998    
2999     
3000     /**
3001      * addCard - add an Attachment to the uploader
3002      * @param data - the data about the image to upload
3003      *
3004      * {
3005           id : 123
3006           title : "Title of file",
3007           is_uploaded : false,
3008           src : "http://.....",
3009           srcfile : { the File upload object },
3010           mimetype : file.type,
3011           preview : false,
3012           is_deleted : 0
3013           .. any other data...
3014         }
3015      *
3016      * 
3017     */
3018      
3019     reset: function()
3020     {
3021          
3022          this.selectorEl
3023     } 
3024     
3025     
3026     
3027     
3028 });
3029  /*
3030  * - LGPL
3031  *
3032  * image
3033  * 
3034  */
3035
3036
3037 /**
3038  * @class Roo.bootstrap.Img
3039  * @extends Roo.bootstrap.Component
3040  * Bootstrap Img class
3041  * @cfg {Boolean} imgResponsive false | true
3042  * @cfg {String} border rounded | circle | thumbnail
3043  * @cfg {String} src image source
3044  * @cfg {String} alt image alternative text
3045  * @cfg {String} href a tag href
3046  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3047  * @cfg {String} xsUrl xs image source
3048  * @cfg {String} smUrl sm image source
3049  * @cfg {String} mdUrl md image source
3050  * @cfg {String} lgUrl lg image source
3051  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3052  * 
3053  * @constructor
3054  * Create a new Input
3055  * @param {Object} config The config object
3056  */
3057
3058 Roo.bootstrap.Img = function(config){
3059     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060     
3061     this.addEvents({
3062         // img events
3063         /**
3064          * @event click
3065          * The img click event for the img.
3066          * @param {Roo.EventObject} e
3067          */
3068         "click" : true,
3069         /**
3070          * @event load
3071          * The when any image loads
3072          * @param {Roo.EventObject} e
3073          */
3074         "load" : true
3075     });
3076 };
3077
3078 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3079     
3080     imgResponsive: true,
3081     border: '',
3082     src: 'about:blank',
3083     href: false,
3084     target: false,
3085     xsUrl: '',
3086     smUrl: '',
3087     mdUrl: '',
3088     lgUrl: '',
3089     backgroundContain : false,
3090
3091     getAutoCreate : function()
3092     {   
3093         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3094             return this.createSingleImg();
3095         }
3096         
3097         var cfg = {
3098             tag: 'div',
3099             cls: 'roo-image-responsive-group',
3100             cn: []
3101         };
3102         var _this = this;
3103         
3104         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3105             
3106             if(!_this[size + 'Url']){
3107                 return;
3108             }
3109             
3110             var img = {
3111                 tag: 'img',
3112                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3113                 html: _this.html || cfg.html,
3114                 src: _this[size + 'Url']
3115             };
3116             
3117             img.cls += ' roo-image-responsive-' + size;
3118             
3119             var s = ['xs', 'sm', 'md', 'lg'];
3120             
3121             s.splice(s.indexOf(size), 1);
3122             
3123             Roo.each(s, function(ss){
3124                 img.cls += ' hidden-' + ss;
3125             });
3126             
3127             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3128                 cfg.cls += ' img-' + _this.border;
3129             }
3130             
3131             if(_this.alt){
3132                 cfg.alt = _this.alt;
3133             }
3134             
3135             if(_this.href){
3136                 var a = {
3137                     tag: 'a',
3138                     href: _this.href,
3139                     cn: [
3140                         img
3141                     ]
3142                 };
3143
3144                 if(this.target){
3145                     a.target = _this.target;
3146                 }
3147             }
3148             
3149             cfg.cn.push((_this.href) ? a : img);
3150             
3151         });
3152         
3153         return cfg;
3154     },
3155     
3156     createSingleImg : function()
3157     {
3158         var cfg = {
3159             tag: 'img',
3160             cls: (this.imgResponsive) ? 'img-responsive' : '',
3161             html : null,
3162             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3163         };
3164         
3165         if (this.backgroundContain) {
3166             cfg.cls += ' background-contain';
3167         }
3168         
3169         cfg.html = this.html || cfg.html;
3170         
3171         if (this.backgroundContain) {
3172             cfg.style="background-image: url(" + this.src + ')';
3173         } else {
3174             cfg.src = this.src || cfg.src;
3175         }
3176         
3177         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3178             cfg.cls += ' img-' + this.border;
3179         }
3180         
3181         if(this.alt){
3182             cfg.alt = this.alt;
3183         }
3184         
3185         if(this.href){
3186             var a = {
3187                 tag: 'a',
3188                 href: this.href,
3189                 cn: [
3190                     cfg
3191                 ]
3192             };
3193             
3194             if(this.target){
3195                 a.target = this.target;
3196             }
3197             
3198         }
3199         
3200         return (this.href) ? a : cfg;
3201     },
3202     
3203     initEvents: function() 
3204     {
3205         if(!this.href){
3206             this.el.on('click', this.onClick, this);
3207         }
3208         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3209             this.el.on('load', this.onImageLoad, this);
3210         } else {
3211             // not sure if this works.. not tested
3212             this.el.select('img', true).on('load', this.onImageLoad, this);
3213         }
3214         
3215     },
3216     
3217     onClick : function(e)
3218     {
3219         Roo.log('img onclick');
3220         this.fireEvent('click', this, e);
3221     },
3222     onImageLoad: function(e)
3223     {
3224         Roo.log('img load');
3225         this.fireEvent('load', this, e);
3226     },
3227     
3228     /**
3229      * Sets the url of the image - used to update it
3230      * @param {String} url the url of the image
3231      */
3232     
3233     setSrc : function(url)
3234     {
3235         this.src =  url;
3236         
3237         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3238             if (this.backgroundContain) {
3239                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3240             } else {
3241                 this.el.dom.src =  url;
3242             }
3243             return;
3244         }
3245         
3246         this.el.select('img', true).first().dom.src =  url;
3247     }
3248     
3249     
3250    
3251 });
3252
3253  /*
3254  * - LGPL
3255  *
3256  * image
3257  * 
3258  */
3259
3260
3261 /**
3262  * @class Roo.bootstrap.Link
3263  * @extends Roo.bootstrap.Component
3264  * Bootstrap Link Class
3265  * @cfg {String} alt image alternative text
3266  * @cfg {String} href a tag href
3267  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3268  * @cfg {String} html the content of the link.
3269  * @cfg {String} anchor name for the anchor link
3270  * @cfg {String} fa - favicon
3271
3272  * @cfg {Boolean} preventDefault (true | false) default false
3273
3274  * 
3275  * @constructor
3276  * Create a new Input
3277  * @param {Object} config The config object
3278  */
3279
3280 Roo.bootstrap.Link = function(config){
3281     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282     
3283     this.addEvents({
3284         // img events
3285         /**
3286          * @event click
3287          * The img click event for the img.
3288          * @param {Roo.EventObject} e
3289          */
3290         "click" : true
3291     });
3292 };
3293
3294 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3295     
3296     href: false,
3297     target: false,
3298     preventDefault: false,
3299     anchor : false,
3300     alt : false,
3301     fa: false,
3302
3303
3304     getAutoCreate : function()
3305     {
3306         var html = this.html || '';
3307         
3308         if (this.fa !== false) {
3309             html = '<i class="fa fa-' + this.fa + '"></i>';
3310         }
3311         var cfg = {
3312             tag: 'a'
3313         };
3314         // anchor's do not require html/href...
3315         if (this.anchor === false) {
3316             cfg.html = html;
3317             cfg.href = this.href || '#';
3318         } else {
3319             cfg.name = this.anchor;
3320             if (this.html !== false || this.fa !== false) {
3321                 cfg.html = html;
3322             }
3323             if (this.href !== false) {
3324                 cfg.href = this.href;
3325             }
3326         }
3327         
3328         if(this.alt !== false){
3329             cfg.alt = this.alt;
3330         }
3331         
3332         
3333         if(this.target !== false) {
3334             cfg.target = this.target;
3335         }
3336         
3337         return cfg;
3338     },
3339     
3340     initEvents: function() {
3341         
3342         if(!this.href || this.preventDefault){
3343             this.el.on('click', this.onClick, this);
3344         }
3345     },
3346     
3347     onClick : function(e)
3348     {
3349         if(this.preventDefault){
3350             e.preventDefault();
3351         }
3352         //Roo.log('img onclick');
3353         this.fireEvent('click', this, e);
3354     }
3355    
3356 });
3357
3358  /*
3359  * - LGPL
3360  *
3361  * header
3362  * 
3363  */
3364
3365 /**
3366  * @class Roo.bootstrap.Header
3367  * @extends Roo.bootstrap.Component
3368  * Bootstrap Header class
3369  * @cfg {String} html content of header
3370  * @cfg {Number} level (1|2|3|4|5|6) default 1
3371  * 
3372  * @constructor
3373  * Create a new Header
3374  * @param {Object} config The config object
3375  */
3376
3377
3378 Roo.bootstrap.Header  = function(config){
3379     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3380 };
3381
3382 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3383     
3384     //href : false,
3385     html : false,
3386     level : 1,
3387     
3388     
3389     
3390     getAutoCreate : function(){
3391         
3392         
3393         
3394         var cfg = {
3395             tag: 'h' + (1 *this.level),
3396             html: this.html || ''
3397         } ;
3398         
3399         return cfg;
3400     }
3401    
3402 });
3403
3404  
3405
3406  /*
3407  * Based on:
3408  * Ext JS Library 1.1.1
3409  * Copyright(c) 2006-2007, Ext JS, LLC.
3410  *
3411  * Originally Released Under LGPL - original licence link has changed is not relivant.
3412  *
3413  * Fork - LGPL
3414  * <script type="text/javascript">
3415  */
3416  
3417 /**
3418  * @class Roo.bootstrap.MenuMgr
3419  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3420  * @singleton
3421  */
3422 Roo.bootstrap.MenuMgr = function(){
3423    var menus, active, groups = {}, attached = false, lastShow = new Date();
3424
3425    // private - called when first menu is created
3426    function init(){
3427        menus = {};
3428        active = new Roo.util.MixedCollection();
3429        Roo.get(document).addKeyListener(27, function(){
3430            if(active.length > 0){
3431                hideAll();
3432            }
3433        });
3434    }
3435
3436    // private
3437    function hideAll(){
3438        if(active && active.length > 0){
3439            var c = active.clone();
3440            c.each(function(m){
3441                m.hide();
3442            });
3443        }
3444    }
3445
3446    // private
3447    function onHide(m){
3448        active.remove(m);
3449        if(active.length < 1){
3450            Roo.get(document).un("mouseup", onMouseDown);
3451             
3452            attached = false;
3453        }
3454    }
3455
3456    // private
3457    function onShow(m){
3458        var last = active.last();
3459        lastShow = new Date();
3460        active.add(m);
3461        if(!attached){
3462           Roo.get(document).on("mouseup", onMouseDown);
3463            
3464            attached = true;
3465        }
3466        if(m.parentMenu){
3467           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3468           m.parentMenu.activeChild = m;
3469        }else if(last && last.isVisible()){
3470           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3471        }
3472    }
3473
3474    // private
3475    function onBeforeHide(m){
3476        if(m.activeChild){
3477            m.activeChild.hide();
3478        }
3479        if(m.autoHideTimer){
3480            clearTimeout(m.autoHideTimer);
3481            delete m.autoHideTimer;
3482        }
3483    }
3484
3485    // private
3486    function onBeforeShow(m){
3487        var pm = m.parentMenu;
3488        if(!pm && !m.allowOtherMenus){
3489            hideAll();
3490        }else if(pm && pm.activeChild && active != m){
3491            pm.activeChild.hide();
3492        }
3493    }
3494
3495    // private this should really trigger on mouseup..
3496    function onMouseDown(e){
3497         Roo.log("on Mouse Up");
3498         
3499         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3500             Roo.log("MenuManager hideAll");
3501             hideAll();
3502             e.stopEvent();
3503         }
3504         
3505         
3506    }
3507
3508    // private
3509    function onBeforeCheck(mi, state){
3510        if(state){
3511            var g = groups[mi.group];
3512            for(var i = 0, l = g.length; i < l; i++){
3513                if(g[i] != mi){
3514                    g[i].setChecked(false);
3515                }
3516            }
3517        }
3518    }
3519
3520    return {
3521
3522        /**
3523         * Hides all menus that are currently visible
3524         */
3525        hideAll : function(){
3526             hideAll();  
3527        },
3528
3529        // private
3530        register : function(menu){
3531            if(!menus){
3532                init();
3533            }
3534            menus[menu.id] = menu;
3535            menu.on("beforehide", onBeforeHide);
3536            menu.on("hide", onHide);
3537            menu.on("beforeshow", onBeforeShow);
3538            menu.on("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                if(!groups[g]){
3542                    groups[g] = [];
3543                }
3544                groups[g].push(menu);
3545                menu.on("checkchange", onCheck);
3546            }
3547        },
3548
3549         /**
3550          * Returns a {@link Roo.menu.Menu} object
3551          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3552          * be used to generate and return a new Menu instance.
3553          */
3554        get : function(menu){
3555            if(typeof menu == "string"){ // menu id
3556                return menus[menu];
3557            }else if(menu.events){  // menu instance
3558                return menu;
3559            }
3560            /*else if(typeof menu.length == 'number'){ // array of menu items?
3561                return new Roo.bootstrap.Menu({items:menu});
3562            }else{ // otherwise, must be a config
3563                return new Roo.bootstrap.Menu(menu);
3564            }
3565            */
3566            return false;
3567        },
3568
3569        // private
3570        unregister : function(menu){
3571            delete menus[menu.id];
3572            menu.un("beforehide", onBeforeHide);
3573            menu.un("hide", onHide);
3574            menu.un("beforeshow", onBeforeShow);
3575            menu.un("show", onShow);
3576            var g = menu.group;
3577            if(g && menu.events["checkchange"]){
3578                groups[g].remove(menu);
3579                menu.un("checkchange", onCheck);
3580            }
3581        },
3582
3583        // private
3584        registerCheckable : function(menuItem){
3585            var g = menuItem.group;
3586            if(g){
3587                if(!groups[g]){
3588                    groups[g] = [];
3589                }
3590                groups[g].push(menuItem);
3591                menuItem.on("beforecheckchange", onBeforeCheck);
3592            }
3593        },
3594
3595        // private
3596        unregisterCheckable : function(menuItem){
3597            var g = menuItem.group;
3598            if(g){
3599                groups[g].remove(menuItem);
3600                menuItem.un("beforecheckchange", onBeforeCheck);
3601            }
3602        }
3603    };
3604 }();/*
3605  * - LGPL
3606  *
3607  * menu
3608  * 
3609  */
3610
3611 /**
3612  * @class Roo.bootstrap.Menu
3613  * @extends Roo.bootstrap.Component
3614  * Bootstrap Menu class - container for MenuItems
3615  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3616  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3617  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3618  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3619   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3620   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3621  
3622  * @constructor
3623  * Create a new Menu
3624  * @param {Object} config The config object
3625  */
3626
3627
3628 Roo.bootstrap.Menu = function(config){
3629     
3630     if (config.type == 'treeview') {
3631         // normally menu's are drawn attached to the document to handle layering etc..
3632         // however treeview (used by the docs menu is drawn into the parent element)
3633         this.container_method = 'getChildContainer'; 
3634     }
3635     
3636     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3637     if (this.registerMenu && this.type != 'treeview')  {
3638         Roo.bootstrap.MenuMgr.register(this);
3639     }
3640     
3641     
3642     this.addEvents({
3643         /**
3644          * @event beforeshow
3645          * Fires before this menu is displayed (return false to block)
3646          * @param {Roo.menu.Menu} this
3647          */
3648         beforeshow : true,
3649         /**
3650          * @event beforehide
3651          * Fires before this menu is hidden (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforehide : true,
3655         /**
3656          * @event show
3657          * Fires after this menu is displayed
3658          * @param {Roo.menu.Menu} this
3659          */
3660         show : true,
3661         /**
3662          * @event hide
3663          * Fires after this menu is hidden
3664          * @param {Roo.menu.Menu} this
3665          */
3666         hide : true,
3667         /**
3668          * @event click
3669          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3670          * @param {Roo.menu.Menu} this
3671          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3672          * @param {Roo.EventObject} e
3673          */
3674         click : true,
3675         /**
3676          * @event mouseover
3677          * Fires when the mouse is hovering over this menu
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.EventObject} e
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          */
3682         mouseover : true,
3683         /**
3684          * @event mouseout
3685          * Fires when the mouse exits this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseout : true,
3691         /**
3692          * @event itemclick
3693          * Fires when a menu item contained in this menu is clicked
3694          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3695          * @param {Roo.EventObject} e
3696          */
3697         itemclick: true
3698     });
3699     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3700 };
3701
3702 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3703     
3704    /// html : false,
3705    
3706     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3707     type: false,
3708     /**
3709      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3710      */
3711     registerMenu : true,
3712     
3713     menuItems :false, // stores the menu items..
3714     
3715     hidden:true,
3716         
3717     parentMenu : false,
3718     
3719     stopEvent : true,
3720     
3721     isLink : false,
3722     
3723     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3724     
3725     hideTrigger : false,
3726     
3727     align : 'tl-bl?',
3728     
3729     
3730     getChildContainer : function() {
3731         return this.el;  
3732     },
3733     
3734     getAutoCreate : function(){
3735          
3736         //if (['right'].indexOf(this.align)!==-1) {
3737         //    cfg.cn[1].cls += ' pull-right'
3738         //}
3739          
3740         var cfg = {
3741             tag : 'ul',
3742             cls : 'dropdown-menu shadow' ,
3743             style : 'z-index:1000'
3744             
3745         };
3746         
3747         if (this.type === 'submenu') {
3748             cfg.cls = 'submenu active';
3749         }
3750         if (this.type === 'treeview') {
3751             cfg.cls = 'treeview-menu';
3752         }
3753         
3754         return cfg;
3755     },
3756     initEvents : function() {
3757         
3758        // Roo.log("ADD event");
3759        // Roo.log(this.triggerEl.dom);
3760         if (this.triggerEl) {
3761             
3762             this.triggerEl.on('click', this.onTriggerClick, this);
3763             
3764             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3765             
3766             if (!this.hideTrigger) {
3767                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3768                     // dropdown toggle on the 'a' in BS4?
3769                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3770                 } else {
3771                     this.triggerEl.addClass('dropdown-toggle');
3772                 }
3773             }
3774         }
3775         
3776         if (Roo.isTouch) {
3777             this.el.on('touchstart'  , this.onTouch, this);
3778         }
3779         this.el.on('click' , this.onClick, this);
3780
3781         this.el.on("mouseover", this.onMouseOver, this);
3782         this.el.on("mouseout", this.onMouseOut, this);
3783         
3784     },
3785     
3786     findTargetItem : function(e)
3787     {
3788         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3789         if(!t){
3790             return false;
3791         }
3792         //Roo.log(t);         Roo.log(t.id);
3793         if(t && t.id){
3794             //Roo.log(this.menuitems);
3795             return this.menuitems.get(t.id);
3796             
3797             //return this.items.get(t.menuItemId);
3798         }
3799         
3800         return false;
3801     },
3802     
3803     onTouch : function(e) 
3804     {
3805         Roo.log("menu.onTouch");
3806         //e.stopEvent(); this make the user popdown broken
3807         this.onClick(e);
3808     },
3809     
3810     onClick : function(e)
3811     {
3812         Roo.log("menu.onClick");
3813         
3814         var t = this.findTargetItem(e);
3815         if(!t || t.isContainer){
3816             return;
3817         }
3818         Roo.log(e);
3819         /*
3820         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3821             if(t == this.activeItem && t.shouldDeactivate(e)){
3822                 this.activeItem.deactivate();
3823                 delete this.activeItem;
3824                 return;
3825             }
3826             if(t.canActivate){
3827                 this.setActiveItem(t, true);
3828             }
3829             return;
3830             
3831             
3832         }
3833         */
3834        
3835         Roo.log('pass click event');
3836         
3837         t.onClick(e);
3838         
3839         this.fireEvent("click", this, t, e);
3840         
3841         var _this = this;
3842         
3843         if(!t.href.length || t.href == '#'){
3844             (function() { _this.hide(); }).defer(100);
3845         }
3846         
3847     },
3848     
3849     onMouseOver : function(e){
3850         var t  = this.findTargetItem(e);
3851         //Roo.log(t);
3852         //if(t){
3853         //    if(t.canActivate && !t.disabled){
3854         //        this.setActiveItem(t, true);
3855         //    }
3856         //}
3857         
3858         this.fireEvent("mouseover", this, e, t);
3859     },
3860     isVisible : function(){
3861         return !this.hidden;
3862     },
3863     onMouseOut : function(e){
3864         var t  = this.findTargetItem(e);
3865         
3866         //if(t ){
3867         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3868         //        this.activeItem.deactivate();
3869         //        delete this.activeItem;
3870         //    }
3871         //}
3872         this.fireEvent("mouseout", this, e, t);
3873     },
3874     
3875     
3876     /**
3877      * Displays this menu relative to another element
3878      * @param {String/HTMLElement/Roo.Element} element The element to align to
3879      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3880      * the element (defaults to this.defaultAlign)
3881      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3882      */
3883     show : function(el, pos, parentMenu)
3884     {
3885         if (false === this.fireEvent("beforeshow", this)) {
3886             Roo.log("show canceled");
3887             return;
3888         }
3889         this.parentMenu = parentMenu;
3890         if(!this.el){
3891             this.render();
3892         }
3893         this.el.addClass('show'); // show otherwise we do not know how big we are..
3894          
3895         var xy = this.el.getAlignToXY(el, pos);
3896         
3897         // bl-tl << left align  below
3898         // tl-bl << left align 
3899         
3900         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3901             // if it goes to far to the right.. -> align left.
3902             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3903         }
3904         if(xy[0] < 0){
3905             // was left align - go right?
3906             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3907         }
3908         
3909         // goes down the bottom
3910         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3911            xy[1]  < 0 ){
3912             var a = this.align.replace('?', '').split('-');
3913             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3914             
3915         }
3916         
3917         this.showAt(  xy , parentMenu, false);
3918     },
3919      /**
3920      * Displays this menu at a specific xy position
3921      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3922      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3923      */
3924     showAt : function(xy, parentMenu, /* private: */_e){
3925         this.parentMenu = parentMenu;
3926         if(!this.el){
3927             this.render();
3928         }
3929         if(_e !== false){
3930             this.fireEvent("beforeshow", this);
3931             //xy = this.el.adjustForConstraints(xy);
3932         }
3933         
3934         //this.el.show();
3935         this.hideMenuItems();
3936         this.hidden = false;
3937         if (this.triggerEl) {
3938             this.triggerEl.addClass('open');
3939         }
3940         
3941         this.el.addClass('show');
3942         
3943         
3944         
3945         // reassign x when hitting right
3946         
3947         // reassign y when hitting bottom
3948         
3949         // but the list may align on trigger left or trigger top... should it be a properity?
3950         
3951         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3952             this.el.setXY(xy);
3953         }
3954         
3955         this.focus();
3956         this.fireEvent("show", this);
3957     },
3958     
3959     focus : function(){
3960         return;
3961         if(!this.hidden){
3962             this.doFocus.defer(50, this);
3963         }
3964     },
3965
3966     doFocus : function(){
3967         if(!this.hidden){
3968             this.focusEl.focus();
3969         }
3970     },
3971
3972     /**
3973      * Hides this menu and optionally all parent menus
3974      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3975      */
3976     hide : function(deep)
3977     {
3978         if (false === this.fireEvent("beforehide", this)) {
3979             Roo.log("hide canceled");
3980             return;
3981         }
3982         this.hideMenuItems();
3983         if(this.el && this.isVisible()){
3984            
3985             if(this.activeItem){
3986                 this.activeItem.deactivate();
3987                 this.activeItem = null;
3988             }
3989             if (this.triggerEl) {
3990                 this.triggerEl.removeClass('open');
3991             }
3992             
3993             this.el.removeClass('show');
3994             this.hidden = true;
3995             this.fireEvent("hide", this);
3996         }
3997         if(deep === true && this.parentMenu){
3998             this.parentMenu.hide(true);
3999         }
4000     },
4001     
4002     onTriggerClick : function(e)
4003     {
4004         Roo.log('trigger click');
4005         
4006         var target = e.getTarget();
4007         
4008         Roo.log(target.nodeName.toLowerCase());
4009         
4010         if(target.nodeName.toLowerCase() === 'i'){
4011             e.preventDefault();
4012         }
4013         
4014     },
4015     
4016     onTriggerPress  : function(e)
4017     {
4018         Roo.log('trigger press');
4019         //Roo.log(e.getTarget());
4020        // Roo.log(this.triggerEl.dom);
4021        
4022         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4023         var pel = Roo.get(e.getTarget());
4024         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4025             Roo.log('is treeview or dropdown?');
4026             return;
4027         }
4028         
4029         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4030             return;
4031         }
4032         
4033         if (this.isVisible()) {
4034             Roo.log('hide');
4035             this.hide();
4036         } else {
4037             Roo.log('show');
4038             
4039             this.show(this.triggerEl, this.align, false);
4040         }
4041         
4042         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4043             e.stopEvent();
4044         }
4045         
4046     },
4047        
4048     
4049     hideMenuItems : function()
4050     {
4051         Roo.log("hide Menu Items");
4052         if (!this.el) { 
4053             return;
4054         }
4055         
4056         this.el.select('.open',true).each(function(aa) {
4057             
4058             aa.removeClass('open');
4059          
4060         });
4061     },
4062     addxtypeChild : function (tree, cntr) {
4063         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4064           
4065         this.menuitems.add(comp);
4066         return comp;
4067
4068     },
4069     getEl : function()
4070     {
4071         Roo.log(this.el);
4072         return this.el;
4073     },
4074     
4075     clear : function()
4076     {
4077         this.getEl().dom.innerHTML = '';
4078         this.menuitems.clear();
4079     }
4080 });
4081
4082  
4083  /*
4084  * - LGPL
4085  *
4086  * menu item
4087  * 
4088  */
4089
4090
4091 /**
4092  * @class Roo.bootstrap.MenuItem
4093  * @extends Roo.bootstrap.Component
4094  * Bootstrap MenuItem class
4095  * @cfg {String} html the menu label
4096  * @cfg {String} href the link
4097  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4098  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4099  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4100  * @cfg {String} fa favicon to show on left of menu item.
4101  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4102  * 
4103  * 
4104  * @constructor
4105  * Create a new MenuItem
4106  * @param {Object} config The config object
4107  */
4108
4109
4110 Roo.bootstrap.MenuItem = function(config){
4111     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4112     this.addEvents({
4113         // raw events
4114         /**
4115          * @event click
4116          * The raw click event for the entire grid.
4117          * @param {Roo.bootstrap.MenuItem} this
4118          * @param {Roo.EventObject} e
4119          */
4120         "click" : true
4121     });
4122 };
4123
4124 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4125     
4126     href : false,
4127     html : false,
4128     preventDefault: false,
4129     isContainer : false,
4130     active : false,
4131     fa: false,
4132     
4133     getAutoCreate : function(){
4134         
4135         if(this.isContainer){
4136             return {
4137                 tag: 'li',
4138                 cls: 'dropdown-menu-item '
4139             };
4140         }
4141         var ctag = {
4142             tag: 'span',
4143             html: 'Link'
4144         };
4145         
4146         var anc = {
4147             tag : 'a',
4148             cls : 'dropdown-item',
4149             href : '#',
4150             cn : [  ]
4151         };
4152         
4153         if (this.fa !== false) {
4154             anc.cn.push({
4155                 tag : 'i',
4156                 cls : 'fa fa-' + this.fa
4157             });
4158         }
4159         
4160         anc.cn.push(ctag);
4161         
4162         
4163         var cfg= {
4164             tag: 'li',
4165             cls: 'dropdown-menu-item',
4166             cn: [ anc ]
4167         };
4168         if (this.parent().type == 'treeview') {
4169             cfg.cls = 'treeview-menu';
4170         }
4171         if (this.active) {
4172             cfg.cls += ' active';
4173         }
4174         
4175         
4176         
4177         anc.href = this.href || cfg.cn[0].href ;
4178         ctag.html = this.html || cfg.cn[0].html ;
4179         return cfg;
4180     },
4181     
4182     initEvents: function()
4183     {
4184         if (this.parent().type == 'treeview') {
4185             this.el.select('a').on('click', this.onClick, this);
4186         }
4187         
4188         if (this.menu) {
4189             this.menu.parentType = this.xtype;
4190             this.menu.triggerEl = this.el;
4191             this.menu = this.addxtype(Roo.apply({}, this.menu));
4192         }
4193         
4194     },
4195     onClick : function(e)
4196     {
4197         Roo.log('item on click ');
4198         
4199         if(this.preventDefault){
4200             e.preventDefault();
4201         }
4202         //this.parent().hideMenuItems();
4203         
4204         this.fireEvent('click', this, e);
4205     },
4206     getEl : function()
4207     {
4208         return this.el;
4209     } 
4210 });
4211
4212  
4213
4214  /*
4215  * - LGPL
4216  *
4217  * menu separator
4218  * 
4219  */
4220
4221
4222 /**
4223  * @class Roo.bootstrap.MenuSeparator
4224  * @extends Roo.bootstrap.Component
4225  * Bootstrap MenuSeparator class
4226  * 
4227  * @constructor
4228  * Create a new MenuItem
4229  * @param {Object} config The config object
4230  */
4231
4232
4233 Roo.bootstrap.MenuSeparator = function(config){
4234     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4235 };
4236
4237 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4238     
4239     getAutoCreate : function(){
4240         var cfg = {
4241             cls: 'divider',
4242             tag : 'li'
4243         };
4244         
4245         return cfg;
4246     }
4247    
4248 });
4249
4250  
4251
4252  
4253 /*
4254 * Licence: LGPL
4255 */
4256
4257 /**
4258  * @class Roo.bootstrap.Modal
4259  * @extends Roo.bootstrap.Component
4260  * @builder-top
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Array} buttons Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4420             }
4421         }
4422
4423         this.items = nitems;
4424
4425         // where are these used - they used to be body/close/footer
4426
4427
4428         this.initEvents();
4429         //this.el.addClass([this.fieldClass, this.cls]);
4430
4431     },
4432
4433     getAutoCreate : function()
4434     {
4435         // we will default to modal-body-overflow - might need to remove or make optional later.
4436         var bdy = {
4437                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4438                 html : this.html || ''
4439         };
4440
4441         var title = {
4442             tag: 'h5',
4443             cls : 'modal-title',
4444             html : this.title
4445         };
4446
4447         if(this.specificTitle){ // WTF is this?
4448             title = this.title;
4449         }
4450
4451         var header = [];
4452         if (this.allow_close && Roo.bootstrap.version == 3) {
4453             header.push({
4454                 tag: 'button',
4455                 cls : 'close',
4456                 html : '&times'
4457             });
4458         }
4459
4460         header.push(title);
4461
4462         if (this.editableTitle) {
4463             header.push({
4464                 cls: 'form-control roo-editable-title d-none',
4465                 tag: 'input',
4466                 type: 'text'
4467             });
4468         }
4469         
4470         if (this.allow_close && Roo.bootstrap.version == 4) {
4471             header.push({
4472                 tag: 'button',
4473                 cls : 'close',
4474                 html : '&times'
4475             });
4476         }
4477         
4478         var size = '';
4479
4480         if(this.size.length){
4481             size = 'modal-' + this.size;
4482         }
4483         
4484         var footer = Roo.bootstrap.version == 3 ?
4485             {
4486                 cls : 'modal-footer',
4487                 cn : [
4488                     {
4489                         tag: 'div',
4490                         cls: 'btn-' + this.buttonPosition
4491                     }
4492                 ]
4493
4494             } :
4495             {  // BS4 uses mr-auto on left buttons....
4496                 cls : 'modal-footer'
4497             };
4498
4499             
4500
4501         
4502         
4503         var modal = {
4504             cls: "modal",
4505              cn : [
4506                 {
4507                     cls: "modal-dialog " + size,
4508                     cn : [
4509                         {
4510                             cls : "modal-content",
4511                             cn : [
4512                                 {
4513                                     cls : 'modal-header',
4514                                     cn : header
4515                                 },
4516                                 bdy,
4517                                 footer
4518                             ]
4519
4520                         }
4521                     ]
4522
4523                 }
4524             ]
4525         };
4526
4527         if(this.animate){
4528             modal.cls += ' fade';
4529         }
4530
4531         return modal;
4532
4533     },
4534     getChildContainer : function() {
4535
4536          return this.bodyEl;
4537
4538     },
4539     getButtonContainer : function() {
4540         
4541          return Roo.bootstrap.version == 4 ?
4542             this.el.select('.modal-footer',true).first()
4543             : this.el.select('.modal-footer div',true).first();
4544
4545     },
4546     initEvents : function()
4547     {
4548         if (this.allow_close) {
4549             this.closeEl.on('click', this.hide, this);
4550         }
4551         Roo.EventManager.onWindowResize(this.resize, this, true);
4552         if (this.editableTitle) {
4553             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4554             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4555             this.headerEditEl.on('keyup', function(e) {
4556                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4557                         this.toggleHeaderInput(false)
4558                     }
4559                 }, this);
4560             this.headerEditEl.on('blur', function(e) {
4561                 this.toggleHeaderInput(false)
4562             },this);
4563         }
4564
4565     },
4566   
4567
4568     resize : function()
4569     {
4570         this.maskEl.setSize(
4571             Roo.lib.Dom.getViewWidth(true),
4572             Roo.lib.Dom.getViewHeight(true)
4573         );
4574         
4575         if (this.fitwindow) {
4576             
4577            this.dialogEl.setStyle( { 'max-width' : '100%' });
4578             this.setSize(
4579                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4580                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4581             );
4582             return;
4583         }
4584         
4585         if(this.max_width !== 0) {
4586             
4587             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4588             
4589             if(this.height) {
4590                 this.setSize(w, this.height);
4591                 return;
4592             }
4593             
4594             if(this.max_height) {
4595                 this.setSize(w,Math.min(
4596                     this.max_height,
4597                     Roo.lib.Dom.getViewportHeight(true) - 60
4598                 ));
4599                 
4600                 return;
4601             }
4602             
4603             if(!this.fit_content) {
4604                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4605                 return;
4606             }
4607             
4608             this.setSize(w, Math.min(
4609                 60 +
4610                 this.headerEl.getHeight() + 
4611                 this.footerEl.getHeight() + 
4612                 this.getChildHeight(this.bodyEl.dom.childNodes),
4613                 Roo.lib.Dom.getViewportHeight(true) - 60)
4614             );
4615         }
4616         
4617     },
4618
4619     setSize : function(w,h)
4620     {
4621         if (!w && !h) {
4622             return;
4623         }
4624         
4625         this.resizeTo(w,h);
4626     },
4627
4628     show : function() {
4629
4630         if (!this.rendered) {
4631             this.render();
4632         }
4633         this.toggleHeaderInput(false);
4634         //this.el.setStyle('display', 'block');
4635         this.el.removeClass('hideing');
4636         this.el.dom.style.display='block';
4637         
4638         Roo.get(document.body).addClass('modal-open');
4639  
4640         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4641             
4642             (function(){
4643                 this.el.addClass('show');
4644                 this.el.addClass('in');
4645             }).defer(50, this);
4646         }else{
4647             this.el.addClass('show');
4648             this.el.addClass('in');
4649         }
4650
4651         // not sure how we can show data in here..
4652         //if (this.tmpl) {
4653         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4654         //}
4655
4656         Roo.get(document.body).addClass("x-body-masked");
4657         
4658         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4659         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4660         this.maskEl.dom.style.display = 'block';
4661         this.maskEl.addClass('show');
4662         
4663         
4664         this.resize();
4665         
4666         this.fireEvent('show', this);
4667
4668         // set zindex here - otherwise it appears to be ignored...
4669         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670
4671         (function () {
4672             this.items.forEach( function(e) {
4673                 e.layout ? e.layout() : false;
4674
4675             });
4676         }).defer(100,this);
4677
4678     },
4679     hide : function()
4680     {
4681         if(this.fireEvent("beforehide", this) !== false){
4682             
4683             this.maskEl.removeClass('show');
4684             
4685             this.maskEl.dom.style.display = '';
4686             Roo.get(document.body).removeClass("x-body-masked");
4687             this.el.removeClass('in');
4688             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4689
4690             if(this.animate){ // why
4691                 this.el.addClass('hideing');
4692                 this.el.removeClass('show');
4693                 (function(){
4694                     if (!this.el.hasClass('hideing')) {
4695                         return; // it's been shown again...
4696                     }
4697                     
4698                     this.el.dom.style.display='';
4699
4700                     Roo.get(document.body).removeClass('modal-open');
4701                     this.el.removeClass('hideing');
4702                 }).defer(150,this);
4703                 
4704             }else{
4705                 this.el.removeClass('show');
4706                 this.el.dom.style.display='';
4707                 Roo.get(document.body).removeClass('modal-open');
4708
4709             }
4710             this.fireEvent('hide', this);
4711         }
4712     },
4713     isVisible : function()
4714     {
4715         
4716         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4717         
4718     },
4719
4720     addButton : function(str, cb)
4721     {
4722
4723
4724         var b = Roo.apply({}, { html : str } );
4725         b.xns = b.xns || Roo.bootstrap;
4726         b.xtype = b.xtype || 'Button';
4727         if (typeof(b.listeners) == 'undefined') {
4728             b.listeners = { click : cb.createDelegate(this)  };
4729         }
4730
4731         var btn = Roo.factory(b);
4732
4733         btn.render(this.getButtonContainer());
4734
4735         return btn;
4736
4737     },
4738
4739     setDefaultButton : function(btn)
4740     {
4741         //this.el.select('.modal-footer').()
4742     },
4743
4744     resizeTo: function(w,h)
4745     {
4746         this.dialogEl.setWidth(w);
4747         
4748         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4749
4750         this.bodyEl.setHeight(h - diff);
4751         
4752         this.fireEvent('resize', this);
4753     },
4754     
4755     setContentSize  : function(w, h)
4756     {
4757
4758     },
4759     onButtonClick: function(btn,e)
4760     {
4761         //Roo.log([a,b,c]);
4762         this.fireEvent('btnclick', btn.name, e);
4763     },
4764      /**
4765      * Set the title of the Dialog
4766      * @param {String} str new Title
4767      */
4768     setTitle: function(str) {
4769         this.titleEl.dom.innerHTML = str;
4770         this.title = str;
4771     },
4772     /**
4773      * Set the body of the Dialog
4774      * @param {String} str new Title
4775      */
4776     setBody: function(str) {
4777         this.bodyEl.dom.innerHTML = str;
4778     },
4779     /**
4780      * Set the body of the Dialog using the template
4781      * @param {Obj} data - apply this data to the template and replace the body contents.
4782      */
4783     applyBody: function(obj)
4784     {
4785         if (!this.tmpl) {
4786             Roo.log("Error - using apply Body without a template");
4787             //code
4788         }
4789         this.tmpl.overwrite(this.bodyEl, obj);
4790     },
4791     
4792     getChildHeight : function(child_nodes)
4793     {
4794         if(
4795             !child_nodes ||
4796             child_nodes.length == 0
4797         ) {
4798             return 0;
4799         }
4800         
4801         var child_height = 0;
4802         
4803         for(var i = 0; i < child_nodes.length; i++) {
4804             
4805             /*
4806             * for modal with tabs...
4807             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4808                 
4809                 var layout_childs = child_nodes[i].childNodes;
4810                 
4811                 for(var j = 0; j < layout_childs.length; j++) {
4812                     
4813                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4814                         
4815                         var layout_body_childs = layout_childs[j].childNodes;
4816                         
4817                         for(var k = 0; k < layout_body_childs.length; k++) {
4818                             
4819                             if(layout_body_childs[k].classList.contains('navbar')) {
4820                                 child_height += layout_body_childs[k].offsetHeight;
4821                                 continue;
4822                             }
4823                             
4824                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4825                                 
4826                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4827                                 
4828                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4829                                     
4830                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4831                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4832                                         continue;
4833                                     }
4834                                     
4835                                 }
4836                                 
4837                             }
4838                             
4839                         }
4840                     }
4841                 }
4842                 continue;
4843             }
4844             */
4845             
4846             child_height += child_nodes[i].offsetHeight;
4847             // Roo.log(child_nodes[i].offsetHeight);
4848         }
4849         
4850         return child_height;
4851     },
4852     toggleHeaderInput : function(is_edit)
4853     {
4854         if (!this.editableTitle) {
4855             return; // not editable.
4856         }
4857         if (is_edit && this.is_header_editing) {
4858             return; // already editing..
4859         }
4860         if (is_edit) {
4861     
4862             this.headerEditEl.dom.value = this.title;
4863             this.headerEditEl.removeClass('d-none');
4864             this.headerEditEl.dom.focus();
4865             this.titleEl.addClass('d-none');
4866             
4867             this.is_header_editing = true;
4868             return
4869         }
4870         // flip back to not editing.
4871         this.title = this.headerEditEl.dom.value;
4872         this.headerEditEl.addClass('d-none');
4873         this.titleEl.removeClass('d-none');
4874         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4875         this.is_header_editing = false;
4876         this.fireEvent('titlechanged', this, this.title);
4877     
4878             
4879         
4880     }
4881
4882 });
4883
4884
4885 Roo.apply(Roo.bootstrap.Modal,  {
4886     /**
4887          * Button config that displays a single OK button
4888          * @type Object
4889          */
4890         OK :  [{
4891             name : 'ok',
4892             weight : 'primary',
4893             html : 'OK'
4894         }],
4895         /**
4896          * Button config that displays Yes and No buttons
4897          * @type Object
4898          */
4899         YESNO : [
4900             {
4901                 name  : 'no',
4902                 html : 'No'
4903             },
4904             {
4905                 name  :'yes',
4906                 weight : 'primary',
4907                 html : 'Yes'
4908             }
4909         ],
4910
4911         /**
4912          * Button config that displays OK and Cancel buttons
4913          * @type Object
4914          */
4915         OKCANCEL : [
4916             {
4917                name : 'cancel',
4918                 html : 'Cancel'
4919             },
4920             {
4921                 name : 'ok',
4922                 weight : 'primary',
4923                 html : 'OK'
4924             }
4925         ],
4926         /**
4927          * Button config that displays Yes, No and Cancel buttons
4928          * @type Object
4929          */
4930         YESNOCANCEL : [
4931             {
4932                 name : 'yes',
4933                 weight : 'primary',
4934                 html : 'Yes'
4935             },
4936             {
4937                 name : 'no',
4938                 html : 'No'
4939             },
4940             {
4941                 name : 'cancel',
4942                 html : 'Cancel'
4943             }
4944         ],
4945         
4946         zIndex : 10001
4947 });
4948
4949 /*
4950  * - LGPL
4951  *
4952  * messagebox - can be used as a replace
4953  * 
4954  */
4955 /**
4956  * @class Roo.MessageBox
4957  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4958  * Example usage:
4959  *<pre><code>
4960 // Basic alert:
4961 Roo.Msg.alert('Status', 'Changes saved successfully.');
4962
4963 // Prompt for user data:
4964 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4965     if (btn == 'ok'){
4966         // process text value...
4967     }
4968 });
4969
4970 // Show a dialog using config options:
4971 Roo.Msg.show({
4972    title:'Save Changes?',
4973    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4974    buttons: Roo.Msg.YESNOCANCEL,
4975    fn: processResult,
4976    animEl: 'elId'
4977 });
4978 </code></pre>
4979  * @singleton
4980  */
4981 Roo.bootstrap.MessageBox = function(){
4982     var dlg, opt, mask, waitTimer;
4983     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4984     var buttons, activeTextEl, bwidth;
4985
4986     
4987     // private
4988     var handleButton = function(button){
4989         dlg.hide();
4990         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4991     };
4992
4993     // private
4994     var handleHide = function(){
4995         if(opt && opt.cls){
4996             dlg.el.removeClass(opt.cls);
4997         }
4998         //if(waitTimer){
4999         //    Roo.TaskMgr.stop(waitTimer);
5000         //    waitTimer = null;
5001         //}
5002     };
5003
5004     // private
5005     var updateButtons = function(b){
5006         var width = 0;
5007         if(!b){
5008             buttons["ok"].hide();
5009             buttons["cancel"].hide();
5010             buttons["yes"].hide();
5011             buttons["no"].hide();
5012             dlg.footerEl.hide();
5013             
5014             return width;
5015         }
5016         dlg.footerEl.show();
5017         for(var k in buttons){
5018             if(typeof buttons[k] != "function"){
5019                 if(b[k]){
5020                     buttons[k].show();
5021                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5022                     width += buttons[k].el.getWidth()+15;
5023                 }else{
5024                     buttons[k].hide();
5025                 }
5026             }
5027         }
5028         return width;
5029     };
5030
5031     // private
5032     var handleEsc = function(d, k, e){
5033         if(opt && opt.closable !== false){
5034             dlg.hide();
5035         }
5036         if(e){
5037             e.stopEvent();
5038         }
5039     };
5040
5041     return {
5042         /**
5043          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5044          * @return {Roo.BasicDialog} The BasicDialog element
5045          */
5046         getDialog : function(){
5047            if(!dlg){
5048                 dlg = new Roo.bootstrap.Modal( {
5049                     //draggable: true,
5050                     //resizable:false,
5051                     //constraintoviewport:false,
5052                     //fixedcenter:true,
5053                     //collapsible : false,
5054                     //shim:true,
5055                     //modal: true,
5056                 //    width: 'auto',
5057                   //  height:100,
5058                     //buttonAlign:"center",
5059                     closeClick : function(){
5060                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5061                             handleButton("no");
5062                         }else{
5063                             handleButton("cancel");
5064                         }
5065                     }
5066                 });
5067                 dlg.render();
5068                 dlg.on("hide", handleHide);
5069                 mask = dlg.mask;
5070                 //dlg.addKeyListener(27, handleEsc);
5071                 buttons = {};
5072                 this.buttons = buttons;
5073                 var bt = this.buttonText;
5074                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5075                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5076                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5077                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5078                 //Roo.log(buttons);
5079                 bodyEl = dlg.bodyEl.createChild({
5080
5081                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5082                         '<textarea class="roo-mb-textarea"></textarea>' +
5083                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5084                 });
5085                 msgEl = bodyEl.dom.firstChild;
5086                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5087                 textboxEl.enableDisplayMode();
5088                 textboxEl.addKeyListener([10,13], function(){
5089                     if(dlg.isVisible() && opt && opt.buttons){
5090                         if(opt.buttons.ok){
5091                             handleButton("ok");
5092                         }else if(opt.buttons.yes){
5093                             handleButton("yes");
5094                         }
5095                     }
5096                 });
5097                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5098                 textareaEl.enableDisplayMode();
5099                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5100                 progressEl.enableDisplayMode();
5101                 
5102                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5103                 var pf = progressEl.dom.firstChild;
5104                 if (pf) {
5105                     pp = Roo.get(pf.firstChild);
5106                     pp.setHeight(pf.offsetHeight);
5107                 }
5108                 
5109             }
5110             return dlg;
5111         },
5112
5113         /**
5114          * Updates the message box body text
5115          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5116          * the XHTML-compliant non-breaking space character '&amp;#160;')
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         updateText : function(text)
5120         {
5121             if(!dlg.isVisible() && !opt.width){
5122                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5123                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5124             }
5125             msgEl.innerHTML = text || '&#160;';
5126       
5127             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5128             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5129             var w = Math.max(
5130                     Math.min(opt.width || cw , this.maxWidth), 
5131                     Math.max(opt.minWidth || this.minWidth, bwidth)
5132             );
5133             if(opt.prompt){
5134                 activeTextEl.setWidth(w);
5135             }
5136             if(dlg.isVisible()){
5137                 dlg.fixedcenter = false;
5138             }
5139             // to big, make it scroll. = But as usual stupid IE does not support
5140             // !important..
5141             
5142             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5143                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5144                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.height = '';
5147                 bodyEl.dom.style.overflowY = '';
5148             }
5149             if (cw > w) {
5150                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5151             } else {
5152                 bodyEl.dom.style.overflowX = '';
5153             }
5154             
5155             dlg.setContentSize(w, bodyEl.getHeight());
5156             if(dlg.isVisible()){
5157                 dlg.fixedcenter = true;
5158             }
5159             return this;
5160         },
5161
5162         /**
5163          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5164          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5165          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5166          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5167          * @return {Roo.MessageBox} This message box
5168          */
5169         updateProgress : function(value, text){
5170             if(text){
5171                 this.updateText(text);
5172             }
5173             
5174             if (pp) { // weird bug on my firefox - for some reason this is not defined
5175                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5176                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5177             }
5178             return this;
5179         },        
5180
5181         /**
5182          * Returns true if the message box is currently displayed
5183          * @return {Boolean} True if the message box is visible, else false
5184          */
5185         isVisible : function(){
5186             return dlg && dlg.isVisible();  
5187         },
5188
5189         /**
5190          * Hides the message box if it is displayed
5191          */
5192         hide : function(){
5193             if(this.isVisible()){
5194                 dlg.hide();
5195             }  
5196         },
5197
5198         /**
5199          * Displays a new message box, or reinitializes an existing message box, based on the config options
5200          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5201          * The following config object properties are supported:
5202          * <pre>
5203 Property    Type             Description
5204 ----------  ---------------  ------------------------------------------------------------------------------------
5205 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5206                                    closes (defaults to undefined)
5207 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5208                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5209 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5210                                    progress and wait dialogs will ignore this property and always hide the
5211                                    close button as they can only be closed programmatically.
5212 cls               String           A custom CSS class to apply to the message box element
5213 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5214                                    displayed (defaults to 75)
5215 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5216                                    function will be btn (the name of the button that was clicked, if applicable,
5217                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5218                                    Progress and wait dialogs will ignore this option since they do not respond to
5219                                    user actions and can only be closed programmatically, so any required function
5220                                    should be called by the same code after it closes the dialog.
5221 icon              String           A CSS class that provides a background image to be used as an icon for
5222                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5223 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5224 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5225 modal             Boolean          False to allow user interaction with the page while the message box is
5226                                    displayed (defaults to true)
5227 msg               String           A string that will replace the existing message box body text (defaults
5228                                    to the XHTML-compliant non-breaking space character '&#160;')
5229 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5230 progress          Boolean          True to display a progress bar (defaults to false)
5231 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5232 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5233 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5234 title             String           The title text
5235 value             String           The string value to set into the active textbox element if displayed
5236 wait              Boolean          True to display a progress bar (defaults to false)
5237 width             Number           The width of the dialog in pixels
5238 </pre>
5239          *
5240          * Example usage:
5241          * <pre><code>
5242 Roo.Msg.show({
5243    title: 'Address',
5244    msg: 'Please enter your address:',
5245    width: 300,
5246    buttons: Roo.MessageBox.OKCANCEL,
5247    multiline: true,
5248    fn: saveAddress,
5249    animEl: 'addAddressBtn'
5250 });
5251 </code></pre>
5252          * @param {Object} config Configuration options
5253          * @return {Roo.MessageBox} This message box
5254          */
5255         show : function(options)
5256         {
5257             
5258             // this causes nightmares if you show one dialog after another
5259             // especially on callbacks..
5260              
5261             if(this.isVisible()){
5262                 
5263                 this.hide();
5264                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5265                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5266                 Roo.log("New Dialog Message:" +  options.msg )
5267                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5268                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5269                 
5270             }
5271             var d = this.getDialog();
5272             opt = options;
5273             d.setTitle(opt.title || "&#160;");
5274             d.closeEl.setDisplayed(opt.closable !== false);
5275             activeTextEl = textboxEl;
5276             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5277             if(opt.prompt){
5278                 if(opt.multiline){
5279                     textboxEl.hide();
5280                     textareaEl.show();
5281                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5282                         opt.multiline : this.defaultTextHeight);
5283                     activeTextEl = textareaEl;
5284                 }else{
5285                     textboxEl.show();
5286                     textareaEl.hide();
5287                 }
5288             }else{
5289                 textboxEl.hide();
5290                 textareaEl.hide();
5291             }
5292             progressEl.setDisplayed(opt.progress === true);
5293             if (opt.progress) {
5294                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5295             }
5296             this.updateProgress(0);
5297             activeTextEl.dom.value = opt.value || "";
5298             if(opt.prompt){
5299                 dlg.setDefaultButton(activeTextEl);
5300             }else{
5301                 var bs = opt.buttons;
5302                 var db = null;
5303                 if(bs && bs.ok){
5304                     db = buttons["ok"];
5305                 }else if(bs && bs.yes){
5306                     db = buttons["yes"];
5307                 }
5308                 dlg.setDefaultButton(db);
5309             }
5310             bwidth = updateButtons(opt.buttons);
5311             this.updateText(opt.msg);
5312             if(opt.cls){
5313                 d.el.addClass(opt.cls);
5314             }
5315             d.proxyDrag = opt.proxyDrag === true;
5316             d.modal = opt.modal !== false;
5317             d.mask = opt.modal !== false ? mask : false;
5318             if(!d.isVisible()){
5319                 // force it to the end of the z-index stack so it gets a cursor in FF
5320                 document.body.appendChild(dlg.el.dom);
5321                 d.animateTarget = null;
5322                 d.show(options.animEl);
5323             }
5324             return this;
5325         },
5326
5327         /**
5328          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5329          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5330          * and closing the message box when the process is complete.
5331          * @param {String} title The title bar text
5332          * @param {String} msg The message box body text
5333          * @return {Roo.MessageBox} This message box
5334          */
5335         progress : function(title, msg){
5336             this.show({
5337                 title : title,
5338                 msg : msg,
5339                 buttons: false,
5340                 progress:true,
5341                 closable:false,
5342                 minWidth: this.minProgressWidth,
5343                 modal : true
5344             });
5345             return this;
5346         },
5347
5348         /**
5349          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5350          * If a callback function is passed it will be called after the user clicks the button, and the
5351          * id of the button that was clicked will be passed as the only parameter to the callback
5352          * (could also be the top-right close button).
5353          * @param {String} title The title bar text
5354          * @param {String} msg The message box body text
5355          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5356          * @param {Object} scope (optional) The scope of the callback function
5357          * @return {Roo.MessageBox} This message box
5358          */
5359         alert : function(title, msg, fn, scope)
5360         {
5361             this.show({
5362                 title : title,
5363                 msg : msg,
5364                 buttons: this.OK,
5365                 fn: fn,
5366                 closable : false,
5367                 scope : scope,
5368                 modal : true
5369             });
5370             return this;
5371         },
5372
5373         /**
5374          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5375          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5376          * You are responsible for closing the message box when the process is complete.
5377          * @param {String} msg The message box body text
5378          * @param {String} title (optional) The title bar text
5379          * @return {Roo.MessageBox} This message box
5380          */
5381         wait : function(msg, title){
5382             this.show({
5383                 title : title,
5384                 msg : msg,
5385                 buttons: false,
5386                 closable:false,
5387                 progress:true,
5388                 modal:true,
5389                 width:300,
5390                 wait:true
5391             });
5392             waitTimer = Roo.TaskMgr.start({
5393                 run: function(i){
5394                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5395                 },
5396                 interval: 1000
5397             });
5398             return this;
5399         },
5400
5401         /**
5402          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5403          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5404          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5405          * @param {String} title The title bar text
5406          * @param {String} msg The message box body text
5407          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5408          * @param {Object} scope (optional) The scope of the callback function
5409          * @return {Roo.MessageBox} This message box
5410          */
5411         confirm : function(title, msg, fn, scope){
5412             this.show({
5413                 title : title,
5414                 msg : msg,
5415                 buttons: this.YESNO,
5416                 fn: fn,
5417                 scope : scope,
5418                 modal : true
5419             });
5420             return this;
5421         },
5422
5423         /**
5424          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5425          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5426          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5427          * (could also be the top-right close button) and the text that was entered will be passed as the two
5428          * parameters to the callback.
5429          * @param {String} title The title bar text
5430          * @param {String} msg The message box body text
5431          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5432          * @param {Object} scope (optional) The scope of the callback function
5433          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5434          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5435          * @return {Roo.MessageBox} This message box
5436          */
5437         prompt : function(title, msg, fn, scope, multiline){
5438             this.show({
5439                 title : title,
5440                 msg : msg,
5441                 buttons: this.OKCANCEL,
5442                 fn: fn,
5443                 minWidth:250,
5444                 scope : scope,
5445                 prompt:true,
5446                 multiline: multiline,
5447                 modal : true
5448             });
5449             return this;
5450         },
5451
5452         /**
5453          * Button config that displays a single OK button
5454          * @type Object
5455          */
5456         OK : {ok:true},
5457         /**
5458          * Button config that displays Yes and No buttons
5459          * @type Object
5460          */
5461         YESNO : {yes:true, no:true},
5462         /**
5463          * Button config that displays OK and Cancel buttons
5464          * @type Object
5465          */
5466         OKCANCEL : {ok:true, cancel:true},
5467         /**
5468          * Button config that displays Yes, No and Cancel buttons
5469          * @type Object
5470          */
5471         YESNOCANCEL : {yes:true, no:true, cancel:true},
5472
5473         /**
5474          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5475          * @type Number
5476          */
5477         defaultTextHeight : 75,
5478         /**
5479          * The maximum width in pixels of the message box (defaults to 600)
5480          * @type Number
5481          */
5482         maxWidth : 600,
5483         /**
5484          * The minimum width in pixels of the message box (defaults to 100)
5485          * @type Number
5486          */
5487         minWidth : 100,
5488         /**
5489          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5490          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5491          * @type Number
5492          */
5493         minProgressWidth : 250,
5494         /**
5495          * An object containing the default button text strings that can be overriden for localized language support.
5496          * Supported properties are: ok, cancel, yes and no.
5497          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5498          * @type Object
5499          */
5500         buttonText : {
5501             ok : "OK",
5502             cancel : "Cancel",
5503             yes : "Yes",
5504             no : "No"
5505         }
5506     };
5507 }();
5508
5509 /**
5510  * Shorthand for {@link Roo.MessageBox}
5511  */
5512 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5513 Roo.Msg = Roo.Msg || Roo.MessageBox;
5514 /*
5515  * - LGPL
5516  *
5517  * navbar
5518  * 
5519  */
5520
5521 /**
5522  * @class Roo.bootstrap.Navbar
5523  * @extends Roo.bootstrap.Component
5524  * Bootstrap Navbar class
5525
5526  * @constructor
5527  * Create a new Navbar
5528  * @param {Object} config The config object
5529  */
5530
5531
5532 Roo.bootstrap.Navbar = function(config){
5533     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5534     this.addEvents({
5535         // raw events
5536         /**
5537          * @event beforetoggle
5538          * Fire before toggle the menu
5539          * @param {Roo.EventObject} e
5540          */
5541         "beforetoggle" : true
5542     });
5543 };
5544
5545 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5546     
5547     
5548    
5549     // private
5550     navItems : false,
5551     loadMask : false,
5552     
5553     
5554     getAutoCreate : function(){
5555         
5556         
5557         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5558         
5559     },
5560     
5561     initEvents :function ()
5562     {
5563         //Roo.log(this.el.select('.navbar-toggle',true));
5564         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565         
5566         var mark = {
5567             tag: "div",
5568             cls:"x-dlg-mask"
5569         };
5570         
5571         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5572         
5573         var size = this.el.getSize();
5574         this.maskEl.setSize(size.width, size.height);
5575         this.maskEl.enableDisplayMode("block");
5576         this.maskEl.hide();
5577         
5578         if(this.loadMask){
5579             this.maskEl.show();
5580         }
5581     },
5582     
5583     
5584     getChildContainer : function()
5585     {
5586         if (this.el && this.el.select('.collapse').getCount()) {
5587             return this.el.select('.collapse',true).first();
5588         }
5589         
5590         return this.el;
5591     },
5592     
5593     mask : function()
5594     {
5595         this.maskEl.show();
5596     },
5597     
5598     unmask : function()
5599     {
5600         this.maskEl.hide();
5601     },
5602     onToggle : function()
5603     {
5604         
5605         if(this.fireEvent('beforetoggle', this) === false){
5606             return;
5607         }
5608         var ce = this.el.select('.navbar-collapse',true).first();
5609       
5610         if (!ce.hasClass('show')) {
5611            this.expand();
5612         } else {
5613             this.collapse();
5614         }
5615         
5616         
5617     
5618     },
5619     /**
5620      * Expand the navbar pulldown 
5621      */
5622     expand : function ()
5623     {
5624        
5625         var ce = this.el.select('.navbar-collapse',true).first();
5626         if (ce.hasClass('collapsing')) {
5627             return;
5628         }
5629         ce.dom.style.height = '';
5630                // show it...
5631         ce.addClass('in'); // old...
5632         ce.removeClass('collapse');
5633         ce.addClass('show');
5634         var h = ce.getHeight();
5635         Roo.log(h);
5636         ce.removeClass('show');
5637         // at this point we should be able to see it..
5638         ce.addClass('collapsing');
5639         
5640         ce.setHeight(0); // resize it ...
5641         ce.on('transitionend', function() {
5642             //Roo.log('done transition');
5643             ce.removeClass('collapsing');
5644             ce.addClass('show');
5645             ce.removeClass('collapse');
5646
5647             ce.dom.style.height = '';
5648         }, this, { single: true} );
5649         ce.setHeight(h);
5650         ce.dom.scrollTop = 0;
5651     },
5652     /**
5653      * Collapse the navbar pulldown 
5654      */
5655     collapse : function()
5656     {
5657          var ce = this.el.select('.navbar-collapse',true).first();
5658        
5659         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5660             // it's collapsed or collapsing..
5661             return;
5662         }
5663         ce.removeClass('in'); // old...
5664         ce.setHeight(ce.getHeight());
5665         ce.removeClass('show');
5666         ce.addClass('collapsing');
5667         
5668         ce.on('transitionend', function() {
5669             ce.dom.style.height = '';
5670             ce.removeClass('collapsing');
5671             ce.addClass('collapse');
5672         }, this, { single: true} );
5673         ce.setHeight(0);
5674     }
5675     
5676     
5677     
5678 });
5679
5680
5681
5682  
5683
5684  /*
5685  * - LGPL
5686  *
5687  * navbar
5688  * 
5689  */
5690
5691 /**
5692  * @class Roo.bootstrap.NavSimplebar
5693  * @extends Roo.bootstrap.Navbar
5694  * Bootstrap Sidebar class
5695  *
5696  * @cfg {Boolean} inverse is inverted color
5697  * 
5698  * @cfg {String} type (nav | pills | tabs)
5699  * @cfg {Boolean} arrangement stacked | justified
5700  * @cfg {String} align (left | right) alignment
5701  * 
5702  * @cfg {Boolean} main (true|false) main nav bar? default false
5703  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5704  * 
5705  * @cfg {String} tag (header|footer|nav|div) default is nav 
5706
5707  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5708  * 
5709  * 
5710  * @constructor
5711  * Create a new Sidebar
5712  * @param {Object} config The config object
5713  */
5714
5715
5716 Roo.bootstrap.NavSimplebar = function(config){
5717     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5718 };
5719
5720 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5721     
5722     inverse: false,
5723     
5724     type: false,
5725     arrangement: '',
5726     align : false,
5727     
5728     weight : 'light',
5729     
5730     main : false,
5731     
5732     
5733     tag : false,
5734     
5735     
5736     getAutoCreate : function(){
5737         
5738         
5739         var cfg = {
5740             tag : this.tag || 'div',
5741             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5742         };
5743         if (['light','white'].indexOf(this.weight) > -1) {
5744             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5745         }
5746         cfg.cls += ' bg-' + this.weight;
5747         
5748         if (this.inverse) {
5749             cfg.cls += ' navbar-inverse';
5750             
5751         }
5752         
5753         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5754         
5755         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5756             return cfg;
5757         }
5758         
5759         
5760     
5761         
5762         cfg.cn = [
5763             {
5764                 cls: 'nav nav-' + this.xtype,
5765                 tag : 'ul'
5766             }
5767         ];
5768         
5769          
5770         this.type = this.type || 'nav';
5771         if (['tabs','pills'].indexOf(this.type) != -1) {
5772             cfg.cn[0].cls += ' nav-' + this.type
5773         
5774         
5775         } else {
5776             if (this.type!=='nav') {
5777                 Roo.log('nav type must be nav/tabs/pills')
5778             }
5779             cfg.cn[0].cls += ' navbar-nav'
5780         }
5781         
5782         
5783         
5784         
5785         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.arrangement;
5787         }
5788         
5789         
5790         if (this.align === 'right') {
5791             cfg.cn[0].cls += ' navbar-right';
5792         }
5793         
5794         
5795         
5796         
5797         return cfg;
5798     
5799         
5800     }
5801     
5802     
5803     
5804 });
5805
5806
5807
5808  
5809
5810  
5811        /*
5812  * - LGPL
5813  *
5814  * navbar
5815  * navbar-fixed-top
5816  * navbar-expand-md  fixed-top 
5817  */
5818
5819 /**
5820  * @class Roo.bootstrap.NavHeaderbar
5821  * @extends Roo.bootstrap.NavSimplebar
5822  * Bootstrap Sidebar class
5823  *
5824  * @cfg {String} brand what is brand
5825  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5826  * @cfg {String} brand_href href of the brand
5827  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5828  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5829  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5830  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5831  * 
5832  * @constructor
5833  * Create a new Sidebar
5834  * @param {Object} config The config object
5835  */
5836
5837
5838 Roo.bootstrap.NavHeaderbar = function(config){
5839     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5840       
5841 };
5842
5843 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5844     
5845     position: '',
5846     brand: '',
5847     brand_href: false,
5848     srButton : true,
5849     autohide : false,
5850     desktopCenter : false,
5851    
5852     
5853     getAutoCreate : function(){
5854         
5855         var   cfg = {
5856             tag: this.nav || 'nav',
5857             cls: 'navbar navbar-expand-md',
5858             role: 'navigation',
5859             cn: []
5860         };
5861         
5862         var cn = cfg.cn;
5863         if (this.desktopCenter) {
5864             cn.push({cls : 'container', cn : []});
5865             cn = cn[0].cn;
5866         }
5867         
5868         if(this.srButton){
5869             var btn = {
5870                 tag: 'button',
5871                 type: 'button',
5872                 cls: 'navbar-toggle navbar-toggler',
5873                 'data-toggle': 'collapse',
5874                 cn: [
5875                     {
5876                         tag: 'span',
5877                         cls: 'sr-only',
5878                         html: 'Toggle navigation'
5879                     },
5880                     {
5881                         tag: 'span',
5882                         cls: 'icon-bar navbar-toggler-icon'
5883                     },
5884                     {
5885                         tag: 'span',
5886                         cls: 'icon-bar'
5887                     },
5888                     {
5889                         tag: 'span',
5890                         cls: 'icon-bar'
5891                     }
5892                 ]
5893             };
5894             
5895             cn.push( Roo.bootstrap.version == 4 ? btn : {
5896                 tag: 'div',
5897                 cls: 'navbar-header',
5898                 cn: [
5899                     btn
5900                 ]
5901             });
5902         }
5903         
5904         cn.push({
5905             tag: 'div',
5906             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5907             cn : []
5908         });
5909         
5910         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5911         
5912         if (['light','white'].indexOf(this.weight) > -1) {
5913             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5914         }
5915         cfg.cls += ' bg-' + this.weight;
5916         
5917         
5918         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5919             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5920             
5921             // tag can override this..
5922             
5923             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5924         }
5925         
5926         if (this.brand !== '') {
5927             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5928             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5929                 tag: 'a',
5930                 href: this.brand_href ? this.brand_href : '#',
5931                 cls: 'navbar-brand',
5932                 cn: [
5933                 this.brand
5934                 ]
5935             });
5936         }
5937         
5938         if(this.main){
5939             cfg.cls += ' main-nav';
5940         }
5941         
5942         
5943         return cfg;
5944
5945         
5946     },
5947     getHeaderChildContainer : function()
5948     {
5949         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5950             return this.el.select('.navbar-header',true).first();
5951         }
5952         
5953         return this.getChildContainer();
5954     },
5955     
5956     getChildContainer : function()
5957     {
5958          
5959         return this.el.select('.roo-navbar-collapse',true).first();
5960          
5961         
5962     },
5963     
5964     initEvents : function()
5965     {
5966         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5967         
5968         if (this.autohide) {
5969             
5970             var prevScroll = 0;
5971             var ft = this.el;
5972             
5973             Roo.get(document).on('scroll',function(e) {
5974                 var ns = Roo.get(document).getScroll().top;
5975                 var os = prevScroll;
5976                 prevScroll = ns;
5977                 
5978                 if(ns > os){
5979                     ft.removeClass('slideDown');
5980                     ft.addClass('slideUp');
5981                     return;
5982                 }
5983                 ft.removeClass('slideUp');
5984                 ft.addClass('slideDown');
5985                  
5986               
5987           },this);
5988         }
5989     }    
5990     
5991 });
5992
5993
5994
5995  
5996
5997  /*
5998  * - LGPL
5999  *
6000  * navbar
6001  * 
6002  */
6003
6004 /**
6005  * @class Roo.bootstrap.NavSidebar
6006  * @extends Roo.bootstrap.Navbar
6007  * Bootstrap Sidebar class
6008  * 
6009  * @constructor
6010  * Create a new Sidebar
6011  * @param {Object} config The config object
6012  */
6013
6014
6015 Roo.bootstrap.NavSidebar = function(config){
6016     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6017 };
6018
6019 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6020     
6021     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6022     
6023     getAutoCreate : function(){
6024         
6025         
6026         return  {
6027             tag: 'div',
6028             cls: 'sidebar sidebar-nav'
6029         };
6030     
6031         
6032     }
6033     
6034     
6035     
6036 });
6037
6038
6039
6040  
6041
6042  /*
6043  * - LGPL
6044  *
6045  * nav group
6046  * 
6047  */
6048
6049 /**
6050  * @class Roo.bootstrap.NavGroup
6051  * @extends Roo.bootstrap.Component
6052  * Bootstrap NavGroup class
6053  * @cfg {String} align (left|right)
6054  * @cfg {Boolean} inverse
6055  * @cfg {String} type (nav|pills|tab) default nav
6056  * @cfg {String} navId - reference Id for navbar.
6057  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6058  * 
6059  * @constructor
6060  * Create a new nav group
6061  * @param {Object} config The config object
6062  */
6063
6064 Roo.bootstrap.NavGroup = function(config){
6065     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6066     this.navItems = [];
6067    
6068     Roo.bootstrap.NavGroup.register(this);
6069      this.addEvents({
6070         /**
6071              * @event changed
6072              * Fires when the active item changes
6073              * @param {Roo.bootstrap.NavGroup} this
6074              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6075              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6076          */
6077         'changed': true
6078      });
6079     
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6083     
6084     align: '',
6085     inverse: false,
6086     form: false,
6087     type: 'nav',
6088     navId : '',
6089     // private
6090     pilltype : true,
6091     
6092     navItems : false, 
6093     
6094     getAutoCreate : function()
6095     {
6096         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6097         
6098         cfg = {
6099             tag : 'ul',
6100             cls: 'nav' 
6101         };
6102         if (Roo.bootstrap.version == 4) {
6103             if (['tabs','pills'].indexOf(this.type) != -1) {
6104                 cfg.cls += ' nav-' + this.type; 
6105             } else {
6106                 // trying to remove so header bar can right align top?
6107                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6108                     // do not use on header bar... 
6109                     cfg.cls += ' navbar-nav';
6110                 }
6111             }
6112             
6113         } else {
6114             if (['tabs','pills'].indexOf(this.type) != -1) {
6115                 cfg.cls += ' nav-' + this.type
6116             } else {
6117                 if (this.type !== 'nav') {
6118                     Roo.log('nav type must be nav/tabs/pills')
6119                 }
6120                 cfg.cls += ' navbar-nav'
6121             }
6122         }
6123         
6124         if (this.parent() && this.parent().sidebar) {
6125             cfg = {
6126                 tag: 'ul',
6127                 cls: 'dashboard-menu sidebar-menu'
6128             };
6129             
6130             return cfg;
6131         }
6132         
6133         if (this.form === true) {
6134             cfg = {
6135                 tag: 'form',
6136                 cls: 'navbar-form form-inline'
6137             };
6138             //nav navbar-right ml-md-auto
6139             if (this.align === 'right') {
6140                 cfg.cls += ' navbar-right ml-md-auto';
6141             } else {
6142                 cfg.cls += ' navbar-left';
6143             }
6144         }
6145         
6146         if (this.align === 'right') {
6147             cfg.cls += ' navbar-right ml-md-auto';
6148         } else {
6149             cfg.cls += ' mr-auto';
6150         }
6151         
6152         if (this.inverse) {
6153             cfg.cls += ' navbar-inverse';
6154             
6155         }
6156         
6157         
6158         return cfg;
6159     },
6160     /**
6161     * sets the active Navigation item
6162     * @param {Roo.bootstrap.NavItem} the new current navitem
6163     */
6164     setActiveItem : function(item)
6165     {
6166         var prev = false;
6167         Roo.each(this.navItems, function(v){
6168             if (v == item) {
6169                 return ;
6170             }
6171             if (v.isActive()) {
6172                 v.setActive(false, true);
6173                 prev = v;
6174                 
6175             }
6176             
6177         });
6178
6179         item.setActive(true, true);
6180         this.fireEvent('changed', this, item, prev);
6181         
6182         
6183     },
6184     /**
6185     * gets the active Navigation item
6186     * @return {Roo.bootstrap.NavItem} the current navitem
6187     */
6188     getActive : function()
6189     {
6190         
6191         var prev = false;
6192         Roo.each(this.navItems, function(v){
6193             
6194             if (v.isActive()) {
6195                 prev = v;
6196                 
6197             }
6198             
6199         });
6200         return prev;
6201     },
6202     
6203     indexOfNav : function()
6204     {
6205         
6206         var prev = false;
6207         Roo.each(this.navItems, function(v,i){
6208             
6209             if (v.isActive()) {
6210                 prev = i;
6211                 
6212             }
6213             
6214         });
6215         return prev;
6216     },
6217     /**
6218     * adds a Navigation item
6219     * @param {Roo.bootstrap.NavItem} the navitem to add
6220     */
6221     addItem : function(cfg)
6222     {
6223         if (this.form && Roo.bootstrap.version == 4) {
6224             cfg.tag = 'div';
6225         }
6226         var cn = new Roo.bootstrap.NavItem(cfg);
6227         this.register(cn);
6228         cn.parentId = this.id;
6229         cn.onRender(this.el, null);
6230         return cn;
6231     },
6232     /**
6233     * register a Navigation item
6234     * @param {Roo.bootstrap.NavItem} the navitem to add
6235     */
6236     register : function(item)
6237     {
6238         this.navItems.push( item);
6239         item.navId = this.navId;
6240     
6241     },
6242     
6243     /**
6244     * clear all the Navigation item
6245     */
6246    
6247     clearAll : function()
6248     {
6249         this.navItems = [];
6250         this.el.dom.innerHTML = '';
6251     },
6252     
6253     getNavItem: function(tabId)
6254     {
6255         var ret = false;
6256         Roo.each(this.navItems, function(e) {
6257             if (e.tabId == tabId) {
6258                ret =  e;
6259                return false;
6260             }
6261             return true;
6262             
6263         });
6264         return ret;
6265     },
6266     
6267     setActiveNext : function()
6268     {
6269         var i = this.indexOfNav(this.getActive());
6270         if (i > this.navItems.length) {
6271             return;
6272         }
6273         this.setActiveItem(this.navItems[i+1]);
6274     },
6275     setActivePrev : function()
6276     {
6277         var i = this.indexOfNav(this.getActive());
6278         if (i  < 1) {
6279             return;
6280         }
6281         this.setActiveItem(this.navItems[i-1]);
6282     },
6283     clearWasActive : function(except) {
6284         Roo.each(this.navItems, function(e) {
6285             if (e.tabId != except.tabId && e.was_active) {
6286                e.was_active = false;
6287                return false;
6288             }
6289             return true;
6290             
6291         });
6292     },
6293     getWasActive : function ()
6294     {
6295         var r = false;
6296         Roo.each(this.navItems, function(e) {
6297             if (e.was_active) {
6298                r = e;
6299                return false;
6300             }
6301             return true;
6302             
6303         });
6304         return r;
6305     }
6306     
6307     
6308 });
6309
6310  
6311 Roo.apply(Roo.bootstrap.NavGroup, {
6312     
6313     groups: {},
6314      /**
6315     * register a Navigation Group
6316     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6317     */
6318     register : function(navgrp)
6319     {
6320         this.groups[navgrp.navId] = navgrp;
6321         
6322     },
6323     /**
6324     * fetch a Navigation Group based on the navigation ID
6325     * @param {string} the navgroup to add
6326     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6327     */
6328     get: function(navId) {
6329         if (typeof(this.groups[navId]) == 'undefined') {
6330             return false;
6331             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6332         }
6333         return this.groups[navId] ;
6334     }
6335     
6336     
6337     
6338 });
6339
6340  /*
6341  * - LGPL
6342  *
6343  * row
6344  * 
6345  */
6346
6347 /**
6348  * @class Roo.bootstrap.NavItem
6349  * @extends Roo.bootstrap.Component
6350  * Bootstrap Navbar.NavItem class
6351  * @cfg {String} href  link to
6352  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6353  * @cfg {Boolean} button_outline show and outlined button
6354  * @cfg {String} html content of button
6355  * @cfg {String} badge text inside badge
6356  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6357  * @cfg {String} glyphicon DEPRICATED - use fa
6358  * @cfg {String} icon DEPRICATED - use fa
6359  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6360  * @cfg {Boolean} active Is item active
6361  * @cfg {Boolean} disabled Is item disabled
6362  * @cfg {String} linkcls  Link Class
6363  * @cfg {Boolean} preventDefault (true | false) default false
6364  * @cfg {String} tabId the tab that this item activates.
6365  * @cfg {String} tagtype (a|span) render as a href or span?
6366  * @cfg {Boolean} animateRef (true|false) link to element default false  
6367   
6368  * @constructor
6369  * Create a new Navbar Item
6370  * @param {Object} config The config object
6371  */
6372 Roo.bootstrap.NavItem = function(config){
6373     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6374     this.addEvents({
6375         // raw events
6376         /**
6377          * @event click
6378          * The raw click event for the entire grid.
6379          * @param {Roo.EventObject} e
6380          */
6381         "click" : true,
6382          /**
6383             * @event changed
6384             * Fires when the active item active state changes
6385             * @param {Roo.bootstrap.NavItem} this
6386             * @param {boolean} state the new state
6387              
6388          */
6389         'changed': true,
6390         /**
6391             * @event scrollto
6392             * Fires when scroll to element
6393             * @param {Roo.bootstrap.NavItem} this
6394             * @param {Object} options
6395             * @param {Roo.EventObject} e
6396              
6397          */
6398         'scrollto': true
6399     });
6400    
6401 };
6402
6403 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6404     
6405     href: false,
6406     html: '',
6407     badge: '',
6408     icon: false,
6409     fa : false,
6410     glyphicon: false,
6411     active: false,
6412     preventDefault : false,
6413     tabId : false,
6414     tagtype : 'a',
6415     tag: 'li',
6416     disabled : false,
6417     animateRef : false,
6418     was_active : false,
6419     button_weight : '',
6420     button_outline : false,
6421     linkcls : '',
6422     navLink: false,
6423     
6424     getAutoCreate : function(){
6425          
6426         var cfg = {
6427             tag: this.tag,
6428             cls: 'nav-item'
6429         };
6430         
6431         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6432         
6433         if (this.active) {
6434             cfg.cls +=  ' active' ;
6435         }
6436         if (this.disabled) {
6437             cfg.cls += ' disabled';
6438         }
6439         
6440         // BS4 only?
6441         if (this.button_weight.length) {
6442             cfg.tag = this.href ? 'a' : 'button';
6443             cfg.html = this.html || '';
6444             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6445             if (this.href) {
6446                 cfg.href = this.href;
6447             }
6448             if (this.fa) {
6449                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6450             } else {
6451                 cfg.cls += " nav-html";
6452             }
6453             
6454             // menu .. should add dropdown-menu class - so no need for carat..
6455             
6456             if (this.badge !== '') {
6457                  
6458                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6459             }
6460             return cfg;
6461         }
6462         
6463         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6464             cfg.cn = [
6465                 {
6466                     tag: this.tagtype,
6467                     href : this.href || "#",
6468                     html: this.html || '',
6469                     cls : ''
6470                 }
6471             ];
6472             if (this.tagtype == 'a') {
6473                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6474         
6475             }
6476             if (this.icon) {
6477                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6478             } else  if (this.fa) {
6479                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6480             } else if(this.glyphicon) {
6481                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6482             } else {
6483                 cfg.cn[0].cls += " nav-html";
6484             }
6485             
6486             if (this.menu) {
6487                 cfg.cn[0].html += " <span class='caret'></span>";
6488              
6489             }
6490             
6491             if (this.badge !== '') {
6492                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6493             }
6494         }
6495         
6496         
6497         
6498         return cfg;
6499     },
6500     onRender : function(ct, position)
6501     {
6502        // Roo.log("Call onRender: " + this.xtype);
6503         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6504             this.tag = 'div';
6505         }
6506         
6507         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6508         this.navLink = this.el.select('.nav-link',true).first();
6509         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6510         return ret;
6511     },
6512       
6513     
6514     initEvents: function() 
6515     {
6516         if (typeof (this.menu) != 'undefined') {
6517             this.menu.parentType = this.xtype;
6518             this.menu.triggerEl = this.el;
6519             this.menu = this.addxtype(Roo.apply({}, this.menu));
6520         }
6521         
6522         this.el.on('click', this.onClick, this);
6523         
6524         //if(this.tagtype == 'span'){
6525         //    this.el.select('span',true).on('click', this.onClick, this);
6526         //}
6527        
6528         // at this point parent should be available..
6529         this.parent().register(this);
6530     },
6531     
6532     onClick : function(e)
6533     {
6534         if (e.getTarget('.dropdown-menu-item')) {
6535             // did you click on a menu itemm.... - then don't trigger onclick..
6536             return;
6537         }
6538         
6539         if(
6540                 this.preventDefault || 
6541                 this.href == '#' 
6542         ){
6543             Roo.log("NavItem - prevent Default?");
6544             e.preventDefault();
6545         }
6546         
6547         if (this.disabled) {
6548             return;
6549         }
6550         
6551         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6552         if (tg && tg.transition) {
6553             Roo.log("waiting for the transitionend");
6554             return;
6555         }
6556         
6557         
6558         
6559         //Roo.log("fire event clicked");
6560         if(this.fireEvent('click', this, e) === false){
6561             return;
6562         };
6563         
6564         if(this.tagtype == 'span'){
6565             return;
6566         }
6567         
6568         //Roo.log(this.href);
6569         var ael = this.el.select('a',true).first();
6570         //Roo.log(ael);
6571         
6572         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6573             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6574             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6575                 return; // ignore... - it's a 'hash' to another page.
6576             }
6577             Roo.log("NavItem - prevent Default?");
6578             e.preventDefault();
6579             this.scrollToElement(e);
6580         }
6581         
6582         
6583         var p =  this.parent();
6584    
6585         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6586             if (typeof(p.setActiveItem) !== 'undefined') {
6587                 p.setActiveItem(this);
6588             }
6589         }
6590         
6591         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6592         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6593             // remove the collapsed menu expand...
6594             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6595         }
6596     },
6597     
6598     isActive: function () {
6599         return this.active
6600     },
6601     setActive : function(state, fire, is_was_active)
6602     {
6603         if (this.active && !state && this.navId) {
6604             this.was_active = true;
6605             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6606             if (nv) {
6607                 nv.clearWasActive(this);
6608             }
6609             
6610         }
6611         this.active = state;
6612         
6613         if (!state ) {
6614             this.el.removeClass('active');
6615             this.navLink ? this.navLink.removeClass('active') : false;
6616         } else if (!this.el.hasClass('active')) {
6617             
6618             this.el.addClass('active');
6619             if (Roo.bootstrap.version == 4 && this.navLink ) {
6620                 this.navLink.addClass('active');
6621             }
6622             
6623         }
6624         if (fire) {
6625             this.fireEvent('changed', this, state);
6626         }
6627         
6628         // show a panel if it's registered and related..
6629         
6630         if (!this.navId || !this.tabId || !state || is_was_active) {
6631             return;
6632         }
6633         
6634         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6635         if (!tg) {
6636             return;
6637         }
6638         var pan = tg.getPanelByName(this.tabId);
6639         if (!pan) {
6640             return;
6641         }
6642         // if we can not flip to new panel - go back to old nav highlight..
6643         if (false == tg.showPanel(pan)) {
6644             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6645             if (nv) {
6646                 var onav = nv.getWasActive();
6647                 if (onav) {
6648                     onav.setActive(true, false, true);
6649                 }
6650             }
6651             
6652         }
6653         
6654         
6655         
6656     },
6657      // this should not be here...
6658     setDisabled : function(state)
6659     {
6660         this.disabled = state;
6661         if (!state ) {
6662             this.el.removeClass('disabled');
6663         } else if (!this.el.hasClass('disabled')) {
6664             this.el.addClass('disabled');
6665         }
6666         
6667     },
6668     
6669     /**
6670      * Fetch the element to display the tooltip on.
6671      * @return {Roo.Element} defaults to this.el
6672      */
6673     tooltipEl : function()
6674     {
6675         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6676     },
6677     
6678     scrollToElement : function(e)
6679     {
6680         var c = document.body;
6681         
6682         /*
6683          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6684          */
6685         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6686             c = document.documentElement;
6687         }
6688         
6689         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6690         
6691         if(!target){
6692             return;
6693         }
6694
6695         var o = target.calcOffsetsTo(c);
6696         
6697         var options = {
6698             target : target,
6699             value : o[1]
6700         };
6701         
6702         this.fireEvent('scrollto', this, options, e);
6703         
6704         Roo.get(c).scrollTo('top', options.value, true);
6705         
6706         return;
6707     },
6708     /**
6709      * Set the HTML (text content) of the item
6710      * @param {string} html  content for the nav item
6711      */
6712     setHtml : function(html)
6713     {
6714         this.html = html;
6715         this.htmlEl.dom.innerHTML = html;
6716         
6717     } 
6718 });
6719  
6720
6721  /*
6722  * - LGPL
6723  *
6724  * sidebar item
6725  *
6726  *  li
6727  *    <span> icon </span>
6728  *    <span> text </span>
6729  *    <span>badge </span>
6730  */
6731
6732 /**
6733  * @class Roo.bootstrap.NavSidebarItem
6734  * @extends Roo.bootstrap.NavItem
6735  * Bootstrap Navbar.NavSidebarItem class
6736  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6737  * {Boolean} open is the menu open
6738  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6739  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6740  * {String} buttonSize (sm|md|lg)the extra classes for the button
6741  * {Boolean} showArrow show arrow next to the text (default true)
6742  * @constructor
6743  * Create a new Navbar Button
6744  * @param {Object} config The config object
6745  */
6746 Roo.bootstrap.NavSidebarItem = function(config){
6747     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6748     this.addEvents({
6749         // raw events
6750         /**
6751          * @event click
6752          * The raw click event for the entire grid.
6753          * @param {Roo.EventObject} e
6754          */
6755         "click" : true,
6756          /**
6757             * @event changed
6758             * Fires when the active item active state changes
6759             * @param {Roo.bootstrap.NavSidebarItem} this
6760             * @param {boolean} state the new state
6761              
6762          */
6763         'changed': true
6764     });
6765    
6766 };
6767
6768 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6769     
6770     badgeWeight : 'default',
6771     
6772     open: false,
6773     
6774     buttonView : false,
6775     
6776     buttonWeight : 'default',
6777     
6778     buttonSize : 'md',
6779     
6780     showArrow : true,
6781     
6782     getAutoCreate : function(){
6783         
6784         
6785         var a = {
6786                 tag: 'a',
6787                 href : this.href || '#',
6788                 cls: '',
6789                 html : '',
6790                 cn : []
6791         };
6792         
6793         if(this.buttonView){
6794             a = {
6795                 tag: 'button',
6796                 href : this.href || '#',
6797                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6798                 html : this.html,
6799                 cn : []
6800             };
6801         }
6802         
6803         var cfg = {
6804             tag: 'li',
6805             cls: '',
6806             cn: [ a ]
6807         };
6808         
6809         if (this.active) {
6810             cfg.cls += ' active';
6811         }
6812         
6813         if (this.disabled) {
6814             cfg.cls += ' disabled';
6815         }
6816         if (this.open) {
6817             cfg.cls += ' open x-open';
6818         }
6819         // left icon..
6820         if (this.glyphicon || this.icon) {
6821             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6822             a.cn.push({ tag : 'i', cls : c }) ;
6823         }
6824         
6825         if(!this.buttonView){
6826             var span = {
6827                 tag: 'span',
6828                 html : this.html || ''
6829             };
6830
6831             a.cn.push(span);
6832             
6833         }
6834         
6835         if (this.badge !== '') {
6836             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6837         }
6838         
6839         if (this.menu) {
6840             
6841             if(this.showArrow){
6842                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6843             }
6844             
6845             a.cls += ' dropdown-toggle treeview' ;
6846         }
6847         
6848         return cfg;
6849     },
6850     
6851     initEvents : function()
6852     { 
6853         if (typeof (this.menu) != 'undefined') {
6854             this.menu.parentType = this.xtype;
6855             this.menu.triggerEl = this.el;
6856             this.menu = this.addxtype(Roo.apply({}, this.menu));
6857         }
6858         
6859         this.el.on('click', this.onClick, this);
6860         
6861         if(this.badge !== ''){
6862             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6863         }
6864         
6865     },
6866     
6867     onClick : function(e)
6868     {
6869         if(this.disabled){
6870             e.preventDefault();
6871             return;
6872         }
6873         
6874         if(this.preventDefault){
6875             e.preventDefault();
6876         }
6877         
6878         this.fireEvent('click', this, e);
6879     },
6880     
6881     disable : function()
6882     {
6883         this.setDisabled(true);
6884     },
6885     
6886     enable : function()
6887     {
6888         this.setDisabled(false);
6889     },
6890     
6891     setDisabled : function(state)
6892     {
6893         if(this.disabled == state){
6894             return;
6895         }
6896         
6897         this.disabled = state;
6898         
6899         if (state) {
6900             this.el.addClass('disabled');
6901             return;
6902         }
6903         
6904         this.el.removeClass('disabled');
6905         
6906         return;
6907     },
6908     
6909     setActive : function(state)
6910     {
6911         if(this.active == state){
6912             return;
6913         }
6914         
6915         this.active = state;
6916         
6917         if (state) {
6918             this.el.addClass('active');
6919             return;
6920         }
6921         
6922         this.el.removeClass('active');
6923         
6924         return;
6925     },
6926     
6927     isActive: function () 
6928     {
6929         return this.active;
6930     },
6931     
6932     setBadge : function(str)
6933     {
6934         if(!this.badgeEl){
6935             return;
6936         }
6937         
6938         this.badgeEl.dom.innerHTML = str;
6939     }
6940     
6941    
6942      
6943  
6944 });
6945  
6946
6947  /*
6948  * - LGPL
6949  *
6950  *  Breadcrumb Nav
6951  * 
6952  */
6953 Roo.namespace('Roo.bootstrap.breadcrumb');
6954
6955
6956 /**
6957  * @class Roo.bootstrap.breadcrumb.Nav
6958  * @extends Roo.bootstrap.Component
6959  * Bootstrap Breadcrumb Nav Class
6960  *  
6961  * @children Roo.bootstrap.breadcrumb.Item
6962  * 
6963  * @constructor
6964  * Create a new breadcrumb.Nav
6965  * @param {Object} config The config object
6966  */
6967
6968
6969 Roo.bootstrap.breadcrumb.Nav = function(config){
6970     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6971     
6972     
6973 };
6974
6975 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6976     
6977     getAutoCreate : function()
6978     {
6979
6980         var cfg = {
6981             tag: 'nav',
6982             cn : [
6983                 {
6984                     tag : 'ol',
6985                     cls : 'breadcrumb'
6986                 }
6987             ]
6988             
6989         };
6990           
6991         return cfg;
6992     },
6993     
6994     initEvents: function()
6995     {
6996         this.olEl = this.el.select('ol',true).first();    
6997     },
6998     getChildContainer : function()
6999     {
7000         return this.olEl;  
7001     }
7002     
7003 });
7004
7005  /*
7006  * - LGPL
7007  *
7008  *  Breadcrumb Item
7009  * 
7010  */
7011
7012
7013 /**
7014  * @class Roo.bootstrap.breadcrumb.Nav
7015  * @extends Roo.bootstrap.Component
7016  * Bootstrap Breadcrumb Nav Class
7017  *  
7018  * @children Roo.bootstrap.breadcrumb.Component
7019  * @cfg {String} html the content of the link.
7020  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7021  * @cfg {Boolean} active is it active
7022
7023  * 
7024  * @constructor
7025  * Create a new breadcrumb.Nav
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.breadcrumb.Item = function(config){
7030     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7031     this.addEvents({
7032         // img events
7033         /**
7034          * @event click
7035          * The img click event for the img.
7036          * @param {Roo.EventObject} e
7037          */
7038         "click" : true
7039     });
7040     
7041 };
7042
7043 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7044     
7045     href: false,
7046     html : '',
7047     
7048     getAutoCreate : function()
7049     {
7050
7051         var cfg = {
7052             tag: 'li',
7053             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7054         };
7055         if (this.href !== false) {
7056             cfg.cn = [{
7057                 tag : 'a',
7058                 href : this.href,
7059                 html : this.html
7060             }];
7061         } else {
7062             cfg.html = this.html;
7063         }
7064         
7065         return cfg;
7066     },
7067     
7068     initEvents: function()
7069     {
7070         if (this.href) {
7071             this.el.select('a', true).first().on('click',this.onClick, this)
7072         }
7073         
7074     },
7075     onClick : function(e)
7076     {
7077         e.preventDefault();
7078         this.fireEvent('click',this,  e);
7079     }
7080     
7081 });
7082
7083  /*
7084  * - LGPL
7085  *
7086  * row
7087  * 
7088  */
7089
7090 /**
7091  * @class Roo.bootstrap.Row
7092  * @extends Roo.bootstrap.Component
7093  * Bootstrap Row class (contains columns...)
7094  * 
7095  * @constructor
7096  * Create a new Row
7097  * @param {Object} config The config object
7098  */
7099
7100 Roo.bootstrap.Row = function(config){
7101     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7102 };
7103
7104 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7105     
7106     getAutoCreate : function(){
7107        return {
7108             cls: 'row clearfix'
7109        };
7110     }
7111     
7112     
7113 });
7114
7115  
7116
7117  /*
7118  * - LGPL
7119  *
7120  * pagination
7121  * 
7122  */
7123
7124 /**
7125  * @class Roo.bootstrap.Pagination
7126  * @extends Roo.bootstrap.Component
7127  * Bootstrap Pagination class
7128  * @cfg {String} size xs | sm | md | lg
7129  * @cfg {Boolean} inverse false | true
7130  * 
7131  * @constructor
7132  * Create a new Pagination
7133  * @param {Object} config The config object
7134  */
7135
7136 Roo.bootstrap.Pagination = function(config){
7137     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7138 };
7139
7140 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7141     
7142     cls: false,
7143     size: false,
7144     inverse: false,
7145     
7146     getAutoCreate : function(){
7147         var cfg = {
7148             tag: 'ul',
7149                 cls: 'pagination'
7150         };
7151         if (this.inverse) {
7152             cfg.cls += ' inverse';
7153         }
7154         if (this.html) {
7155             cfg.html=this.html;
7156         }
7157         if (this.cls) {
7158             cfg.cls += " " + this.cls;
7159         }
7160         return cfg;
7161     }
7162    
7163 });
7164
7165  
7166
7167  /*
7168  * - LGPL
7169  *
7170  * Pagination item
7171  * 
7172  */
7173
7174
7175 /**
7176  * @class Roo.bootstrap.PaginationItem
7177  * @extends Roo.bootstrap.Component
7178  * Bootstrap PaginationItem class
7179  * @cfg {String} html text
7180  * @cfg {String} href the link
7181  * @cfg {Boolean} preventDefault (true | false) default true
7182  * @cfg {Boolean} active (true | false) default false
7183  * @cfg {Boolean} disabled default false
7184  * 
7185  * 
7186  * @constructor
7187  * Create a new PaginationItem
7188  * @param {Object} config The config object
7189  */
7190
7191
7192 Roo.bootstrap.PaginationItem = function(config){
7193     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7194     this.addEvents({
7195         // raw events
7196         /**
7197          * @event click
7198          * The raw click event for the entire grid.
7199          * @param {Roo.EventObject} e
7200          */
7201         "click" : true
7202     });
7203 };
7204
7205 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7206     
7207     href : false,
7208     html : false,
7209     preventDefault: true,
7210     active : false,
7211     cls : false,
7212     disabled: false,
7213     
7214     getAutoCreate : function(){
7215         var cfg= {
7216             tag: 'li',
7217             cn: [
7218                 {
7219                     tag : 'a',
7220                     href : this.href ? this.href : '#',
7221                     html : this.html ? this.html : ''
7222                 }
7223             ]
7224         };
7225         
7226         if(this.cls){
7227             cfg.cls = this.cls;
7228         }
7229         
7230         if(this.disabled){
7231             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7232         }
7233         
7234         if(this.active){
7235             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7236         }
7237         
7238         return cfg;
7239     },
7240     
7241     initEvents: function() {
7242         
7243         this.el.on('click', this.onClick, this);
7244         
7245     },
7246     onClick : function(e)
7247     {
7248         Roo.log('PaginationItem on click ');
7249         if(this.preventDefault){
7250             e.preventDefault();
7251         }
7252         
7253         if(this.disabled){
7254             return;
7255         }
7256         
7257         this.fireEvent('click', this, e);
7258     }
7259    
7260 });
7261
7262  
7263
7264  /*
7265  * - LGPL
7266  *
7267  * slider
7268  * 
7269  */
7270
7271
7272 /**
7273  * @class Roo.bootstrap.Slider
7274  * @extends Roo.bootstrap.Component
7275  * Bootstrap Slider class
7276  *    
7277  * @constructor
7278  * Create a new Slider
7279  * @param {Object} config The config object
7280  */
7281
7282 Roo.bootstrap.Slider = function(config){
7283     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7284 };
7285
7286 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7287     
7288     getAutoCreate : function(){
7289         
7290         var cfg = {
7291             tag: 'div',
7292             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7293             cn: [
7294                 {
7295                     tag: 'a',
7296                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7297                 }
7298             ]
7299         };
7300         
7301         return cfg;
7302     }
7303    
7304 });
7305
7306  /*
7307  * Based on:
7308  * Ext JS Library 1.1.1
7309  * Copyright(c) 2006-2007, Ext JS, LLC.
7310  *
7311  * Originally Released Under LGPL - original licence link has changed is not relivant.
7312  *
7313  * Fork - LGPL
7314  * <script type="text/javascript">
7315  */
7316  /**
7317  * @extends Roo.dd.DDProxy
7318  * @class Roo.grid.SplitDragZone
7319  * Support for Column Header resizing
7320  * @constructor
7321  * @param {Object} config
7322  */
7323 // private
7324 // This is a support class used internally by the Grid components
7325 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7326     this.grid = grid;
7327     this.view = grid.getView();
7328     this.proxy = this.view.resizeProxy;
7329     Roo.grid.SplitDragZone.superclass.constructor.call(
7330         this,
7331         hd, // ID
7332         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7333         {  // CONFIG
7334             dragElId : Roo.id(this.proxy.dom),
7335             resizeFrame:false
7336         }
7337     );
7338     
7339     this.setHandleElId(Roo.id(hd));
7340     if (hd2 !== false) {
7341         this.setOuterHandleElId(Roo.id(hd2));
7342     }
7343     
7344     this.scroll = false;
7345 };
7346 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7347     fly: Roo.Element.fly,
7348
7349     b4StartDrag : function(x, y){
7350         this.view.headersDisabled = true;
7351         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7352                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7353         );
7354         this.proxy.setHeight(h);
7355         
7356         // for old system colWidth really stored the actual width?
7357         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7358         // which in reality did not work.. - it worked only for fixed sizes
7359         // for resizable we need to use actual sizes.
7360         var w = this.cm.getColumnWidth(this.cellIndex);
7361         if (!this.view.mainWrap) {
7362             // bootstrap.
7363             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7364         }
7365         
7366         
7367         
7368         // this was w-this.grid.minColumnWidth;
7369         // doesnt really make sense? - w = thie curren width or the rendered one?
7370         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7371         this.resetConstraints();
7372         this.setXConstraint(minw, 1000);
7373         this.setYConstraint(0, 0);
7374         this.minX = x - minw;
7375         this.maxX = x + 1000;
7376         this.startPos = x;
7377         if (!this.view.mainWrap) { // this is Bootstrap code..
7378             this.getDragEl().style.display='block';
7379         }
7380         
7381         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7382     },
7383
7384
7385     handleMouseDown : function(e){
7386         ev = Roo.EventObject.setEvent(e);
7387         var t = this.fly(ev.getTarget());
7388         if(t.hasClass("x-grid-split")){
7389             this.cellIndex = this.view.getCellIndex(t.dom);
7390             this.split = t.dom;
7391             this.cm = this.grid.colModel;
7392             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7393                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7394             }
7395         }
7396     },
7397
7398     endDrag : function(e){
7399         this.view.headersDisabled = false;
7400         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7401         var diff = endX - this.startPos;
7402         // 
7403         var w = this.cm.getColumnWidth(this.cellIndex);
7404         if (!this.view.mainWrap) {
7405             w = 0;
7406         }
7407         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7408     },
7409
7410     autoOffset : function(){
7411         this.setDelta(0,0);
7412     }
7413 });/*
7414  * Based on:
7415  * Ext JS Library 1.1.1
7416  * Copyright(c) 2006-2007, Ext JS, LLC.
7417  *
7418  * Originally Released Under LGPL - original licence link has changed is not relivant.
7419  *
7420  * Fork - LGPL
7421  * <script type="text/javascript">
7422  */
7423
7424 /**
7425  * @class Roo.grid.AbstractSelectionModel
7426  * @extends Roo.util.Observable
7427  * @abstract
7428  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7429  * implemented by descendant classes.  This class should not be directly instantiated.
7430  * @constructor
7431  */
7432 Roo.grid.AbstractSelectionModel = function(){
7433     this.locked = false;
7434     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7435 };
7436
7437 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7438     /** @ignore Called by the grid automatically. Do not call directly. */
7439     init : function(grid){
7440         this.grid = grid;
7441         this.initEvents();
7442     },
7443
7444     /**
7445      * Locks the selections.
7446      */
7447     lock : function(){
7448         this.locked = true;
7449     },
7450
7451     /**
7452      * Unlocks the selections.
7453      */
7454     unlock : function(){
7455         this.locked = false;
7456     },
7457
7458     /**
7459      * Returns true if the selections are locked.
7460      * @return {Boolean}
7461      */
7462     isLocked : function(){
7463         return this.locked;
7464     }
7465 });/*
7466  * Based on:
7467  * Ext JS Library 1.1.1
7468  * Copyright(c) 2006-2007, Ext JS, LLC.
7469  *
7470  * Originally Released Under LGPL - original licence link has changed is not relivant.
7471  *
7472  * Fork - LGPL
7473  * <script type="text/javascript">
7474  */
7475 /**
7476  * @extends Roo.grid.AbstractSelectionModel
7477  * @class Roo.grid.RowSelectionModel
7478  * The default SelectionModel used by {@link Roo.grid.Grid}.
7479  * It supports multiple selections and keyboard selection/navigation. 
7480  * @constructor
7481  * @param {Object} config
7482  */
7483 Roo.grid.RowSelectionModel = function(config){
7484     Roo.apply(this, config);
7485     this.selections = new Roo.util.MixedCollection(false, function(o){
7486         return o.id;
7487     });
7488
7489     this.last = false;
7490     this.lastActive = false;
7491
7492     this.addEvents({
7493         /**
7494         * @event selectionchange
7495         * Fires when the selection changes
7496         * @param {SelectionModel} this
7497         */
7498        "selectionchange" : true,
7499        /**
7500         * @event afterselectionchange
7501         * Fires after the selection changes (eg. by key press or clicking)
7502         * @param {SelectionModel} this
7503         */
7504        "afterselectionchange" : true,
7505        /**
7506         * @event beforerowselect
7507         * Fires when a row is selected being selected, return false to cancel.
7508         * @param {SelectionModel} this
7509         * @param {Number} rowIndex The selected index
7510         * @param {Boolean} keepExisting False if other selections will be cleared
7511         */
7512        "beforerowselect" : true,
7513        /**
7514         * @event rowselect
7515         * Fires when a row is selected.
7516         * @param {SelectionModel} this
7517         * @param {Number} rowIndex The selected index
7518         * @param {Roo.data.Record} r The record
7519         */
7520        "rowselect" : true,
7521        /**
7522         * @event rowdeselect
7523         * Fires when a row is deselected.
7524         * @param {SelectionModel} this
7525         * @param {Number} rowIndex The selected index
7526         */
7527         "rowdeselect" : true
7528     });
7529     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7530     this.locked = false;
7531 };
7532
7533 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7534     /**
7535      * @cfg {Boolean} singleSelect
7536      * True to allow selection of only one row at a time (defaults to false)
7537      */
7538     singleSelect : false,
7539
7540     // private
7541     initEvents : function(){
7542
7543         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7544             this.grid.on("mousedown", this.handleMouseDown, this);
7545         }else{ // allow click to work like normal
7546             this.grid.on("rowclick", this.handleDragableRowClick, this);
7547         }
7548         // bootstrap does not have a view..
7549         var view = this.grid.view ? this.grid.view : this.grid;
7550         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7551             "up" : function(e){
7552                 if(!e.shiftKey){
7553                     this.selectPrevious(e.shiftKey);
7554                 }else if(this.last !== false && this.lastActive !== false){
7555                     var last = this.last;
7556                     this.selectRange(this.last,  this.lastActive-1);
7557                     view.focusRow(this.lastActive);
7558                     if(last !== false){
7559                         this.last = last;
7560                     }
7561                 }else{
7562                     this.selectFirstRow();
7563                 }
7564                 this.fireEvent("afterselectionchange", this);
7565             },
7566             "down" : function(e){
7567                 if(!e.shiftKey){
7568                     this.selectNext(e.shiftKey);
7569                 }else if(this.last !== false && this.lastActive !== false){
7570                     var last = this.last;
7571                     this.selectRange(this.last,  this.lastActive+1);
7572                     view.focusRow(this.lastActive);
7573                     if(last !== false){
7574                         this.last = last;
7575                     }
7576                 }else{
7577                     this.selectFirstRow();
7578                 }
7579                 this.fireEvent("afterselectionchange", this);
7580             },
7581             scope: this
7582         });
7583
7584          
7585         view.on("refresh", this.onRefresh, this);
7586         view.on("rowupdated", this.onRowUpdated, this);
7587         view.on("rowremoved", this.onRemove, this);
7588     },
7589
7590     // private
7591     onRefresh : function(){
7592         var ds = this.grid.ds, i, v = this.grid.view;
7593         var s = this.selections;
7594         s.each(function(r){
7595             if((i = ds.indexOfId(r.id)) != -1){
7596                 v.onRowSelect(i);
7597                 s.add(ds.getAt(i)); // updating the selection relate data
7598             }else{
7599                 s.remove(r);
7600             }
7601         });
7602     },
7603
7604     // private
7605     onRemove : function(v, index, r){
7606         this.selections.remove(r);
7607     },
7608
7609     // private
7610     onRowUpdated : function(v, index, r){
7611         if(this.isSelected(r)){
7612             v.onRowSelect(index);
7613         }
7614     },
7615
7616     /**
7617      * Select records.
7618      * @param {Array} records The records to select
7619      * @param {Boolean} keepExisting (optional) True to keep existing selections
7620      */
7621     selectRecords : function(records, keepExisting){
7622         if(!keepExisting){
7623             this.clearSelections();
7624         }
7625         var ds = this.grid.ds;
7626         for(var i = 0, len = records.length; i < len; i++){
7627             this.selectRow(ds.indexOf(records[i]), true);
7628         }
7629     },
7630
7631     /**
7632      * Gets the number of selected rows.
7633      * @return {Number}
7634      */
7635     getCount : function(){
7636         return this.selections.length;
7637     },
7638
7639     /**
7640      * Selects the first row in the grid.
7641      */
7642     selectFirstRow : function(){
7643         this.selectRow(0);
7644     },
7645
7646     /**
7647      * Select the last row.
7648      * @param {Boolean} keepExisting (optional) True to keep existing selections
7649      */
7650     selectLastRow : function(keepExisting){
7651         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7652     },
7653
7654     /**
7655      * Selects the row immediately following the last selected row.
7656      * @param {Boolean} keepExisting (optional) True to keep existing selections
7657      */
7658     selectNext : function(keepExisting){
7659         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7660             this.selectRow(this.last+1, keepExisting);
7661             var view = this.grid.view ? this.grid.view : this.grid;
7662             view.focusRow(this.last);
7663         }
7664     },
7665
7666     /**
7667      * Selects the row that precedes the last selected row.
7668      * @param {Boolean} keepExisting (optional) True to keep existing selections
7669      */
7670     selectPrevious : function(keepExisting){
7671         if(this.last){
7672             this.selectRow(this.last-1, keepExisting);
7673             var view = this.grid.view ? this.grid.view : this.grid;
7674             view.focusRow(this.last);
7675         }
7676     },
7677
7678     /**
7679      * Returns the selected records
7680      * @return {Array} Array of selected records
7681      */
7682     getSelections : function(){
7683         return [].concat(this.selections.items);
7684     },
7685
7686     /**
7687      * Returns the first selected record.
7688      * @return {Record}
7689      */
7690     getSelected : function(){
7691         return this.selections.itemAt(0);
7692     },
7693
7694
7695     /**
7696      * Clears all selections.
7697      */
7698     clearSelections : function(fast){
7699         if(this.locked) {
7700             return;
7701         }
7702         if(fast !== true){
7703             var ds = this.grid.ds;
7704             var s = this.selections;
7705             s.each(function(r){
7706                 this.deselectRow(ds.indexOfId(r.id));
7707             }, this);
7708             s.clear();
7709         }else{
7710             this.selections.clear();
7711         }
7712         this.last = false;
7713     },
7714
7715
7716     /**
7717      * Selects all rows.
7718      */
7719     selectAll : function(){
7720         if(this.locked) {
7721             return;
7722         }
7723         this.selections.clear();
7724         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7725             this.selectRow(i, true);
7726         }
7727     },
7728
7729     /**
7730      * Returns True if there is a selection.
7731      * @return {Boolean}
7732      */
7733     hasSelection : function(){
7734         return this.selections.length > 0;
7735     },
7736
7737     /**
7738      * Returns True if the specified row is selected.
7739      * @param {Number/Record} record The record or index of the record to check
7740      * @return {Boolean}
7741      */
7742     isSelected : function(index){
7743         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7744         return (r && this.selections.key(r.id) ? true : false);
7745     },
7746
7747     /**
7748      * Returns True if the specified record id is selected.
7749      * @param {String} id The id of record to check
7750      * @return {Boolean}
7751      */
7752     isIdSelected : function(id){
7753         return (this.selections.key(id) ? true : false);
7754     },
7755
7756     // private
7757     handleMouseDown : function(e, t)
7758     {
7759         var view = this.grid.view ? this.grid.view : this.grid;
7760         var rowIndex;
7761         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7762             return;
7763         };
7764         if(e.shiftKey && this.last !== false){
7765             var last = this.last;
7766             this.selectRange(last, rowIndex, e.ctrlKey);
7767             this.last = last; // reset the last
7768             view.focusRow(rowIndex);
7769         }else{
7770             var isSelected = this.isSelected(rowIndex);
7771             if(e.button !== 0 && isSelected){
7772                 view.focusRow(rowIndex);
7773             }else if(e.ctrlKey && isSelected){
7774                 this.deselectRow(rowIndex);
7775             }else if(!isSelected){
7776                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7777                 view.focusRow(rowIndex);
7778             }
7779         }
7780         this.fireEvent("afterselectionchange", this);
7781     },
7782     // private
7783     handleDragableRowClick :  function(grid, rowIndex, e) 
7784     {
7785         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7786             this.selectRow(rowIndex, false);
7787             var view = this.grid.view ? this.grid.view : this.grid;
7788             view.focusRow(rowIndex);
7789              this.fireEvent("afterselectionchange", this);
7790         }
7791     },
7792     
7793     /**
7794      * Selects multiple rows.
7795      * @param {Array} rows Array of the indexes of the row to select
7796      * @param {Boolean} keepExisting (optional) True to keep existing selections
7797      */
7798     selectRows : function(rows, keepExisting){
7799         if(!keepExisting){
7800             this.clearSelections();
7801         }
7802         for(var i = 0, len = rows.length; i < len; i++){
7803             this.selectRow(rows[i], true);
7804         }
7805     },
7806
7807     /**
7808      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7809      * @param {Number} startRow The index of the first row in the range
7810      * @param {Number} endRow The index of the last row in the range
7811      * @param {Boolean} keepExisting (optional) True to retain existing selections
7812      */
7813     selectRange : function(startRow, endRow, keepExisting){
7814         if(this.locked) {
7815             return;
7816         }
7817         if(!keepExisting){
7818             this.clearSelections();
7819         }
7820         if(startRow <= endRow){
7821             for(var i = startRow; i <= endRow; i++){
7822                 this.selectRow(i, true);
7823             }
7824         }else{
7825             for(var i = startRow; i >= endRow; i--){
7826                 this.selectRow(i, true);
7827             }
7828         }
7829     },
7830
7831     /**
7832      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7833      * @param {Number} startRow The index of the first row in the range
7834      * @param {Number} endRow The index of the last row in the range
7835      */
7836     deselectRange : function(startRow, endRow, preventViewNotify){
7837         if(this.locked) {
7838             return;
7839         }
7840         for(var i = startRow; i <= endRow; i++){
7841             this.deselectRow(i, preventViewNotify);
7842         }
7843     },
7844
7845     /**
7846      * Selects a row.
7847      * @param {Number} row The index of the row to select
7848      * @param {Boolean} keepExisting (optional) True to keep existing selections
7849      */
7850     selectRow : function(index, keepExisting, preventViewNotify){
7851         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7852             return;
7853         }
7854         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7855             if(!keepExisting || this.singleSelect){
7856                 this.clearSelections();
7857             }
7858             var r = this.grid.ds.getAt(index);
7859             this.selections.add(r);
7860             this.last = this.lastActive = index;
7861             if(!preventViewNotify){
7862                 var view = this.grid.view ? this.grid.view : this.grid;
7863                 view.onRowSelect(index);
7864             }
7865             this.fireEvent("rowselect", this, index, r);
7866             this.fireEvent("selectionchange", this);
7867         }
7868     },
7869
7870     /**
7871      * Deselects a row.
7872      * @param {Number} row The index of the row to deselect
7873      */
7874     deselectRow : function(index, preventViewNotify){
7875         if(this.locked) {
7876             return;
7877         }
7878         if(this.last == index){
7879             this.last = false;
7880         }
7881         if(this.lastActive == index){
7882             this.lastActive = false;
7883         }
7884         var r = this.grid.ds.getAt(index);
7885         this.selections.remove(r);
7886         if(!preventViewNotify){
7887             var view = this.grid.view ? this.grid.view : this.grid;
7888             view.onRowDeselect(index);
7889         }
7890         this.fireEvent("rowdeselect", this, index);
7891         this.fireEvent("selectionchange", this);
7892     },
7893
7894     // private
7895     restoreLast : function(){
7896         if(this._last){
7897             this.last = this._last;
7898         }
7899     },
7900
7901     // private
7902     acceptsNav : function(row, col, cm){
7903         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7904     },
7905
7906     // private
7907     onEditorKey : function(field, e){
7908         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7909         if(k == e.TAB){
7910             e.stopEvent();
7911             ed.completeEdit();
7912             if(e.shiftKey){
7913                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7914             }else{
7915                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7916             }
7917         }else if(k == e.ENTER && !e.ctrlKey){
7918             e.stopEvent();
7919             ed.completeEdit();
7920             if(e.shiftKey){
7921                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7922             }else{
7923                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7924             }
7925         }else if(k == e.ESC){
7926             ed.cancelEdit();
7927         }
7928         if(newCell){
7929             g.startEditing(newCell[0], newCell[1]);
7930         }
7931     }
7932 });/*
7933  * Based on:
7934  * Ext JS Library 1.1.1
7935  * Copyright(c) 2006-2007, Ext JS, LLC.
7936  *
7937  * Originally Released Under LGPL - original licence link has changed is not relivant.
7938  *
7939  * Fork - LGPL
7940  * <script type="text/javascript">
7941  */
7942  
7943
7944 /**
7945  * @class Roo.grid.ColumnModel
7946  * @extends Roo.util.Observable
7947  * This is the default implementation of a ColumnModel used by the Grid. It defines
7948  * the columns in the grid.
7949  * <br>Usage:<br>
7950  <pre><code>
7951  var colModel = new Roo.grid.ColumnModel([
7952         {header: "Ticker", width: 60, sortable: true, locked: true},
7953         {header: "Company Name", width: 150, sortable: true},
7954         {header: "Market Cap.", width: 100, sortable: true},
7955         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7956         {header: "Employees", width: 100, sortable: true, resizable: false}
7957  ]);
7958  </code></pre>
7959  * <p>
7960  
7961  * The config options listed for this class are options which may appear in each
7962  * individual column definition.
7963  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7964  * @constructor
7965  * @param {Object} config An Array of column config objects. See this class's
7966  * config objects for details.
7967 */
7968 Roo.grid.ColumnModel = function(config){
7969         /**
7970      * The config passed into the constructor
7971      */
7972     this.config = []; //config;
7973     this.lookup = {};
7974
7975     // if no id, create one
7976     // if the column does not have a dataIndex mapping,
7977     // map it to the order it is in the config
7978     for(var i = 0, len = config.length; i < len; i++){
7979         this.addColumn(config[i]);
7980         
7981     }
7982
7983     /**
7984      * The width of columns which have no width specified (defaults to 100)
7985      * @type Number
7986      */
7987     this.defaultWidth = 100;
7988
7989     /**
7990      * Default sortable of columns which have no sortable specified (defaults to false)
7991      * @type Boolean
7992      */
7993     this.defaultSortable = false;
7994
7995     this.addEvents({
7996         /**
7997              * @event widthchange
7998              * Fires when the width of a column changes.
7999              * @param {ColumnModel} this
8000              * @param {Number} columnIndex The column index
8001              * @param {Number} newWidth The new width
8002              */
8003             "widthchange": true,
8004         /**
8005              * @event headerchange
8006              * Fires when the text of a header changes.
8007              * @param {ColumnModel} this
8008              * @param {Number} columnIndex The column index
8009              * @param {Number} newText The new header text
8010              */
8011             "headerchange": true,
8012         /**
8013              * @event hiddenchange
8014              * Fires when a column is hidden or "unhidden".
8015              * @param {ColumnModel} this
8016              * @param {Number} columnIndex The column index
8017              * @param {Boolean} hidden true if hidden, false otherwise
8018              */
8019             "hiddenchange": true,
8020             /**
8021          * @event columnmoved
8022          * Fires when a column is moved.
8023          * @param {ColumnModel} this
8024          * @param {Number} oldIndex
8025          * @param {Number} newIndex
8026          */
8027         "columnmoved" : true,
8028         /**
8029          * @event columlockchange
8030          * Fires when a column's locked state is changed
8031          * @param {ColumnModel} this
8032          * @param {Number} colIndex
8033          * @param {Boolean} locked true if locked
8034          */
8035         "columnlockchange" : true
8036     });
8037     Roo.grid.ColumnModel.superclass.constructor.call(this);
8038 };
8039 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8040     /**
8041      * @cfg {String} header The header text to display in the Grid view.
8042      */
8043         /**
8044      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8045      */
8046         /**
8047      * @cfg {String} smHeader Header at Bootsrap Small width
8048      */
8049         /**
8050      * @cfg {String} mdHeader Header at Bootsrap Medium width
8051      */
8052         /**
8053      * @cfg {String} lgHeader Header at Bootsrap Large width
8054      */
8055         /**
8056      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8057      */
8058     /**
8059      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8060      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8061      * specified, the column's index is used as an index into the Record's data Array.
8062      */
8063     /**
8064      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8065      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8066      */
8067     /**
8068      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8069      * Defaults to the value of the {@link #defaultSortable} property.
8070      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8071      */
8072     /**
8073      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8074      */
8075     /**
8076      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8077      */
8078     /**
8079      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8080      */
8081     /**
8082      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8083      */
8084     /**
8085      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8086      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8087      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8088      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8089      */
8090        /**
8091      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8092      */
8093     /**
8094      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8095      */
8096     /**
8097      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8098      */
8099     /**
8100      * @cfg {String} cursor (Optional)
8101      */
8102     /**
8103      * @cfg {String} tooltip (Optional)
8104      */
8105     /**
8106      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111     /**
8112      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114     /**
8115      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8116      */
8117         /**
8118      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8119      */
8120     /**
8121      * Returns the id of the column at the specified index.
8122      * @param {Number} index The column index
8123      * @return {String} the id
8124      */
8125     getColumnId : function(index){
8126         return this.config[index].id;
8127     },
8128
8129     /**
8130      * Returns the column for a specified id.
8131      * @param {String} id The column id
8132      * @return {Object} the column
8133      */
8134     getColumnById : function(id){
8135         return this.lookup[id];
8136     },
8137
8138     
8139     /**
8140      * Returns the column Object for a specified dataIndex.
8141      * @param {String} dataIndex The column dataIndex
8142      * @return {Object|Boolean} the column or false if not found
8143      */
8144     getColumnByDataIndex: function(dataIndex){
8145         var index = this.findColumnIndex(dataIndex);
8146         return index > -1 ? this.config[index] : false;
8147     },
8148     
8149     /**
8150      * Returns the index for a specified column id.
8151      * @param {String} id The column id
8152      * @return {Number} the index, or -1 if not found
8153      */
8154     getIndexById : function(id){
8155         for(var i = 0, len = this.config.length; i < len; i++){
8156             if(this.config[i].id == id){
8157                 return i;
8158             }
8159         }
8160         return -1;
8161     },
8162     
8163     /**
8164      * Returns the index for a specified column dataIndex.
8165      * @param {String} dataIndex The column dataIndex
8166      * @return {Number} the index, or -1 if not found
8167      */
8168     
8169     findColumnIndex : function(dataIndex){
8170         for(var i = 0, len = this.config.length; i < len; i++){
8171             if(this.config[i].dataIndex == dataIndex){
8172                 return i;
8173             }
8174         }
8175         return -1;
8176     },
8177     
8178     
8179     moveColumn : function(oldIndex, newIndex){
8180         var c = this.config[oldIndex];
8181         this.config.splice(oldIndex, 1);
8182         this.config.splice(newIndex, 0, c);
8183         this.dataMap = null;
8184         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8185     },
8186
8187     isLocked : function(colIndex){
8188         return this.config[colIndex].locked === true;
8189     },
8190
8191     setLocked : function(colIndex, value, suppressEvent){
8192         if(this.isLocked(colIndex) == value){
8193             return;
8194         }
8195         this.config[colIndex].locked = value;
8196         if(!suppressEvent){
8197             this.fireEvent("columnlockchange", this, colIndex, value);
8198         }
8199     },
8200
8201     getTotalLockedWidth : function(){
8202         var totalWidth = 0;
8203         for(var i = 0; i < this.config.length; i++){
8204             if(this.isLocked(i) && !this.isHidden(i)){
8205                 this.totalWidth += this.getColumnWidth(i);
8206             }
8207         }
8208         return totalWidth;
8209     },
8210
8211     getLockedCount : function(){
8212         for(var i = 0, len = this.config.length; i < len; i++){
8213             if(!this.isLocked(i)){
8214                 return i;
8215             }
8216         }
8217         
8218         return this.config.length;
8219     },
8220
8221     /**
8222      * Returns the number of columns.
8223      * @return {Number}
8224      */
8225     getColumnCount : function(visibleOnly){
8226         if(visibleOnly === true){
8227             var c = 0;
8228             for(var i = 0, len = this.config.length; i < len; i++){
8229                 if(!this.isHidden(i)){
8230                     c++;
8231                 }
8232             }
8233             return c;
8234         }
8235         return this.config.length;
8236     },
8237
8238     /**
8239      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8240      * @param {Function} fn
8241      * @param {Object} scope (optional)
8242      * @return {Array} result
8243      */
8244     getColumnsBy : function(fn, scope){
8245         var r = [];
8246         for(var i = 0, len = this.config.length; i < len; i++){
8247             var c = this.config[i];
8248             if(fn.call(scope||this, c, i) === true){
8249                 r[r.length] = c;
8250             }
8251         }
8252         return r;
8253     },
8254
8255     /**
8256      * Returns true if the specified column is sortable.
8257      * @param {Number} col The column index
8258      * @return {Boolean}
8259      */
8260     isSortable : function(col){
8261         if(typeof this.config[col].sortable == "undefined"){
8262             return this.defaultSortable;
8263         }
8264         return this.config[col].sortable;
8265     },
8266
8267     /**
8268      * Returns the rendering (formatting) function defined for the column.
8269      * @param {Number} col The column index.
8270      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8271      */
8272     getRenderer : function(col){
8273         if(!this.config[col].renderer){
8274             return Roo.grid.ColumnModel.defaultRenderer;
8275         }
8276         return this.config[col].renderer;
8277     },
8278
8279     /**
8280      * Sets the rendering (formatting) function for a column.
8281      * @param {Number} col The column index
8282      * @param {Function} fn The function to use to process the cell's raw data
8283      * to return HTML markup for the grid view. The render function is called with
8284      * the following parameters:<ul>
8285      * <li>Data value.</li>
8286      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8287      * <li>css A CSS style string to apply to the table cell.</li>
8288      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8289      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8290      * <li>Row index</li>
8291      * <li>Column index</li>
8292      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8293      */
8294     setRenderer : function(col, fn){
8295         this.config[col].renderer = fn;
8296     },
8297
8298     /**
8299      * Returns the width for the specified column.
8300      * @param {Number} col The column index
8301      * @param (optional) {String} gridSize bootstrap width size.
8302      * @return {Number}
8303      */
8304     getColumnWidth : function(col, gridSize)
8305         {
8306                 var cfg = this.config[col];
8307                 
8308                 if (typeof(gridSize) == 'undefined') {
8309                         return cfg.width * 1 || this.defaultWidth;
8310                 }
8311                 if (gridSize === false) { // if we set it..
8312                         return cfg.width || false;
8313                 }
8314                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8315                 
8316                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8317                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8318                                 continue;
8319                         }
8320                         return cfg[ sizes[i] ];
8321                 }
8322                 return 1;
8323                 
8324     },
8325
8326     /**
8327      * Sets the width for a column.
8328      * @param {Number} col The column index
8329      * @param {Number} width The new width
8330      */
8331     setColumnWidth : function(col, width, suppressEvent){
8332         this.config[col].width = width;
8333         this.totalWidth = null;
8334         if(!suppressEvent){
8335              this.fireEvent("widthchange", this, col, width);
8336         }
8337     },
8338
8339     /**
8340      * Returns the total width of all columns.
8341      * @param {Boolean} includeHidden True to include hidden column widths
8342      * @return {Number}
8343      */
8344     getTotalWidth : function(includeHidden){
8345         if(!this.totalWidth){
8346             this.totalWidth = 0;
8347             for(var i = 0, len = this.config.length; i < len; i++){
8348                 if(includeHidden || !this.isHidden(i)){
8349                     this.totalWidth += this.getColumnWidth(i);
8350                 }
8351             }
8352         }
8353         return this.totalWidth;
8354     },
8355
8356     /**
8357      * Returns the header for the specified column.
8358      * @param {Number} col The column index
8359      * @return {String}
8360      */
8361     getColumnHeader : function(col){
8362         return this.config[col].header;
8363     },
8364
8365     /**
8366      * Sets the header for a column.
8367      * @param {Number} col The column index
8368      * @param {String} header The new header
8369      */
8370     setColumnHeader : function(col, header){
8371         this.config[col].header = header;
8372         this.fireEvent("headerchange", this, col, header);
8373     },
8374
8375     /**
8376      * Returns the tooltip for the specified column.
8377      * @param {Number} col The column index
8378      * @return {String}
8379      */
8380     getColumnTooltip : function(col){
8381             return this.config[col].tooltip;
8382     },
8383     /**
8384      * Sets the tooltip for a column.
8385      * @param {Number} col The column index
8386      * @param {String} tooltip The new tooltip
8387      */
8388     setColumnTooltip : function(col, tooltip){
8389             this.config[col].tooltip = tooltip;
8390     },
8391
8392     /**
8393      * Returns the dataIndex for the specified column.
8394      * @param {Number} col The column index
8395      * @return {Number}
8396      */
8397     getDataIndex : function(col){
8398         return this.config[col].dataIndex;
8399     },
8400
8401     /**
8402      * Sets the dataIndex for a column.
8403      * @param {Number} col The column index
8404      * @param {Number} dataIndex The new dataIndex
8405      */
8406     setDataIndex : function(col, dataIndex){
8407         this.config[col].dataIndex = dataIndex;
8408     },
8409
8410     
8411     
8412     /**
8413      * Returns true if the cell is editable.
8414      * @param {Number} colIndex The column index
8415      * @param {Number} rowIndex The row index - this is nto actually used..?
8416      * @return {Boolean}
8417      */
8418     isCellEditable : function(colIndex, rowIndex){
8419         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8420     },
8421
8422     /**
8423      * Returns the editor defined for the cell/column.
8424      * return false or null to disable editing.
8425      * @param {Number} colIndex The column index
8426      * @param {Number} rowIndex The row index
8427      * @return {Object}
8428      */
8429     getCellEditor : function(colIndex, rowIndex){
8430         return this.config[colIndex].editor;
8431     },
8432
8433     /**
8434      * Sets if a column is editable.
8435      * @param {Number} col The column index
8436      * @param {Boolean} editable True if the column is editable
8437      */
8438     setEditable : function(col, editable){
8439         this.config[col].editable = editable;
8440     },
8441
8442
8443     /**
8444      * Returns true if the column is hidden.
8445      * @param {Number} colIndex The column index
8446      * @return {Boolean}
8447      */
8448     isHidden : function(colIndex){
8449         return this.config[colIndex].hidden;
8450     },
8451
8452
8453     /**
8454      * Returns true if the column width cannot be changed
8455      */
8456     isFixed : function(colIndex){
8457         return this.config[colIndex].fixed;
8458     },
8459
8460     /**
8461      * Returns true if the column can be resized
8462      * @return {Boolean}
8463      */
8464     isResizable : function(colIndex){
8465         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8466     },
8467     /**
8468      * Sets if a column is hidden.
8469      * @param {Number} colIndex The column index
8470      * @param {Boolean} hidden True if the column is hidden
8471      */
8472     setHidden : function(colIndex, hidden){
8473         this.config[colIndex].hidden = hidden;
8474         this.totalWidth = null;
8475         this.fireEvent("hiddenchange", this, colIndex, hidden);
8476     },
8477
8478     /**
8479      * Sets the editor for a column.
8480      * @param {Number} col The column index
8481      * @param {Object} editor The editor object
8482      */
8483     setEditor : function(col, editor){
8484         this.config[col].editor = editor;
8485     },
8486     /**
8487      * Add a column (experimental...) - defaults to adding to the end..
8488      * @param {Object} config 
8489     */
8490     addColumn : function(c)
8491     {
8492     
8493         var i = this.config.length;
8494         this.config[i] = c;
8495         
8496         if(typeof c.dataIndex == "undefined"){
8497             c.dataIndex = i;
8498         }
8499         if(typeof c.renderer == "string"){
8500             c.renderer = Roo.util.Format[c.renderer];
8501         }
8502         if(typeof c.id == "undefined"){
8503             c.id = Roo.id();
8504         }
8505         if(c.editor && c.editor.xtype){
8506             c.editor  = Roo.factory(c.editor, Roo.grid);
8507         }
8508         if(c.editor && c.editor.isFormField){
8509             c.editor = new Roo.grid.GridEditor(c.editor);
8510         }
8511         this.lookup[c.id] = c;
8512     }
8513     
8514 });
8515
8516 Roo.grid.ColumnModel.defaultRenderer = function(value)
8517 {
8518     if(typeof value == "object") {
8519         return value;
8520     }
8521         if(typeof value == "string" && value.length < 1){
8522             return "&#160;";
8523         }
8524     
8525         return String.format("{0}", value);
8526 };
8527
8528 // Alias for backwards compatibility
8529 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8530 /*
8531  * Based on:
8532  * Ext JS Library 1.1.1
8533  * Copyright(c) 2006-2007, Ext JS, LLC.
8534  *
8535  * Originally Released Under LGPL - original licence link has changed is not relivant.
8536  *
8537  * Fork - LGPL
8538  * <script type="text/javascript">
8539  */
8540  
8541 /**
8542  * @class Roo.LoadMask
8543  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8544  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8545  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8546  * element's UpdateManager load indicator and will be destroyed after the initial load.
8547  * @constructor
8548  * Create a new LoadMask
8549  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8550  * @param {Object} config The config object
8551  */
8552 Roo.LoadMask = function(el, config){
8553     this.el = Roo.get(el);
8554     Roo.apply(this, config);
8555     if(this.store){
8556         this.store.on('beforeload', this.onBeforeLoad, this);
8557         this.store.on('load', this.onLoad, this);
8558         this.store.on('loadexception', this.onLoadException, this);
8559         this.removeMask = false;
8560     }else{
8561         var um = this.el.getUpdateManager();
8562         um.showLoadIndicator = false; // disable the default indicator
8563         um.on('beforeupdate', this.onBeforeLoad, this);
8564         um.on('update', this.onLoad, this);
8565         um.on('failure', this.onLoad, this);
8566         this.removeMask = true;
8567     }
8568 };
8569
8570 Roo.LoadMask.prototype = {
8571     /**
8572      * @cfg {Boolean} removeMask
8573      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8574      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8575      */
8576     removeMask : false,
8577     /**
8578      * @cfg {String} msg
8579      * The text to display in a centered loading message box (defaults to 'Loading...')
8580      */
8581     msg : 'Loading...',
8582     /**
8583      * @cfg {String} msgCls
8584      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8585      */
8586     msgCls : 'x-mask-loading',
8587
8588     /**
8589      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8590      * @type Boolean
8591      */
8592     disabled: false,
8593
8594     /**
8595      * Disables the mask to prevent it from being displayed
8596      */
8597     disable : function(){
8598        this.disabled = true;
8599     },
8600
8601     /**
8602      * Enables the mask so that it can be displayed
8603      */
8604     enable : function(){
8605         this.disabled = false;
8606     },
8607     
8608     onLoadException : function()
8609     {
8610         Roo.log(arguments);
8611         
8612         if (typeof(arguments[3]) != 'undefined') {
8613             Roo.MessageBox.alert("Error loading",arguments[3]);
8614         } 
8615         /*
8616         try {
8617             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8618                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8619             }   
8620         } catch(e) {
8621             
8622         }
8623         */
8624     
8625         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626     },
8627     // private
8628     onLoad : function()
8629     {
8630         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8631     },
8632
8633     // private
8634     onBeforeLoad : function(){
8635         if(!this.disabled){
8636             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8637         }
8638     },
8639
8640     // private
8641     destroy : function(){
8642         if(this.store){
8643             this.store.un('beforeload', this.onBeforeLoad, this);
8644             this.store.un('load', this.onLoad, this);
8645             this.store.un('loadexception', this.onLoadException, this);
8646         }else{
8647             var um = this.el.getUpdateManager();
8648             um.un('beforeupdate', this.onBeforeLoad, this);
8649             um.un('update', this.onLoad, this);
8650             um.un('failure', this.onLoad, this);
8651         }
8652     }
8653 };/**
8654  * @class Roo.bootstrap.Table
8655  * @licence LGBL
8656  * @extends Roo.bootstrap.Component
8657  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8658  * Similar to Roo.grid.Grid
8659  * <pre><code>
8660  var table = Roo.factory({
8661     xtype : 'Table',
8662     xns : Roo.bootstrap,
8663     autoSizeColumns: true,
8664     
8665     
8666     store : {
8667         xtype : 'Store',
8668         xns : Roo.data,
8669         remoteSort : true,
8670         sortInfo : { direction : 'ASC', field: 'name' },
8671         proxy : {
8672            xtype : 'HttpProxy',
8673            xns : Roo.data,
8674            method : 'GET',
8675            url : 'https://example.com/some.data.url.json'
8676         },
8677         reader : {
8678            xtype : 'JsonReader',
8679            xns : Roo.data,
8680            fields : [ 'id', 'name', whatever' ],
8681            id : 'id',
8682            root : 'data'
8683         }
8684     },
8685     cm : [
8686         {
8687             xtype : 'ColumnModel',
8688             xns : Roo.grid,
8689             align : 'center',
8690             cursor : 'pointer',
8691             dataIndex : 'is_in_group',
8692             header : "Name",
8693             sortable : true,
8694             renderer : function(v, x , r) {  
8695             
8696                 return String.format("{0}", v)
8697             }
8698             width : 3
8699         } // more columns..
8700     ],
8701     selModel : {
8702         xtype : 'RowSelectionModel',
8703         xns : Roo.bootstrap.Table
8704         // you can add listeners to catch selection change here....
8705     }
8706      
8707
8708  });
8709  // set any options
8710  grid.render(Roo.get("some-div"));
8711 </code></pre>
8712
8713 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8714
8715
8716
8717  *
8718  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8719  * @cfg {Roo.data.Store} store The data store to use
8720  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8721  * 
8722  * @cfg {String} cls table class
8723  *
8724  * 
8725  * @cfg {boolean} striped Should the rows be alternative striped
8726  * @cfg {boolean} bordered Add borders to the table
8727  * @cfg {boolean} hover Add hover highlighting
8728  * @cfg {boolean} condensed Format condensed
8729  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8730  *                also adds table-responsive (see bootstrap docs for details)
8731  * @cfg {Boolean} loadMask (true|false) default false
8732  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8733  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8734  * @cfg {Boolean} rowSelection (true|false) default false
8735  * @cfg {Boolean} cellSelection (true|false) default false
8736  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8737  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8738  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8739  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8740  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8741  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8742  * 
8743  * @constructor
8744  * Create a new Table
8745  * @param {Object} config The config object
8746  */
8747
8748 Roo.bootstrap.Table = function(config)
8749 {
8750     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8751      
8752     // BC...
8753     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8754     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8755     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8756     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8757     
8758     this.view = this; // compat with grid.
8759     
8760     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8761     if (this.sm) {
8762         this.sm.grid = this;
8763         this.selModel = Roo.factory(this.sm, Roo.grid);
8764         this.sm = this.selModel;
8765         this.sm.xmodule = this.xmodule || false;
8766     }
8767     
8768     if (this.cm && typeof(this.cm.config) == 'undefined') {
8769         this.colModel = new Roo.grid.ColumnModel(this.cm);
8770         this.cm = this.colModel;
8771         this.cm.xmodule = this.xmodule || false;
8772     }
8773     if (this.store) {
8774         this.store= Roo.factory(this.store, Roo.data);
8775         this.ds = this.store;
8776         this.ds.xmodule = this.xmodule || false;
8777          
8778     }
8779     if (this.footer && this.store) {
8780         this.footer.dataSource = this.ds;
8781         this.footer = Roo.factory(this.footer);
8782     }
8783     
8784     /** @private */
8785     this.addEvents({
8786         /**
8787          * @event cellclick
8788          * Fires when a cell is clicked
8789          * @param {Roo.bootstrap.Table} this
8790          * @param {Roo.Element} el
8791          * @param {Number} rowIndex
8792          * @param {Number} columnIndex
8793          * @param {Roo.EventObject} e
8794          */
8795         "cellclick" : true,
8796         /**
8797          * @event celldblclick
8798          * Fires when a cell is double clicked
8799          * @param {Roo.bootstrap.Table} this
8800          * @param {Roo.Element} el
8801          * @param {Number} rowIndex
8802          * @param {Number} columnIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "celldblclick" : true,
8806         /**
8807          * @event rowclick
8808          * Fires when a row is clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Roo.EventObject} e
8813          */
8814         "rowclick" : true,
8815         /**
8816          * @event rowdblclick
8817          * Fires when a row is double clicked
8818          * @param {Roo.bootstrap.Table} this
8819          * @param {Roo.Element} el
8820          * @param {Number} rowIndex
8821          * @param {Roo.EventObject} e
8822          */
8823         "rowdblclick" : true,
8824         /**
8825          * @event mouseover
8826          * Fires when a mouseover occur
8827          * @param {Roo.bootstrap.Table} this
8828          * @param {Roo.Element} el
8829          * @param {Number} rowIndex
8830          * @param {Number} columnIndex
8831          * @param {Roo.EventObject} e
8832          */
8833         "mouseover" : true,
8834         /**
8835          * @event mouseout
8836          * Fires when a mouseout occur
8837          * @param {Roo.bootstrap.Table} this
8838          * @param {Roo.Element} el
8839          * @param {Number} rowIndex
8840          * @param {Number} columnIndex
8841          * @param {Roo.EventObject} e
8842          */
8843         "mouseout" : true,
8844         /**
8845          * @event rowclass
8846          * Fires when a row is rendered, so you can change add a style to it.
8847          * @param {Roo.bootstrap.Table} this
8848          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8849          */
8850         'rowclass' : true,
8851           /**
8852          * @event rowsrendered
8853          * Fires when all the  rows have been rendered
8854          * @param {Roo.bootstrap.Table} this
8855          */
8856         'rowsrendered' : true,
8857         /**
8858          * @event contextmenu
8859          * The raw contextmenu event for the entire grid.
8860          * @param {Roo.EventObject} e
8861          */
8862         "contextmenu" : true,
8863         /**
8864          * @event rowcontextmenu
8865          * Fires when a row is right clicked
8866          * @param {Roo.bootstrap.Table} this
8867          * @param {Number} rowIndex
8868          * @param {Roo.EventObject} e
8869          */
8870         "rowcontextmenu" : true,
8871         /**
8872          * @event cellcontextmenu
8873          * Fires when a cell is right clicked
8874          * @param {Roo.bootstrap.Table} this
8875          * @param {Number} rowIndex
8876          * @param {Number} cellIndex
8877          * @param {Roo.EventObject} e
8878          */
8879          "cellcontextmenu" : true,
8880          /**
8881          * @event headercontextmenu
8882          * Fires when a header is right clicked
8883          * @param {Roo.bootstrap.Table} this
8884          * @param {Number} columnIndex
8885          * @param {Roo.EventObject} e
8886          */
8887         "headercontextmenu" : true,
8888         /**
8889          * @event mousedown
8890          * The raw mousedown event for the entire grid.
8891          * @param {Roo.EventObject} e
8892          */
8893         "mousedown" : true
8894         
8895     });
8896 };
8897
8898 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8899     
8900     cls: false,
8901     
8902     striped : false,
8903     scrollBody : false,
8904     bordered: false,
8905     hover:  false,
8906     condensed : false,
8907     responsive : false,
8908     sm : false,
8909     cm : false,
8910     store : false,
8911     loadMask : false,
8912     footerShow : true,
8913     headerShow : true,
8914     enableColumnResize: true,
8915   
8916     rowSelection : false,
8917     cellSelection : false,
8918     layout : false,
8919
8920     minColumnWidth : 50,
8921     
8922     // Roo.Element - the tbody
8923     bodyEl: false,  // <tbody> Roo.Element - thead element    
8924     headEl: false,  // <thead> Roo.Element - thead element
8925     resizeProxy : false, // proxy element for dragging?
8926
8927
8928     
8929     container: false, // used by gridpanel...
8930     
8931     lazyLoad : false,
8932     
8933     CSS : Roo.util.CSS,
8934     
8935     auto_hide_footer : false,
8936     
8937     view: false, // actually points to this..
8938     
8939     getAutoCreate : function()
8940     {
8941         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8942         
8943         cfg = {
8944             tag: 'table',
8945             cls : 'table', 
8946             cn : []
8947         };
8948         // this get's auto added by panel.Grid
8949         if (this.scrollBody) {
8950             cfg.cls += ' table-body-fixed';
8951         }    
8952         if (this.striped) {
8953             cfg.cls += ' table-striped';
8954         }
8955         
8956         if (this.hover) {
8957             cfg.cls += ' table-hover';
8958         }
8959         if (this.bordered) {
8960             cfg.cls += ' table-bordered';
8961         }
8962         if (this.condensed) {
8963             cfg.cls += ' table-condensed';
8964         }
8965         
8966         if (this.responsive) {
8967             cfg.cls += ' table-responsive';
8968         }
8969         
8970         if (this.cls) {
8971             cfg.cls+=  ' ' +this.cls;
8972         }
8973         
8974         
8975         
8976         if (this.layout) {
8977             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8978         }
8979         
8980         if(this.store || this.cm){
8981             if(this.headerShow){
8982                 cfg.cn.push(this.renderHeader());
8983             }
8984             
8985             cfg.cn.push(this.renderBody());
8986             
8987             if(this.footerShow){
8988                 cfg.cn.push(this.renderFooter());
8989             }
8990             // where does this come from?
8991             //cfg.cls+=  ' TableGrid';
8992         }
8993         
8994         return { cn : [ cfg ] };
8995     },
8996     
8997     initEvents : function()
8998     {   
8999         if(!this.store || !this.cm){
9000             return;
9001         }
9002         if (this.selModel) {
9003             this.selModel.initEvents();
9004         }
9005         
9006         
9007         //Roo.log('initEvents with ds!!!!');
9008         
9009         this.bodyEl = this.el.select('tbody', true).first();
9010         this.headEl = this.el.select('thead', true).first();
9011         this.mainFoot = this.el.select('tfoot', true).first();
9012         
9013         
9014         
9015         
9016         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9017             e.on('click', this.sort, this);
9018         }, this);
9019         
9020         
9021         // why is this done????? = it breaks dialogs??
9022         //this.parent().el.setStyle('position', 'relative');
9023         
9024         
9025         if (this.footer) {
9026             this.footer.parentId = this.id;
9027             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9028             
9029             if(this.lazyLoad){
9030                 this.el.select('tfoot tr td').first().addClass('hide');
9031             }
9032         } 
9033         
9034         if(this.loadMask) {
9035             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9036         }
9037         
9038         this.store.on('load', this.onLoad, this);
9039         this.store.on('beforeload', this.onBeforeLoad, this);
9040         this.store.on('update', this.onUpdate, this);
9041         this.store.on('add', this.onAdd, this);
9042         this.store.on("clear", this.clear, this);
9043         
9044         this.el.on("contextmenu", this.onContextMenu, this);
9045         
9046         
9047         this.cm.on("headerchange", this.onHeaderChange, this);
9048         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9049
9050  //?? does bodyEl get replaced on render?
9051         this.bodyEl.on("click", this.onClick, this);
9052         this.bodyEl.on("dblclick", this.onDblClick, this);        
9053         this.bodyEl.on('scroll', this.onBodyScroll, this);
9054
9055         // guessing mainbody will work - this relays usually caught by selmodel at present.
9056         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9057   
9058   
9059         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9060         
9061   
9062         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9063             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9064         }
9065         
9066         this.initCSS();
9067     },
9068     // Compatibility with grid - we implement all the view features at present.
9069     getView : function()
9070     {
9071         return this;
9072     },
9073     
9074     initCSS : function()
9075     {
9076         
9077         
9078         var cm = this.cm, styles = [];
9079         this.CSS.removeStyleSheet(this.id + '-cssrules');
9080         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9081         // we can honour xs/sm/md/xl  as widths...
9082         // we first have to decide what widht we are currently at...
9083         var sz = Roo.getGridSize();
9084         
9085         var total = 0;
9086         var last = -1;
9087         var cols = []; // visable cols.
9088         var total_abs = 0;
9089         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9090             var w = cm.getColumnWidth(i, false);
9091             if(cm.isHidden(i)){
9092                 cols.push( { rel : false, abs : 0 });
9093                 continue;
9094             }
9095             if (w !== false) {
9096                 cols.push( { rel : false, abs : w });
9097                 total_abs += w;
9098                 last = i; // not really..
9099                 continue;
9100             }
9101             var w = cm.getColumnWidth(i, sz);
9102             if (w > 0) {
9103                 last = i
9104             }
9105             total += w;
9106             cols.push( { rel : w, abs : false });
9107         }
9108         
9109         var avail = this.bodyEl.dom.clientWidth - total_abs;
9110         
9111         var unitWidth = Math.floor(avail / total);
9112         var rem = avail - (unitWidth * total);
9113         
9114         var hidden, width, pos = 0 , splithide , left;
9115         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9116             
9117             hidden = 'display:none;';
9118             left = '';
9119             width  = 'width:0px;';
9120             splithide = '';
9121             if(!cm.isHidden(i)){
9122                 hidden = '';
9123                 
9124                 
9125                 // we can honour xs/sm/md/xl ?
9126                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9127                 if (w===0) {
9128                     hidden = 'display:none;';
9129                 }
9130                 // width should return a small number...
9131                 if (i == last) {
9132                     w+=rem; // add the remaining with..
9133                 }
9134                 pos += w;
9135                 left = "left:" + (pos -4) + "px;";
9136                 width = "width:" + w+ "px;";
9137                 
9138             }
9139             if (this.responsive) {
9140                 width = '';
9141                 left = '';
9142                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9143                 splithide = 'display: none;';
9144             }
9145             
9146             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9147             if (this.headEl) {
9148                 if (i == last) {
9149                     splithide = 'display:none;';
9150                 }
9151                 
9152                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9153                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9154                 );
9155             }
9156             
9157         }
9158         //Roo.log(styles.join(''));
9159         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9160         
9161     },
9162     
9163     
9164     
9165     onContextMenu : function(e, t)
9166     {
9167         this.processEvent("contextmenu", e);
9168     },
9169     
9170     processEvent : function(name, e)
9171     {
9172         if (name != 'touchstart' ) {
9173             this.fireEvent(name, e);    
9174         }
9175         
9176         var t = e.getTarget();
9177         
9178         var cell = Roo.get(t);
9179         
9180         if(!cell){
9181             return;
9182         }
9183         
9184         if(cell.findParent('tfoot', false, true)){
9185             return;
9186         }
9187         
9188         if(cell.findParent('thead', false, true)){
9189             
9190             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9191                 cell = Roo.get(t).findParent('th', false, true);
9192                 if (!cell) {
9193                     Roo.log("failed to find th in thead?");
9194                     Roo.log(e.getTarget());
9195                     return;
9196                 }
9197             }
9198             
9199             var cellIndex = cell.dom.cellIndex;
9200             
9201             var ename = name == 'touchstart' ? 'click' : name;
9202             this.fireEvent("header" + ename, this, cellIndex, e);
9203             
9204             return;
9205         }
9206         
9207         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9208             cell = Roo.get(t).findParent('td', false, true);
9209             if (!cell) {
9210                 Roo.log("failed to find th in tbody?");
9211                 Roo.log(e.getTarget());
9212                 return;
9213             }
9214         }
9215         
9216         var row = cell.findParent('tr', false, true);
9217         var cellIndex = cell.dom.cellIndex;
9218         var rowIndex = row.dom.rowIndex - 1;
9219         
9220         if(row !== false){
9221             
9222             this.fireEvent("row" + name, this, rowIndex, e);
9223             
9224             if(cell !== false){
9225             
9226                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9227             }
9228         }
9229         
9230     },
9231     
9232     onMouseover : function(e, el)
9233     {
9234         var cell = Roo.get(el);
9235         
9236         if(!cell){
9237             return;
9238         }
9239         
9240         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9241             cell = cell.findParent('td', false, true);
9242         }
9243         
9244         var row = cell.findParent('tr', false, true);
9245         var cellIndex = cell.dom.cellIndex;
9246         var rowIndex = row.dom.rowIndex - 1; // start from 0
9247         
9248         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9249         
9250     },
9251     
9252     onMouseout : function(e, el)
9253     {
9254         var cell = Roo.get(el);
9255         
9256         if(!cell){
9257             return;
9258         }
9259         
9260         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9261             cell = cell.findParent('td', false, true);
9262         }
9263         
9264         var row = cell.findParent('tr', false, true);
9265         var cellIndex = cell.dom.cellIndex;
9266         var rowIndex = row.dom.rowIndex - 1; // start from 0
9267         
9268         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9269         
9270     },
9271     
9272     onClick : function(e, el)
9273     {
9274         var cell = Roo.get(el);
9275         
9276         if(!cell || (!this.cellSelection && !this.rowSelection)){
9277             return;
9278         }
9279         
9280         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9281             cell = cell.findParent('td', false, true);
9282         }
9283         
9284         if(!cell || typeof(cell) == 'undefined'){
9285             return;
9286         }
9287         
9288         var row = cell.findParent('tr', false, true);
9289         
9290         if(!row || typeof(row) == 'undefined'){
9291             return;
9292         }
9293         
9294         var cellIndex = cell.dom.cellIndex;
9295         var rowIndex = this.getRowIndex(row);
9296         
9297         // why??? - should these not be based on SelectionModel?
9298         //if(this.cellSelection){
9299             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9300         //}
9301         
9302         //if(this.rowSelection){
9303             this.fireEvent('rowclick', this, row, rowIndex, e);
9304         //}
9305          
9306     },
9307         
9308     onDblClick : function(e,el)
9309     {
9310         var cell = Roo.get(el);
9311         
9312         if(!cell || (!this.cellSelection && !this.rowSelection)){
9313             return;
9314         }
9315         
9316         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9317             cell = cell.findParent('td', false, true);
9318         }
9319         
9320         if(!cell || typeof(cell) == 'undefined'){
9321             return;
9322         }
9323         
9324         var row = cell.findParent('tr', false, true);
9325         
9326         if(!row || typeof(row) == 'undefined'){
9327             return;
9328         }
9329         
9330         var cellIndex = cell.dom.cellIndex;
9331         var rowIndex = this.getRowIndex(row);
9332         
9333         if(this.cellSelection){
9334             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9335         }
9336         
9337         if(this.rowSelection){
9338             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9339         }
9340     },
9341     findRowIndex : function(el)
9342     {
9343         var cell = Roo.get(el);
9344         if(!cell) {
9345             return false;
9346         }
9347         var row = cell.findParent('tr', false, true);
9348         
9349         if(!row || typeof(row) == 'undefined'){
9350             return false;
9351         }
9352         return this.getRowIndex(row);
9353     },
9354     sort : function(e,el)
9355     {
9356         var col = Roo.get(el);
9357         
9358         if(!col.hasClass('sortable')){
9359             return;
9360         }
9361         
9362         var sort = col.attr('sort');
9363         var dir = 'ASC';
9364         
9365         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9366             dir = 'DESC';
9367         }
9368         
9369         this.store.sortInfo = {field : sort, direction : dir};
9370         
9371         if (this.footer) {
9372             Roo.log("calling footer first");
9373             this.footer.onClick('first');
9374         } else {
9375         
9376             this.store.load({ params : { start : 0 } });
9377         }
9378     },
9379     
9380     renderHeader : function()
9381     {
9382         var header = {
9383             tag: 'thead',
9384             cn : []
9385         };
9386         
9387         var cm = this.cm;
9388         this.totalWidth = 0;
9389         
9390         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9391             
9392             var config = cm.config[i];
9393             
9394             var c = {
9395                 tag: 'th',
9396                 cls : 'x-hcol-' + i,
9397                 style : '',
9398                 
9399                 html: cm.getColumnHeader(i)
9400             };
9401             
9402             var tooltip = cm.getColumnTooltip(i);
9403             if (tooltip) {
9404                 c.tooltip = tooltip;
9405             }
9406             
9407             
9408             var hh = '';
9409             
9410             if(typeof(config.sortable) != 'undefined' && config.sortable){
9411                 c.cls += ' sortable';
9412                 c.html = '<i class="fa"></i>' + c.html;
9413             }
9414             
9415             // could use BS4 hidden-..-down 
9416             
9417             if(typeof(config.lgHeader) != 'undefined'){
9418                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9419             }
9420             
9421             if(typeof(config.mdHeader) != 'undefined'){
9422                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9423             }
9424             
9425             if(typeof(config.smHeader) != 'undefined'){
9426                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9427             }
9428             
9429             if(typeof(config.xsHeader) != 'undefined'){
9430                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9431             }
9432             
9433             if(hh.length){
9434                 c.html = hh;
9435             }
9436             
9437             if(typeof(config.tooltip) != 'undefined'){
9438                 c.tooltip = config.tooltip;
9439             }
9440             
9441             if(typeof(config.colspan) != 'undefined'){
9442                 c.colspan = config.colspan;
9443             }
9444             
9445             // hidden is handled by CSS now
9446             
9447             if(typeof(config.dataIndex) != 'undefined'){
9448                 c.sort = config.dataIndex;
9449             }
9450             
9451            
9452             
9453             if(typeof(config.align) != 'undefined' && config.align.length){
9454                 c.style += ' text-align:' + config.align + ';';
9455             }
9456             
9457             /* width is done in CSS
9458              *if(typeof(config.width) != 'undefined'){
9459                 c.style += ' width:' + config.width + 'px;';
9460                 this.totalWidth += config.width;
9461             } else {
9462                 this.totalWidth += 100; // assume minimum of 100 per column?
9463             }
9464             */
9465             
9466             if(typeof(config.cls) != 'undefined'){
9467                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9468             }
9469             // this is the bit that doesnt reall work at all...
9470             
9471             if (this.responsive) {
9472                  
9473             
9474                 ['xs','sm','md','lg'].map(function(size){
9475                     
9476                     if(typeof(config[size]) == 'undefined'){
9477                         return;
9478                     }
9479                      
9480                     if (!config[size]) { // 0 = hidden
9481                         // BS 4 '0' is treated as hide that column and below.
9482                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9483                         return;
9484                     }
9485                     
9486                     c.cls += ' col-' + size + '-' + config[size] + (
9487                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9488                     );
9489                     
9490                     
9491                 });
9492             }
9493             // at the end?
9494             
9495             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9496             
9497             
9498             
9499             
9500             header.cn.push(c)
9501         }
9502         
9503         return header;
9504     },
9505     
9506     renderBody : function()
9507     {
9508         var body = {
9509             tag: 'tbody',
9510             cn : [
9511                 {
9512                     tag: 'tr',
9513                     cn : [
9514                         {
9515                             tag : 'td',
9516                             colspan :  this.cm.getColumnCount()
9517                         }
9518                     ]
9519                 }
9520             ]
9521         };
9522         
9523         return body;
9524     },
9525     
9526     renderFooter : function()
9527     {
9528         var footer = {
9529             tag: 'tfoot',
9530             cn : [
9531                 {
9532                     tag: 'tr',
9533                     cn : [
9534                         {
9535                             tag : 'td',
9536                             colspan :  this.cm.getColumnCount()
9537                         }
9538                     ]
9539                 }
9540             ]
9541         };
9542         
9543         return footer;
9544     },
9545     
9546     
9547     
9548     onLoad : function()
9549     {
9550 //        Roo.log('ds onload');
9551         this.clear();
9552         
9553         var _this = this;
9554         var cm = this.cm;
9555         var ds = this.store;
9556         
9557         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9558             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9559             if (_this.store.sortInfo) {
9560                     
9561                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9562                     e.select('i', true).addClass(['fa-arrow-up']);
9563                 }
9564                 
9565                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9566                     e.select('i', true).addClass(['fa-arrow-down']);
9567                 }
9568             }
9569         });
9570         
9571         var tbody =  this.bodyEl;
9572               
9573         if(ds.getCount() > 0){
9574             ds.data.each(function(d,rowIndex){
9575                 var row =  this.renderRow(cm, ds, rowIndex);
9576                 
9577                 tbody.createChild(row);
9578                 
9579                 var _this = this;
9580                 
9581                 if(row.cellObjects.length){
9582                     Roo.each(row.cellObjects, function(r){
9583                         _this.renderCellObject(r);
9584                     })
9585                 }
9586                 
9587             }, this);
9588         }
9589         
9590         var tfoot = this.el.select('tfoot', true).first();
9591         
9592         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9593             
9594             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9595             
9596             var total = this.ds.getTotalCount();
9597             
9598             if(this.footer.pageSize < total){
9599                 this.mainFoot.show();
9600             }
9601         }
9602         
9603         Roo.each(this.el.select('tbody td', true).elements, function(e){
9604             e.on('mouseover', _this.onMouseover, _this);
9605         });
9606         
9607         Roo.each(this.el.select('tbody td', true).elements, function(e){
9608             e.on('mouseout', _this.onMouseout, _this);
9609         });
9610         this.fireEvent('rowsrendered', this);
9611         
9612         this.autoSize();
9613         
9614         this.initCSS(); /// resize cols
9615
9616         
9617     },
9618     
9619     
9620     onUpdate : function(ds,record)
9621     {
9622         this.refreshRow(record);
9623         this.autoSize();
9624     },
9625     
9626     onRemove : function(ds, record, index, isUpdate){
9627         if(isUpdate !== true){
9628             this.fireEvent("beforerowremoved", this, index, record);
9629         }
9630         var bt = this.bodyEl.dom;
9631         
9632         var rows = this.el.select('tbody > tr', true).elements;
9633         
9634         if(typeof(rows[index]) != 'undefined'){
9635             bt.removeChild(rows[index].dom);
9636         }
9637         
9638 //        if(bt.rows[index]){
9639 //            bt.removeChild(bt.rows[index]);
9640 //        }
9641         
9642         if(isUpdate !== true){
9643             //this.stripeRows(index);
9644             //this.syncRowHeights(index, index);
9645             //this.layout();
9646             this.fireEvent("rowremoved", this, index, record);
9647         }
9648     },
9649     
9650     onAdd : function(ds, records, rowIndex)
9651     {
9652         //Roo.log('on Add called');
9653         // - note this does not handle multiple adding very well..
9654         var bt = this.bodyEl.dom;
9655         for (var i =0 ; i < records.length;i++) {
9656             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9657             //Roo.log(records[i]);
9658             //Roo.log(this.store.getAt(rowIndex+i));
9659             this.insertRow(this.store, rowIndex + i, false);
9660             return;
9661         }
9662         
9663     },
9664     
9665     
9666     refreshRow : function(record){
9667         var ds = this.store, index;
9668         if(typeof record == 'number'){
9669             index = record;
9670             record = ds.getAt(index);
9671         }else{
9672             index = ds.indexOf(record);
9673             if (index < 0) {
9674                 return; // should not happen - but seems to 
9675             }
9676         }
9677         this.insertRow(ds, index, true);
9678         this.autoSize();
9679         this.onRemove(ds, record, index+1, true);
9680         this.autoSize();
9681         //this.syncRowHeights(index, index);
9682         //this.layout();
9683         this.fireEvent("rowupdated", this, index, record);
9684     },
9685     // private - called by RowSelection
9686     onRowSelect : function(rowIndex){
9687         var row = this.getRowDom(rowIndex);
9688         row.addClass(['bg-info','info']);
9689     },
9690     // private - called by RowSelection
9691     onRowDeselect : function(rowIndex)
9692     {
9693         if (rowIndex < 0) {
9694             return;
9695         }
9696         var row = this.getRowDom(rowIndex);
9697         row.removeClass(['bg-info','info']);
9698     },
9699       /**
9700      * Focuses the specified row.
9701      * @param {Number} row The row index
9702      */
9703     focusRow : function(row)
9704     {
9705         //Roo.log('GridView.focusRow');
9706         var x = this.bodyEl.dom.scrollLeft;
9707         this.focusCell(row, 0, false);
9708         this.bodyEl.dom.scrollLeft = x;
9709
9710     },
9711      /**
9712      * Focuses the specified cell.
9713      * @param {Number} row The row index
9714      * @param {Number} col The column index
9715      * @param {Boolean} hscroll false to disable horizontal scrolling
9716      */
9717     focusCell : function(row, col, hscroll)
9718     {
9719         //Roo.log('GridView.focusCell');
9720         var el = this.ensureVisible(row, col, hscroll);
9721         // not sure what focusEL achives = it's a <a> pos relative 
9722         //this.focusEl.alignTo(el, "tl-tl");
9723         //if(Roo.isGecko){
9724         //    this.focusEl.focus();
9725         //}else{
9726         //    this.focusEl.focus.defer(1, this.focusEl);
9727         //}
9728     },
9729     
9730      /**
9731      * Scrolls the specified cell into view
9732      * @param {Number} row The row index
9733      * @param {Number} col The column index
9734      * @param {Boolean} hscroll false to disable horizontal scrolling
9735      */
9736     ensureVisible : function(row, col, hscroll)
9737     {
9738         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9739         //return null; //disable for testing.
9740         if(typeof row != "number"){
9741             row = row.rowIndex;
9742         }
9743         if(row < 0 && row >= this.ds.getCount()){
9744             return  null;
9745         }
9746         col = (col !== undefined ? col : 0);
9747         var cm = this.cm;
9748         while(cm.isHidden(col)){
9749             col++;
9750         }
9751
9752         var el = this.getCellDom(row, col);
9753         if(!el){
9754             return null;
9755         }
9756         var c = this.bodyEl.dom;
9757
9758         var ctop = parseInt(el.offsetTop, 10);
9759         var cleft = parseInt(el.offsetLeft, 10);
9760         var cbot = ctop + el.offsetHeight;
9761         var cright = cleft + el.offsetWidth;
9762
9763         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9764         var ch = 0; //?? header is not withing the area?
9765         var stop = parseInt(c.scrollTop, 10);
9766         var sleft = parseInt(c.scrollLeft, 10);
9767         var sbot = stop + ch;
9768         var sright = sleft + c.clientWidth;
9769         /*
9770         Roo.log('GridView.ensureVisible:' +
9771                 ' ctop:' + ctop +
9772                 ' c.clientHeight:' + c.clientHeight +
9773                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9774                 ' stop:' + stop +
9775                 ' cbot:' + cbot +
9776                 ' sbot:' + sbot +
9777                 ' ch:' + ch  
9778                 );
9779         */
9780         if(ctop < stop){
9781             c.scrollTop = ctop;
9782             //Roo.log("set scrolltop to ctop DISABLE?");
9783         }else if(cbot > sbot){
9784             //Roo.log("set scrolltop to cbot-ch");
9785             c.scrollTop = cbot-ch;
9786         }
9787
9788         if(hscroll !== false){
9789             if(cleft < sleft){
9790                 c.scrollLeft = cleft;
9791             }else if(cright > sright){
9792                 c.scrollLeft = cright-c.clientWidth;
9793             }
9794         }
9795
9796         return el;
9797     },
9798     
9799     
9800     insertRow : function(dm, rowIndex, isUpdate){
9801         
9802         if(!isUpdate){
9803             this.fireEvent("beforerowsinserted", this, rowIndex);
9804         }
9805             //var s = this.getScrollState();
9806         var row = this.renderRow(this.cm, this.store, rowIndex);
9807         // insert before rowIndex..
9808         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9809         
9810         var _this = this;
9811                 
9812         if(row.cellObjects.length){
9813             Roo.each(row.cellObjects, function(r){
9814                 _this.renderCellObject(r);
9815             })
9816         }
9817             
9818         if(!isUpdate){
9819             this.fireEvent("rowsinserted", this, rowIndex);
9820             //this.syncRowHeights(firstRow, lastRow);
9821             //this.stripeRows(firstRow);
9822             //this.layout();
9823         }
9824         
9825     },
9826     
9827     
9828     getRowDom : function(rowIndex)
9829     {
9830         var rows = this.el.select('tbody > tr', true).elements;
9831         
9832         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9833         
9834     },
9835     getCellDom : function(rowIndex, colIndex)
9836     {
9837         var row = this.getRowDom(rowIndex);
9838         if (row === false) {
9839             return false;
9840         }
9841         var cols = row.select('td', true).elements;
9842         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9843         
9844     },
9845     
9846     // returns the object tree for a tr..
9847   
9848     
9849     renderRow : function(cm, ds, rowIndex) 
9850     {
9851         var d = ds.getAt(rowIndex);
9852         
9853         var row = {
9854             tag : 'tr',
9855             cls : 'x-row-' + rowIndex,
9856             cn : []
9857         };
9858             
9859         var cellObjects = [];
9860         
9861         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9862             var config = cm.config[i];
9863             
9864             var renderer = cm.getRenderer(i);
9865             var value = '';
9866             var id = false;
9867             
9868             if(typeof(renderer) !== 'undefined'){
9869                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9870             }
9871             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9872             // and are rendered into the cells after the row is rendered - using the id for the element.
9873             
9874             if(typeof(value) === 'object'){
9875                 id = Roo.id();
9876                 cellObjects.push({
9877                     container : id,
9878                     cfg : value 
9879                 })
9880             }
9881             
9882             var rowcfg = {
9883                 record: d,
9884                 rowIndex : rowIndex,
9885                 colIndex : i,
9886                 rowClass : ''
9887             };
9888
9889             this.fireEvent('rowclass', this, rowcfg);
9890             
9891             var td = {
9892                 tag: 'td',
9893                 // this might end up displaying HTML?
9894                 // this is too messy... - better to only do it on columsn you know are going to be too long
9895                 //tooltip : (typeof(value) === 'object') ? '' : value,
9896                 cls : rowcfg.rowClass + ' x-col-' + i,
9897                 style: '',
9898                 html: (typeof(value) === 'object') ? '' : value
9899             };
9900             
9901             if (id) {
9902                 td.id = id;
9903             }
9904             
9905             if(typeof(config.colspan) != 'undefined'){
9906                 td.colspan = config.colspan;
9907             }
9908             
9909             
9910             
9911             if(typeof(config.align) != 'undefined' && config.align.length){
9912                 td.style += ' text-align:' + config.align + ';';
9913             }
9914             if(typeof(config.valign) != 'undefined' && config.valign.length){
9915                 td.style += ' vertical-align:' + config.valign + ';';
9916             }
9917             /*
9918             if(typeof(config.width) != 'undefined'){
9919                 td.style += ' width:' +  config.width + 'px;';
9920             }
9921             */
9922             
9923             if(typeof(config.cursor) != 'undefined'){
9924                 td.style += ' cursor:' +  config.cursor + ';';
9925             }
9926             
9927             if(typeof(config.cls) != 'undefined'){
9928                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9929             }
9930             if (this.responsive) {
9931                 ['xs','sm','md','lg'].map(function(size){
9932                     
9933                     if(typeof(config[size]) == 'undefined'){
9934                         return;
9935                     }
9936                     
9937                     
9938                       
9939                     if (!config[size]) { // 0 = hidden
9940                         // BS 4 '0' is treated as hide that column and below.
9941                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9942                         return;
9943                     }
9944                     
9945                     td.cls += ' col-' + size + '-' + config[size] + (
9946                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9947                     );
9948                      
9949     
9950                 });
9951             }
9952             row.cn.push(td);
9953            
9954         }
9955         
9956         row.cellObjects = cellObjects;
9957         
9958         return row;
9959           
9960     },
9961     
9962     
9963     
9964     onBeforeLoad : function()
9965     {
9966         
9967     },
9968      /**
9969      * Remove all rows
9970      */
9971     clear : function()
9972     {
9973         this.el.select('tbody', true).first().dom.innerHTML = '';
9974     },
9975     /**
9976      * Show or hide a row.
9977      * @param {Number} rowIndex to show or hide
9978      * @param {Boolean} state hide
9979      */
9980     setRowVisibility : function(rowIndex, state)
9981     {
9982         var bt = this.bodyEl.dom;
9983         
9984         var rows = this.el.select('tbody > tr', true).elements;
9985         
9986         if(typeof(rows[rowIndex]) == 'undefined'){
9987             return;
9988         }
9989         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9990         
9991     },
9992     
9993     
9994     getSelectionModel : function(){
9995         if(!this.selModel){
9996             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9997         }
9998         return this.selModel;
9999     },
10000     /*
10001      * Render the Roo.bootstrap object from renderder
10002      */
10003     renderCellObject : function(r)
10004     {
10005         var _this = this;
10006         
10007         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10008         
10009         var t = r.cfg.render(r.container);
10010         
10011         if(r.cfg.cn){
10012             Roo.each(r.cfg.cn, function(c){
10013                 var child = {
10014                     container: t.getChildContainer(),
10015                     cfg: c
10016                 };
10017                 _this.renderCellObject(child);
10018             })
10019         }
10020     },
10021     /**
10022      * get the Row Index from a dom element.
10023      * @param {Roo.Element} row The row to look for
10024      * @returns {Number} the row
10025      */
10026     getRowIndex : function(row)
10027     {
10028         var rowIndex = -1;
10029         
10030         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10031             if(el != row){
10032                 return;
10033             }
10034             
10035             rowIndex = index;
10036         });
10037         
10038         return rowIndex;
10039     },
10040     /**
10041      * get the header TH element for columnIndex
10042      * @param {Number} columnIndex
10043      * @returns {Roo.Element}
10044      */
10045     getHeaderIndex: function(colIndex)
10046     {
10047         var cols = this.headEl.select('th', true).elements;
10048         return cols[colIndex]; 
10049     },
10050     /**
10051      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10052      * @param {domElement} cell to look for
10053      * @returns {Number} the column
10054      */
10055     getCellIndex : function(cell)
10056     {
10057         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10058         if(id){
10059             return parseInt(id[1], 10);
10060         }
10061         return 0;
10062     },
10063      /**
10064      * Returns the grid's underlying element = used by panel.Grid
10065      * @return {Element} The element
10066      */
10067     getGridEl : function(){
10068         return this.el;
10069     },
10070      /**
10071      * Forces a resize - used by panel.Grid
10072      * @return {Element} The element
10073      */
10074     autoSize : function()
10075     {
10076         //var ctr = Roo.get(this.container.dom.parentElement);
10077         var ctr = Roo.get(this.el.dom);
10078         
10079         var thd = this.getGridEl().select('thead',true).first();
10080         var tbd = this.getGridEl().select('tbody', true).first();
10081         var tfd = this.getGridEl().select('tfoot', true).first();
10082         
10083         var cw = ctr.getWidth();
10084         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10085         
10086         if (tbd) {
10087             
10088             tbd.setWidth(ctr.getWidth());
10089             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10090             // this needs fixing for various usage - currently only hydra job advers I think..
10091             //tdb.setHeight(
10092             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10093             //); 
10094             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10095             cw -= barsize;
10096         }
10097         cw = Math.max(cw, this.totalWidth);
10098         this.getGridEl().select('tbody tr',true).setWidth(cw);
10099         this.initCSS();
10100         
10101         // resize 'expandable coloumn?
10102         
10103         return; // we doe not have a view in this design..
10104         
10105     },
10106     onBodyScroll: function()
10107     {
10108         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10109         if(this.headEl){
10110             this.headEl.setStyle({
10111                 'position' : 'relative',
10112                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10113             });
10114         }
10115         
10116         if(this.lazyLoad){
10117             
10118             var scrollHeight = this.bodyEl.dom.scrollHeight;
10119             
10120             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10121             
10122             var height = this.bodyEl.getHeight();
10123             
10124             if(scrollHeight - height == scrollTop) {
10125                 
10126                 var total = this.ds.getTotalCount();
10127                 
10128                 if(this.footer.cursor + this.footer.pageSize < total){
10129                     
10130                     this.footer.ds.load({
10131                         params : {
10132                             start : this.footer.cursor + this.footer.pageSize,
10133                             limit : this.footer.pageSize
10134                         },
10135                         add : true
10136                     });
10137                 }
10138             }
10139             
10140         }
10141     },
10142     onColumnSplitterMoved : function(i, diff)
10143     {
10144         this.userResized = true;
10145         
10146         var cm = this.colModel;
10147         
10148         var w = this.getHeaderIndex(i).getWidth() + diff;
10149         
10150         
10151         cm.setColumnWidth(i, w, true);
10152         this.initCSS();
10153         //var cid = cm.getColumnId(i); << not used in this version?
10154        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10155         
10156         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10157         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10158         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10159 */
10160         //this.updateSplitters();
10161         //this.layout(); << ??
10162         this.fireEvent("columnresize", i, w);
10163     },
10164     onHeaderChange : function()
10165     {
10166         var header = this.renderHeader();
10167         var table = this.el.select('table', true).first();
10168         
10169         this.headEl.remove();
10170         this.headEl = table.createChild(header, this.bodyEl, false);
10171         
10172         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10173             e.on('click', this.sort, this);
10174         }, this);
10175         
10176         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10177             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10178         }
10179         
10180     },
10181     
10182     onHiddenChange : function(colModel, colIndex, hidden)
10183     {
10184         /*
10185         this.cm.setHidden()
10186         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10187         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10188         
10189         this.CSS.updateRule(thSelector, "display", "");
10190         this.CSS.updateRule(tdSelector, "display", "");
10191         
10192         if(hidden){
10193             this.CSS.updateRule(thSelector, "display", "none");
10194             this.CSS.updateRule(tdSelector, "display", "none");
10195         }
10196         */
10197         // onload calls initCSS()
10198         this.onHeaderChange();
10199         this.onLoad();
10200     },
10201     
10202     setColumnWidth: function(col_index, width)
10203     {
10204         // width = "md-2 xs-2..."
10205         if(!this.colModel.config[col_index]) {
10206             return;
10207         }
10208         
10209         var w = width.split(" ");
10210         
10211         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10212         
10213         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10214         
10215         
10216         for(var j = 0; j < w.length; j++) {
10217             
10218             if(!w[j]) {
10219                 continue;
10220             }
10221             
10222             var size_cls = w[j].split("-");
10223             
10224             if(!Number.isInteger(size_cls[1] * 1)) {
10225                 continue;
10226             }
10227             
10228             if(!this.colModel.config[col_index][size_cls[0]]) {
10229                 continue;
10230             }
10231             
10232             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10233                 continue;
10234             }
10235             
10236             h_row[0].classList.replace(
10237                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10238                 "col-"+size_cls[0]+"-"+size_cls[1]
10239             );
10240             
10241             for(var i = 0; i < rows.length; i++) {
10242                 
10243                 var size_cls = w[j].split("-");
10244                 
10245                 if(!Number.isInteger(size_cls[1] * 1)) {
10246                     continue;
10247                 }
10248                 
10249                 if(!this.colModel.config[col_index][size_cls[0]]) {
10250                     continue;
10251                 }
10252                 
10253                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10254                     continue;
10255                 }
10256                 
10257                 rows[i].classList.replace(
10258                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10259                     "col-"+size_cls[0]+"-"+size_cls[1]
10260                 );
10261             }
10262             
10263             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10264         }
10265     }
10266 });
10267
10268 // currently only used to find the split on drag.. 
10269 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10270
10271 /**
10272  * @depricated
10273 */
10274 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10275 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10276 /*
10277  * - LGPL
10278  *
10279  * table cell
10280  * 
10281  */
10282
10283 /**
10284  * @class Roo.bootstrap.TableCell
10285  * @extends Roo.bootstrap.Component
10286  * Bootstrap TableCell class
10287  * @cfg {String} html cell contain text
10288  * @cfg {String} cls cell class
10289  * @cfg {String} tag cell tag (td|th) default td
10290  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10291  * @cfg {String} align Aligns the content in a cell
10292  * @cfg {String} axis Categorizes cells
10293  * @cfg {String} bgcolor Specifies the background color of a cell
10294  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10295  * @cfg {Number} colspan Specifies the number of columns a cell should span
10296  * @cfg {String} headers Specifies one or more header cells a cell is related to
10297  * @cfg {Number} height Sets the height of a cell
10298  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10299  * @cfg {Number} rowspan Sets the number of rows a cell should span
10300  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10301  * @cfg {String} valign Vertical aligns the content in a cell
10302  * @cfg {Number} width Specifies the width of a cell
10303  * 
10304  * @constructor
10305  * Create a new TableCell
10306  * @param {Object} config The config object
10307  */
10308
10309 Roo.bootstrap.TableCell = function(config){
10310     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10311 };
10312
10313 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10314     
10315     html: false,
10316     cls: false,
10317     tag: false,
10318     abbr: false,
10319     align: false,
10320     axis: false,
10321     bgcolor: false,
10322     charoff: false,
10323     colspan: false,
10324     headers: false,
10325     height: false,
10326     nowrap: false,
10327     rowspan: false,
10328     scope: false,
10329     valign: false,
10330     width: false,
10331     
10332     
10333     getAutoCreate : function(){
10334         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10335         
10336         cfg = {
10337             tag: 'td'
10338         };
10339         
10340         if(this.tag){
10341             cfg.tag = this.tag;
10342         }
10343         
10344         if (this.html) {
10345             cfg.html=this.html
10346         }
10347         if (this.cls) {
10348             cfg.cls=this.cls
10349         }
10350         if (this.abbr) {
10351             cfg.abbr=this.abbr
10352         }
10353         if (this.align) {
10354             cfg.align=this.align
10355         }
10356         if (this.axis) {
10357             cfg.axis=this.axis
10358         }
10359         if (this.bgcolor) {
10360             cfg.bgcolor=this.bgcolor
10361         }
10362         if (this.charoff) {
10363             cfg.charoff=this.charoff
10364         }
10365         if (this.colspan) {
10366             cfg.colspan=this.colspan
10367         }
10368         if (this.headers) {
10369             cfg.headers=this.headers
10370         }
10371         if (this.height) {
10372             cfg.height=this.height
10373         }
10374         if (this.nowrap) {
10375             cfg.nowrap=this.nowrap
10376         }
10377         if (this.rowspan) {
10378             cfg.rowspan=this.rowspan
10379         }
10380         if (this.scope) {
10381             cfg.scope=this.scope
10382         }
10383         if (this.valign) {
10384             cfg.valign=this.valign
10385         }
10386         if (this.width) {
10387             cfg.width=this.width
10388         }
10389         
10390         
10391         return cfg;
10392     }
10393    
10394 });
10395
10396  
10397
10398  /*
10399  * - LGPL
10400  *
10401  * table row
10402  * 
10403  */
10404
10405 /**
10406  * @class Roo.bootstrap.TableRow
10407  * @extends Roo.bootstrap.Component
10408  * Bootstrap TableRow class
10409  * @cfg {String} cls row class
10410  * @cfg {String} align Aligns the content in a table row
10411  * @cfg {String} bgcolor Specifies a background color for a table row
10412  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10413  * @cfg {String} valign Vertical aligns the content in a table row
10414  * 
10415  * @constructor
10416  * Create a new TableRow
10417  * @param {Object} config The config object
10418  */
10419
10420 Roo.bootstrap.TableRow = function(config){
10421     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10422 };
10423
10424 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10425     
10426     cls: false,
10427     align: false,
10428     bgcolor: false,
10429     charoff: false,
10430     valign: false,
10431     
10432     getAutoCreate : function(){
10433         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10434         
10435         cfg = {
10436             tag: 'tr'
10437         };
10438             
10439         if(this.cls){
10440             cfg.cls = this.cls;
10441         }
10442         if(this.align){
10443             cfg.align = this.align;
10444         }
10445         if(this.bgcolor){
10446             cfg.bgcolor = this.bgcolor;
10447         }
10448         if(this.charoff){
10449             cfg.charoff = this.charoff;
10450         }
10451         if(this.valign){
10452             cfg.valign = this.valign;
10453         }
10454         
10455         return cfg;
10456     }
10457    
10458 });
10459
10460  
10461
10462  /*
10463  * - LGPL
10464  *
10465  * table body
10466  * 
10467  */
10468
10469 /**
10470  * @class Roo.bootstrap.TableBody
10471  * @extends Roo.bootstrap.Component
10472  * Bootstrap TableBody class
10473  * @cfg {String} cls element class
10474  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10475  * @cfg {String} align Aligns the content inside the element
10476  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10477  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10478  * 
10479  * @constructor
10480  * Create a new TableBody
10481  * @param {Object} config The config object
10482  */
10483
10484 Roo.bootstrap.TableBody = function(config){
10485     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10486 };
10487
10488 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10489     
10490     cls: false,
10491     tag: false,
10492     align: false,
10493     charoff: false,
10494     valign: false,
10495     
10496     getAutoCreate : function(){
10497         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10498         
10499         cfg = {
10500             tag: 'tbody'
10501         };
10502             
10503         if (this.cls) {
10504             cfg.cls=this.cls
10505         }
10506         if(this.tag){
10507             cfg.tag = this.tag;
10508         }
10509         
10510         if(this.align){
10511             cfg.align = this.align;
10512         }
10513         if(this.charoff){
10514             cfg.charoff = this.charoff;
10515         }
10516         if(this.valign){
10517             cfg.valign = this.valign;
10518         }
10519         
10520         return cfg;
10521     }
10522     
10523     
10524 //    initEvents : function()
10525 //    {
10526 //        
10527 //        if(!this.store){
10528 //            return;
10529 //        }
10530 //        
10531 //        this.store = Roo.factory(this.store, Roo.data);
10532 //        this.store.on('load', this.onLoad, this);
10533 //        
10534 //        this.store.load();
10535 //        
10536 //    },
10537 //    
10538 //    onLoad: function () 
10539 //    {   
10540 //        this.fireEvent('load', this);
10541 //    }
10542 //    
10543 //   
10544 });
10545
10546  
10547
10548  /*
10549  * Based on:
10550  * Ext JS Library 1.1.1
10551  * Copyright(c) 2006-2007, Ext JS, LLC.
10552  *
10553  * Originally Released Under LGPL - original licence link has changed is not relivant.
10554  *
10555  * Fork - LGPL
10556  * <script type="text/javascript">
10557  */
10558
10559 // as we use this in bootstrap.
10560 Roo.namespace('Roo.form');
10561  /**
10562  * @class Roo.form.Action
10563  * Internal Class used to handle form actions
10564  * @constructor
10565  * @param {Roo.form.BasicForm} el The form element or its id
10566  * @param {Object} config Configuration options
10567  */
10568
10569  
10570  
10571 // define the action interface
10572 Roo.form.Action = function(form, options){
10573     this.form = form;
10574     this.options = options || {};
10575 };
10576 /**
10577  * Client Validation Failed
10578  * @const 
10579  */
10580 Roo.form.Action.CLIENT_INVALID = 'client';
10581 /**
10582  * Server Validation Failed
10583  * @const 
10584  */
10585 Roo.form.Action.SERVER_INVALID = 'server';
10586  /**
10587  * Connect to Server Failed
10588  * @const 
10589  */
10590 Roo.form.Action.CONNECT_FAILURE = 'connect';
10591 /**
10592  * Reading Data from Server Failed
10593  * @const 
10594  */
10595 Roo.form.Action.LOAD_FAILURE = 'load';
10596
10597 Roo.form.Action.prototype = {
10598     type : 'default',
10599     failureType : undefined,
10600     response : undefined,
10601     result : undefined,
10602
10603     // interface method
10604     run : function(options){
10605
10606     },
10607
10608     // interface method
10609     success : function(response){
10610
10611     },
10612
10613     // interface method
10614     handleResponse : function(response){
10615
10616     },
10617
10618     // default connection failure
10619     failure : function(response){
10620         
10621         this.response = response;
10622         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10623         this.form.afterAction(this, false);
10624     },
10625
10626     processResponse : function(response){
10627         this.response = response;
10628         if(!response.responseText){
10629             return true;
10630         }
10631         this.result = this.handleResponse(response);
10632         return this.result;
10633     },
10634
10635     // utility functions used internally
10636     getUrl : function(appendParams){
10637         var url = this.options.url || this.form.url || this.form.el.dom.action;
10638         if(appendParams){
10639             var p = this.getParams();
10640             if(p){
10641                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10642             }
10643         }
10644         return url;
10645     },
10646
10647     getMethod : function(){
10648         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10649     },
10650
10651     getParams : function(){
10652         var bp = this.form.baseParams;
10653         var p = this.options.params;
10654         if(p){
10655             if(typeof p == "object"){
10656                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10657             }else if(typeof p == 'string' && bp){
10658                 p += '&' + Roo.urlEncode(bp);
10659             }
10660         }else if(bp){
10661             p = Roo.urlEncode(bp);
10662         }
10663         return p;
10664     },
10665
10666     createCallback : function(){
10667         return {
10668             success: this.success,
10669             failure: this.failure,
10670             scope: this,
10671             timeout: (this.form.timeout*1000),
10672             upload: this.form.fileUpload ? this.success : undefined
10673         };
10674     }
10675 };
10676
10677 Roo.form.Action.Submit = function(form, options){
10678     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10679 };
10680
10681 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10682     type : 'submit',
10683
10684     haveProgress : false,
10685     uploadComplete : false,
10686     
10687     // uploadProgress indicator.
10688     uploadProgress : function()
10689     {
10690         if (!this.form.progressUrl) {
10691             return;
10692         }
10693         
10694         if (!this.haveProgress) {
10695             Roo.MessageBox.progress("Uploading", "Uploading");
10696         }
10697         if (this.uploadComplete) {
10698            Roo.MessageBox.hide();
10699            return;
10700         }
10701         
10702         this.haveProgress = true;
10703    
10704         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10705         
10706         var c = new Roo.data.Connection();
10707         c.request({
10708             url : this.form.progressUrl,
10709             params: {
10710                 id : uid
10711             },
10712             method: 'GET',
10713             success : function(req){
10714                //console.log(data);
10715                 var rdata = false;
10716                 var edata;
10717                 try  {
10718                    rdata = Roo.decode(req.responseText)
10719                 } catch (e) {
10720                     Roo.log("Invalid data from server..");
10721                     Roo.log(edata);
10722                     return;
10723                 }
10724                 if (!rdata || !rdata.success) {
10725                     Roo.log(rdata);
10726                     Roo.MessageBox.alert(Roo.encode(rdata));
10727                     return;
10728                 }
10729                 var data = rdata.data;
10730                 
10731                 if (this.uploadComplete) {
10732                    Roo.MessageBox.hide();
10733                    return;
10734                 }
10735                    
10736                 if (data){
10737                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10738                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10739                     );
10740                 }
10741                 this.uploadProgress.defer(2000,this);
10742             },
10743        
10744             failure: function(data) {
10745                 Roo.log('progress url failed ');
10746                 Roo.log(data);
10747             },
10748             scope : this
10749         });
10750            
10751     },
10752     
10753     
10754     run : function()
10755     {
10756         // run get Values on the form, so it syncs any secondary forms.
10757         this.form.getValues();
10758         
10759         var o = this.options;
10760         var method = this.getMethod();
10761         var isPost = method == 'POST';
10762         if(o.clientValidation === false || this.form.isValid()){
10763             
10764             if (this.form.progressUrl) {
10765                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10766                     (new Date() * 1) + '' + Math.random());
10767                     
10768             } 
10769             
10770             
10771             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10772                 form:this.form.el.dom,
10773                 url:this.getUrl(!isPost),
10774                 method: method,
10775                 params:isPost ? this.getParams() : null,
10776                 isUpload: this.form.fileUpload,
10777                 formData : this.form.formData
10778             }));
10779             
10780             this.uploadProgress();
10781
10782         }else if (o.clientValidation !== false){ // client validation failed
10783             this.failureType = Roo.form.Action.CLIENT_INVALID;
10784             this.form.afterAction(this, false);
10785         }
10786     },
10787
10788     success : function(response)
10789     {
10790         this.uploadComplete= true;
10791         if (this.haveProgress) {
10792             Roo.MessageBox.hide();
10793         }
10794         
10795         
10796         var result = this.processResponse(response);
10797         if(result === true || result.success){
10798             this.form.afterAction(this, true);
10799             return;
10800         }
10801         if(result.errors){
10802             this.form.markInvalid(result.errors);
10803             this.failureType = Roo.form.Action.SERVER_INVALID;
10804         }
10805         this.form.afterAction(this, false);
10806     },
10807     failure : function(response)
10808     {
10809         this.uploadComplete= true;
10810         if (this.haveProgress) {
10811             Roo.MessageBox.hide();
10812         }
10813         
10814         this.response = response;
10815         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10816         this.form.afterAction(this, false);
10817     },
10818     
10819     handleResponse : function(response){
10820         if(this.form.errorReader){
10821             var rs = this.form.errorReader.read(response);
10822             var errors = [];
10823             if(rs.records){
10824                 for(var i = 0, len = rs.records.length; i < len; i++) {
10825                     var r = rs.records[i];
10826                     errors[i] = r.data;
10827                 }
10828             }
10829             if(errors.length < 1){
10830                 errors = null;
10831             }
10832             return {
10833                 success : rs.success,
10834                 errors : errors
10835             };
10836         }
10837         var ret = false;
10838         try {
10839             ret = Roo.decode(response.responseText);
10840         } catch (e) {
10841             ret = {
10842                 success: false,
10843                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10844                 errors : []
10845             };
10846         }
10847         return ret;
10848         
10849     }
10850 });
10851
10852
10853 Roo.form.Action.Load = function(form, options){
10854     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10855     this.reader = this.form.reader;
10856 };
10857
10858 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10859     type : 'load',
10860
10861     run : function(){
10862         
10863         Roo.Ajax.request(Roo.apply(
10864                 this.createCallback(), {
10865                     method:this.getMethod(),
10866                     url:this.getUrl(false),
10867                     params:this.getParams()
10868         }));
10869     },
10870
10871     success : function(response){
10872         
10873         var result = this.processResponse(response);
10874         if(result === true || !result.success || !result.data){
10875             this.failureType = Roo.form.Action.LOAD_FAILURE;
10876             this.form.afterAction(this, false);
10877             return;
10878         }
10879         this.form.clearInvalid();
10880         this.form.setValues(result.data);
10881         this.form.afterAction(this, true);
10882     },
10883
10884     handleResponse : function(response){
10885         if(this.form.reader){
10886             var rs = this.form.reader.read(response);
10887             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10888             return {
10889                 success : rs.success,
10890                 data : data
10891             };
10892         }
10893         return Roo.decode(response.responseText);
10894     }
10895 });
10896
10897 Roo.form.Action.ACTION_TYPES = {
10898     'load' : Roo.form.Action.Load,
10899     'submit' : Roo.form.Action.Submit
10900 };/*
10901  * - LGPL
10902  *
10903  * form
10904  *
10905  */
10906
10907 /**
10908  * @class Roo.bootstrap.Form
10909  * @extends Roo.bootstrap.Component
10910  * Bootstrap Form class
10911  * @cfg {String} method  GET | POST (default POST)
10912  * @cfg {String} labelAlign top | left (default top)
10913  * @cfg {String} align left  | right - for navbars
10914  * @cfg {Boolean} loadMask load mask when submit (default true)
10915
10916  *
10917  * @constructor
10918  * Create a new Form
10919  * @param {Object} config The config object
10920  */
10921
10922
10923 Roo.bootstrap.Form = function(config){
10924     
10925     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10926     
10927     Roo.bootstrap.Form.popover.apply();
10928     
10929     this.addEvents({
10930         /**
10931          * @event clientvalidation
10932          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10933          * @param {Form} this
10934          * @param {Boolean} valid true if the form has passed client-side validation
10935          */
10936         clientvalidation: true,
10937         /**
10938          * @event beforeaction
10939          * Fires before any action is performed. Return false to cancel the action.
10940          * @param {Form} this
10941          * @param {Action} action The action to be performed
10942          */
10943         beforeaction: true,
10944         /**
10945          * @event actionfailed
10946          * Fires when an action fails.
10947          * @param {Form} this
10948          * @param {Action} action The action that failed
10949          */
10950         actionfailed : true,
10951         /**
10952          * @event actioncomplete
10953          * Fires when an action is completed.
10954          * @param {Form} this
10955          * @param {Action} action The action that completed
10956          */
10957         actioncomplete : true
10958     });
10959 };
10960
10961 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10962
10963      /**
10964      * @cfg {String} method
10965      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10966      */
10967     method : 'POST',
10968     /**
10969      * @cfg {String} url
10970      * The URL to use for form actions if one isn't supplied in the action options.
10971      */
10972     /**
10973      * @cfg {Boolean} fileUpload
10974      * Set to true if this form is a file upload.
10975      */
10976
10977     /**
10978      * @cfg {Object} baseParams
10979      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10980      */
10981
10982     /**
10983      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10984      */
10985     timeout: 30,
10986     /**
10987      * @cfg {Sting} align (left|right) for navbar forms
10988      */
10989     align : 'left',
10990
10991     // private
10992     activeAction : null,
10993
10994     /**
10995      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10996      * element by passing it or its id or mask the form itself by passing in true.
10997      * @type Mixed
10998      */
10999     waitMsgTarget : false,
11000
11001     loadMask : true,
11002     
11003     /**
11004      * @cfg {Boolean} errorMask (true|false) default false
11005      */
11006     errorMask : false,
11007     
11008     /**
11009      * @cfg {Number} maskOffset Default 100
11010      */
11011     maskOffset : 100,
11012     
11013     /**
11014      * @cfg {Boolean} maskBody
11015      */
11016     maskBody : false,
11017
11018     getAutoCreate : function(){
11019
11020         var cfg = {
11021             tag: 'form',
11022             method : this.method || 'POST',
11023             id : this.id || Roo.id(),
11024             cls : ''
11025         };
11026         if (this.parent().xtype.match(/^Nav/)) {
11027             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11028
11029         }
11030
11031         if (this.labelAlign == 'left' ) {
11032             cfg.cls += ' form-horizontal';
11033         }
11034
11035
11036         return cfg;
11037     },
11038     initEvents : function()
11039     {
11040         this.el.on('submit', this.onSubmit, this);
11041         // this was added as random key presses on the form where triggering form submit.
11042         this.el.on('keypress', function(e) {
11043             if (e.getCharCode() != 13) {
11044                 return true;
11045             }
11046             // we might need to allow it for textareas.. and some other items.
11047             // check e.getTarget().
11048
11049             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11050                 return true;
11051             }
11052
11053             Roo.log("keypress blocked");
11054
11055             e.preventDefault();
11056             return false;
11057         });
11058         
11059     },
11060     // private
11061     onSubmit : function(e){
11062         e.stopEvent();
11063     },
11064
11065      /**
11066      * Returns true if client-side validation on the form is successful.
11067      * @return Boolean
11068      */
11069     isValid : function(){
11070         var items = this.getItems();
11071         var valid = true;
11072         var target = false;
11073         
11074         items.each(function(f){
11075             
11076             if(f.validate()){
11077                 return;
11078             }
11079             
11080             Roo.log('invalid field: ' + f.name);
11081             
11082             valid = false;
11083
11084             if(!target && f.el.isVisible(true)){
11085                 target = f;
11086             }
11087            
11088         });
11089         
11090         if(this.errorMask && !valid){
11091             Roo.bootstrap.Form.popover.mask(this, target);
11092         }
11093         
11094         return valid;
11095     },
11096     
11097     /**
11098      * Returns true if any fields in this form have changed since their original load.
11099      * @return Boolean
11100      */
11101     isDirty : function(){
11102         var dirty = false;
11103         var items = this.getItems();
11104         items.each(function(f){
11105            if(f.isDirty()){
11106                dirty = true;
11107                return false;
11108            }
11109            return true;
11110         });
11111         return dirty;
11112     },
11113      /**
11114      * Performs a predefined action (submit or load) or custom actions you define on this form.
11115      * @param {String} actionName The name of the action type
11116      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11117      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11118      * accept other config options):
11119      * <pre>
11120 Property          Type             Description
11121 ----------------  ---------------  ----------------------------------------------------------------------------------
11122 url               String           The url for the action (defaults to the form's url)
11123 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11124 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11125 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11126                                    validate the form on the client (defaults to false)
11127      * </pre>
11128      * @return {BasicForm} this
11129      */
11130     doAction : function(action, options){
11131         if(typeof action == 'string'){
11132             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11133         }
11134         if(this.fireEvent('beforeaction', this, action) !== false){
11135             this.beforeAction(action);
11136             action.run.defer(100, action);
11137         }
11138         return this;
11139     },
11140
11141     // private
11142     beforeAction : function(action){
11143         var o = action.options;
11144         
11145         if(this.loadMask){
11146             
11147             if(this.maskBody){
11148                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11149             } else {
11150                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151             }
11152         }
11153         // not really supported yet.. ??
11154
11155         //if(this.waitMsgTarget === true){
11156         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11157         //}else if(this.waitMsgTarget){
11158         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11159         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11160         //}else {
11161         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11162        // }
11163
11164     },
11165
11166     // private
11167     afterAction : function(action, success){
11168         this.activeAction = null;
11169         var o = action.options;
11170
11171         if(this.loadMask){
11172             
11173             if(this.maskBody){
11174                 Roo.get(document.body).unmask();
11175             } else {
11176                 this.el.unmask();
11177             }
11178         }
11179         
11180         //if(this.waitMsgTarget === true){
11181 //            this.el.unmask();
11182         //}else if(this.waitMsgTarget){
11183         //    this.waitMsgTarget.unmask();
11184         //}else{
11185         //    Roo.MessageBox.updateProgress(1);
11186         //    Roo.MessageBox.hide();
11187        // }
11188         //
11189         if(success){
11190             if(o.reset){
11191                 this.reset();
11192             }
11193             Roo.callback(o.success, o.scope, [this, action]);
11194             this.fireEvent('actioncomplete', this, action);
11195
11196         }else{
11197
11198             // failure condition..
11199             // we have a scenario where updates need confirming.
11200             // eg. if a locking scenario exists..
11201             // we look for { errors : { needs_confirm : true }} in the response.
11202             if (
11203                 (typeof(action.result) != 'undefined')  &&
11204                 (typeof(action.result.errors) != 'undefined')  &&
11205                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11206            ){
11207                 var _t = this;
11208                 Roo.log("not supported yet");
11209                  /*
11210
11211                 Roo.MessageBox.confirm(
11212                     "Change requires confirmation",
11213                     action.result.errorMsg,
11214                     function(r) {
11215                         if (r != 'yes') {
11216                             return;
11217                         }
11218                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11219                     }
11220
11221                 );
11222                 */
11223
11224
11225                 return;
11226             }
11227
11228             Roo.callback(o.failure, o.scope, [this, action]);
11229             // show an error message if no failed handler is set..
11230             if (!this.hasListener('actionfailed')) {
11231                 Roo.log("need to add dialog support");
11232                 /*
11233                 Roo.MessageBox.alert("Error",
11234                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11235                         action.result.errorMsg :
11236                         "Saving Failed, please check your entries or try again"
11237                 );
11238                 */
11239             }
11240
11241             this.fireEvent('actionfailed', this, action);
11242         }
11243
11244     },
11245     /**
11246      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11247      * @param {String} id The value to search for
11248      * @return Field
11249      */
11250     findField : function(id){
11251         var items = this.getItems();
11252         var field = items.get(id);
11253         if(!field){
11254              items.each(function(f){
11255                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11256                     field = f;
11257                     return false;
11258                 }
11259                 return true;
11260             });
11261         }
11262         return field || null;
11263     },
11264      /**
11265      * Mark fields in this form invalid in bulk.
11266      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11267      * @return {BasicForm} this
11268      */
11269     markInvalid : function(errors){
11270         if(errors instanceof Array){
11271             for(var i = 0, len = errors.length; i < len; i++){
11272                 var fieldError = errors[i];
11273                 var f = this.findField(fieldError.id);
11274                 if(f){
11275                     f.markInvalid(fieldError.msg);
11276                 }
11277             }
11278         }else{
11279             var field, id;
11280             for(id in errors){
11281                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11282                     field.markInvalid(errors[id]);
11283                 }
11284             }
11285         }
11286         //Roo.each(this.childForms || [], function (f) {
11287         //    f.markInvalid(errors);
11288         //});
11289
11290         return this;
11291     },
11292
11293     /**
11294      * Set values for fields in this form in bulk.
11295      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11296      * @return {BasicForm} this
11297      */
11298     setValues : function(values){
11299         if(values instanceof Array){ // array of objects
11300             for(var i = 0, len = values.length; i < len; i++){
11301                 var v = values[i];
11302                 var f = this.findField(v.id);
11303                 if(f){
11304                     f.setValue(v.value);
11305                     if(this.trackResetOnLoad){
11306                         f.originalValue = f.getValue();
11307                     }
11308                 }
11309             }
11310         }else{ // object hash
11311             var field, id;
11312             for(id in values){
11313                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11314
11315                     if (field.setFromData &&
11316                         field.valueField &&
11317                         field.displayField &&
11318                         // combos' with local stores can
11319                         // be queried via setValue()
11320                         // to set their value..
11321                         (field.store && !field.store.isLocal)
11322                         ) {
11323                         // it's a combo
11324                         var sd = { };
11325                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11326                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11327                         field.setFromData(sd);
11328
11329                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11330                         
11331                         field.setFromData(values);
11332                         
11333                     } else {
11334                         field.setValue(values[id]);
11335                     }
11336
11337
11338                     if(this.trackResetOnLoad){
11339                         field.originalValue = field.getValue();
11340                     }
11341                 }
11342             }
11343         }
11344
11345         //Roo.each(this.childForms || [], function (f) {
11346         //    f.setValues(values);
11347         //});
11348
11349         return this;
11350     },
11351
11352     /**
11353      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11354      * they are returned as an array.
11355      * @param {Boolean} asString
11356      * @return {Object}
11357      */
11358     getValues : function(asString){
11359         //if (this.childForms) {
11360             // copy values from the child forms
11361         //    Roo.each(this.childForms, function (f) {
11362         //        this.setValues(f.getValues());
11363         //    }, this);
11364         //}
11365
11366
11367
11368         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11369         if(asString === true){
11370             return fs;
11371         }
11372         return Roo.urlDecode(fs);
11373     },
11374
11375     /**
11376      * Returns the fields in this form as an object with key/value pairs.
11377      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11378      * @return {Object}
11379      */
11380     getFieldValues : function(with_hidden)
11381     {
11382         var items = this.getItems();
11383         var ret = {};
11384         items.each(function(f){
11385             
11386             if (!f.getName()) {
11387                 return;
11388             }
11389             
11390             var v = f.getValue();
11391             
11392             if (f.inputType =='radio') {
11393                 if (typeof(ret[f.getName()]) == 'undefined') {
11394                     ret[f.getName()] = ''; // empty..
11395                 }
11396
11397                 if (!f.el.dom.checked) {
11398                     return;
11399
11400                 }
11401                 v = f.el.dom.value;
11402
11403             }
11404             
11405             if(f.xtype == 'MoneyField'){
11406                 ret[f.currencyName] = f.getCurrency();
11407             }
11408
11409             // not sure if this supported any more..
11410             if ((typeof(v) == 'object') && f.getRawValue) {
11411                 v = f.getRawValue() ; // dates..
11412             }
11413             // combo boxes where name != hiddenName...
11414             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11415                 ret[f.name] = f.getRawValue();
11416             }
11417             ret[f.getName()] = v;
11418         });
11419
11420         return ret;
11421     },
11422
11423     /**
11424      * Clears all invalid messages in this form.
11425      * @return {BasicForm} this
11426      */
11427     clearInvalid : function(){
11428         var items = this.getItems();
11429
11430         items.each(function(f){
11431            f.clearInvalid();
11432         });
11433
11434         return this;
11435     },
11436
11437     /**
11438      * Resets this form.
11439      * @return {BasicForm} this
11440      */
11441     reset : function(){
11442         var items = this.getItems();
11443         items.each(function(f){
11444             f.reset();
11445         });
11446
11447         Roo.each(this.childForms || [], function (f) {
11448             f.reset();
11449         });
11450
11451
11452         return this;
11453     },
11454     
11455     getItems : function()
11456     {
11457         var r=new Roo.util.MixedCollection(false, function(o){
11458             return o.id || (o.id = Roo.id());
11459         });
11460         var iter = function(el) {
11461             if (el.inputEl) {
11462                 r.add(el);
11463             }
11464             if (!el.items) {
11465                 return;
11466             }
11467             Roo.each(el.items,function(e) {
11468                 iter(e);
11469             });
11470         };
11471
11472         iter(this);
11473         return r;
11474     },
11475     
11476     hideFields : function(items)
11477     {
11478         Roo.each(items, function(i){
11479             
11480             var f = this.findField(i);
11481             
11482             if(!f){
11483                 return;
11484             }
11485             
11486             f.hide();
11487             
11488         }, this);
11489     },
11490     
11491     showFields : function(items)
11492     {
11493         Roo.each(items, function(i){
11494             
11495             var f = this.findField(i);
11496             
11497             if(!f){
11498                 return;
11499             }
11500             
11501             f.show();
11502             
11503         }, this);
11504     }
11505
11506 });
11507
11508 Roo.apply(Roo.bootstrap.Form, {
11509     
11510     popover : {
11511         
11512         padding : 5,
11513         
11514         isApplied : false,
11515         
11516         isMasked : false,
11517         
11518         form : false,
11519         
11520         target : false,
11521         
11522         toolTip : false,
11523         
11524         intervalID : false,
11525         
11526         maskEl : false,
11527         
11528         apply : function()
11529         {
11530             if(this.isApplied){
11531                 return;
11532             }
11533             
11534             this.maskEl = {
11535                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11536                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11537                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11538                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11539             };
11540             
11541             this.maskEl.top.enableDisplayMode("block");
11542             this.maskEl.left.enableDisplayMode("block");
11543             this.maskEl.bottom.enableDisplayMode("block");
11544             this.maskEl.right.enableDisplayMode("block");
11545             
11546             this.toolTip = new Roo.bootstrap.Tooltip({
11547                 cls : 'roo-form-error-popover',
11548                 alignment : {
11549                     'left' : ['r-l', [-2,0], 'right'],
11550                     'right' : ['l-r', [2,0], 'left'],
11551                     'bottom' : ['tl-bl', [0,2], 'top'],
11552                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11553                 }
11554             });
11555             
11556             this.toolTip.render(Roo.get(document.body));
11557
11558             this.toolTip.el.enableDisplayMode("block");
11559             
11560             Roo.get(document.body).on('click', function(){
11561                 this.unmask();
11562             }, this);
11563             
11564             Roo.get(document.body).on('touchstart', function(){
11565                 this.unmask();
11566             }, this);
11567             
11568             this.isApplied = true
11569         },
11570         
11571         mask : function(form, target)
11572         {
11573             this.form = form;
11574             
11575             this.target = target;
11576             
11577             if(!this.form.errorMask || !target.el){
11578                 return;
11579             }
11580             
11581             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11582             
11583             Roo.log(scrollable);
11584             
11585             var ot = this.target.el.calcOffsetsTo(scrollable);
11586             
11587             var scrollTo = ot[1] - this.form.maskOffset;
11588             
11589             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11590             
11591             scrollable.scrollTo('top', scrollTo);
11592             
11593             var box = this.target.el.getBox();
11594             Roo.log(box);
11595             var zIndex = Roo.bootstrap.Modal.zIndex++;
11596
11597             
11598             this.maskEl.top.setStyle('position', 'absolute');
11599             this.maskEl.top.setStyle('z-index', zIndex);
11600             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11601             this.maskEl.top.setLeft(0);
11602             this.maskEl.top.setTop(0);
11603             this.maskEl.top.show();
11604             
11605             this.maskEl.left.setStyle('position', 'absolute');
11606             this.maskEl.left.setStyle('z-index', zIndex);
11607             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11608             this.maskEl.left.setLeft(0);
11609             this.maskEl.left.setTop(box.y - this.padding);
11610             this.maskEl.left.show();
11611
11612             this.maskEl.bottom.setStyle('position', 'absolute');
11613             this.maskEl.bottom.setStyle('z-index', zIndex);
11614             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11615             this.maskEl.bottom.setLeft(0);
11616             this.maskEl.bottom.setTop(box.bottom + this.padding);
11617             this.maskEl.bottom.show();
11618
11619             this.maskEl.right.setStyle('position', 'absolute');
11620             this.maskEl.right.setStyle('z-index', zIndex);
11621             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11622             this.maskEl.right.setLeft(box.right + this.padding);
11623             this.maskEl.right.setTop(box.y - this.padding);
11624             this.maskEl.right.show();
11625
11626             this.toolTip.bindEl = this.target.el;
11627
11628             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11629
11630             var tip = this.target.blankText;
11631
11632             if(this.target.getValue() !== '' ) {
11633                 
11634                 if (this.target.invalidText.length) {
11635                     tip = this.target.invalidText;
11636                 } else if (this.target.regexText.length){
11637                     tip = this.target.regexText;
11638                 }
11639             }
11640
11641             this.toolTip.show(tip);
11642
11643             this.intervalID = window.setInterval(function() {
11644                 Roo.bootstrap.Form.popover.unmask();
11645             }, 10000);
11646
11647             window.onwheel = function(){ return false;};
11648             
11649             (function(){ this.isMasked = true; }).defer(500, this);
11650             
11651         },
11652         
11653         unmask : function()
11654         {
11655             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11656                 return;
11657             }
11658             
11659             this.maskEl.top.setStyle('position', 'absolute');
11660             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11661             this.maskEl.top.hide();
11662
11663             this.maskEl.left.setStyle('position', 'absolute');
11664             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11665             this.maskEl.left.hide();
11666
11667             this.maskEl.bottom.setStyle('position', 'absolute');
11668             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11669             this.maskEl.bottom.hide();
11670
11671             this.maskEl.right.setStyle('position', 'absolute');
11672             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11673             this.maskEl.right.hide();
11674             
11675             this.toolTip.hide();
11676             
11677             this.toolTip.el.hide();
11678             
11679             window.onwheel = function(){ return true;};
11680             
11681             if(this.intervalID){
11682                 window.clearInterval(this.intervalID);
11683                 this.intervalID = false;
11684             }
11685             
11686             this.isMasked = false;
11687             
11688         }
11689         
11690     }
11691     
11692 });
11693
11694 /*
11695  * Based on:
11696  * Ext JS Library 1.1.1
11697  * Copyright(c) 2006-2007, Ext JS, LLC.
11698  *
11699  * Originally Released Under LGPL - original licence link has changed is not relivant.
11700  *
11701  * Fork - LGPL
11702  * <script type="text/javascript">
11703  */
11704 /**
11705  * @class Roo.form.VTypes
11706  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11707  * @singleton
11708  */
11709 Roo.form.VTypes = function(){
11710     // closure these in so they are only created once.
11711     var alpha = /^[a-zA-Z_]+$/;
11712     var alphanum = /^[a-zA-Z0-9_]+$/;
11713     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11714     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11715
11716     // All these messages and functions are configurable
11717     return {
11718         /**
11719          * The function used to validate email addresses
11720          * @param {String} value The email address
11721          */
11722         'email' : function(v){
11723             return email.test(v);
11724         },
11725         /**
11726          * The error text to display when the email validation function returns false
11727          * @type String
11728          */
11729         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11730         /**
11731          * The keystroke filter mask to be applied on email input
11732          * @type RegExp
11733          */
11734         'emailMask' : /[a-z0-9_\.\-@]/i,
11735
11736         /**
11737          * The function used to validate URLs
11738          * @param {String} value The URL
11739          */
11740         'url' : function(v){
11741             return url.test(v);
11742         },
11743         /**
11744          * The error text to display when the url validation function returns false
11745          * @type String
11746          */
11747         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11748         
11749         /**
11750          * The function used to validate alpha values
11751          * @param {String} value The value
11752          */
11753         'alpha' : function(v){
11754             return alpha.test(v);
11755         },
11756         /**
11757          * The error text to display when the alpha validation function returns false
11758          * @type String
11759          */
11760         'alphaText' : 'This field should only contain letters and _',
11761         /**
11762          * The keystroke filter mask to be applied on alpha input
11763          * @type RegExp
11764          */
11765         'alphaMask' : /[a-z_]/i,
11766
11767         /**
11768          * The function used to validate alphanumeric values
11769          * @param {String} value The value
11770          */
11771         'alphanum' : function(v){
11772             return alphanum.test(v);
11773         },
11774         /**
11775          * The error text to display when the alphanumeric validation function returns false
11776          * @type String
11777          */
11778         'alphanumText' : 'This field should only contain letters, numbers and _',
11779         /**
11780          * The keystroke filter mask to be applied on alphanumeric input
11781          * @type RegExp
11782          */
11783         'alphanumMask' : /[a-z0-9_]/i
11784     };
11785 }();/*
11786  * - LGPL
11787  *
11788  * Input
11789  * 
11790  */
11791
11792 /**
11793  * @class Roo.bootstrap.Input
11794  * @extends Roo.bootstrap.Component
11795  * Bootstrap Input class
11796  * @cfg {Boolean} disabled is it disabled
11797  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11798  * @cfg {String} name name of the input
11799  * @cfg {string} fieldLabel - the label associated
11800  * @cfg {string} placeholder - placeholder to put in text.
11801  * @cfg {string}  before - input group add on before
11802  * @cfg {string} after - input group add on after
11803  * @cfg {string} size - (lg|sm) or leave empty..
11804  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11805  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11806  * @cfg {Number} md colspan out of 12 for computer-sized screens
11807  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11808  * @cfg {string} value default value of the input
11809  * @cfg {Number} labelWidth set the width of label 
11810  * @cfg {Number} labellg set the width of label (1-12)
11811  * @cfg {Number} labelmd set the width of label (1-12)
11812  * @cfg {Number} labelsm set the width of label (1-12)
11813  * @cfg {Number} labelxs set the width of label (1-12)
11814  * @cfg {String} labelAlign (top|left)
11815  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11816  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11817  * @cfg {String} indicatorpos (left|right) default left
11818  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11819  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11820  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11821
11822  * @cfg {String} align (left|center|right) Default left
11823  * @cfg {Boolean} forceFeedback (true|false) Default false
11824  * 
11825  * @constructor
11826  * Create a new Input
11827  * @param {Object} config The config object
11828  */
11829
11830 Roo.bootstrap.Input = function(config){
11831     
11832     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11833     
11834     this.addEvents({
11835         /**
11836          * @event focus
11837          * Fires when this field receives input focus.
11838          * @param {Roo.form.Field} this
11839          */
11840         focus : true,
11841         /**
11842          * @event blur
11843          * Fires when this field loses input focus.
11844          * @param {Roo.form.Field} this
11845          */
11846         blur : true,
11847         /**
11848          * @event specialkey
11849          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11850          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11851          * @param {Roo.form.Field} this
11852          * @param {Roo.EventObject} e The event object
11853          */
11854         specialkey : true,
11855         /**
11856          * @event change
11857          * Fires just before the field blurs if the field value has changed.
11858          * @param {Roo.form.Field} this
11859          * @param {Mixed} newValue The new value
11860          * @param {Mixed} oldValue The original value
11861          */
11862         change : true,
11863         /**
11864          * @event invalid
11865          * Fires after the field has been marked as invalid.
11866          * @param {Roo.form.Field} this
11867          * @param {String} msg The validation message
11868          */
11869         invalid : true,
11870         /**
11871          * @event valid
11872          * Fires after the field has been validated with no errors.
11873          * @param {Roo.form.Field} this
11874          */
11875         valid : true,
11876          /**
11877          * @event keyup
11878          * Fires after the key up
11879          * @param {Roo.form.Field} this
11880          * @param {Roo.EventObject}  e The event Object
11881          */
11882         keyup : true,
11883         /**
11884          * @event paste
11885          * Fires after the user pastes into input
11886          * @param {Roo.form.Field} this
11887          * @param {Roo.EventObject}  e The event Object
11888          */
11889         paste : true
11890     });
11891 };
11892
11893 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11894      /**
11895      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11896       automatic validation (defaults to "keyup").
11897      */
11898     validationEvent : "keyup",
11899      /**
11900      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11901      */
11902     validateOnBlur : true,
11903     /**
11904      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11905      */
11906     validationDelay : 250,
11907      /**
11908      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11909      */
11910     focusClass : "x-form-focus",  // not needed???
11911     
11912        
11913     /**
11914      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11915      */
11916     invalidClass : "has-warning",
11917     
11918     /**
11919      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11920      */
11921     validClass : "has-success",
11922     
11923     /**
11924      * @cfg {Boolean} hasFeedback (true|false) default true
11925      */
11926     hasFeedback : true,
11927     
11928     /**
11929      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11930      */
11931     invalidFeedbackClass : "glyphicon-warning-sign",
11932     
11933     /**
11934      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11935      */
11936     validFeedbackClass : "glyphicon-ok",
11937     
11938     /**
11939      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11940      */
11941     selectOnFocus : false,
11942     
11943      /**
11944      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11945      */
11946     maskRe : null,
11947        /**
11948      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11949      */
11950     vtype : null,
11951     
11952       /**
11953      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11954      */
11955     disableKeyFilter : false,
11956     
11957        /**
11958      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11959      */
11960     disabled : false,
11961      /**
11962      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11963      */
11964     allowBlank : true,
11965     /**
11966      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11967      */
11968     blankText : "Please complete this mandatory field",
11969     
11970      /**
11971      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11972      */
11973     minLength : 0,
11974     /**
11975      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11976      */
11977     maxLength : Number.MAX_VALUE,
11978     /**
11979      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11980      */
11981     minLengthText : "The minimum length for this field is {0}",
11982     /**
11983      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11984      */
11985     maxLengthText : "The maximum length for this field is {0}",
11986   
11987     
11988     /**
11989      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11990      * If available, this function will be called only after the basic validators all return true, and will be passed the
11991      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11992      */
11993     validator : null,
11994     /**
11995      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11996      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11997      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11998      */
11999     regex : null,
12000     /**
12001      * @cfg {String} regexText -- Depricated - use Invalid Text
12002      */
12003     regexText : "",
12004     
12005     /**
12006      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12007      */
12008     invalidText : "",
12009     
12010     
12011     
12012     autocomplete: false,
12013     
12014     
12015     fieldLabel : '',
12016     inputType : 'text',
12017     
12018     name : false,
12019     placeholder: false,
12020     before : false,
12021     after : false,
12022     size : false,
12023     hasFocus : false,
12024     preventMark: false,
12025     isFormField : true,
12026     value : '',
12027     labelWidth : 2,
12028     labelAlign : false,
12029     readOnly : false,
12030     align : false,
12031     formatedValue : false,
12032     forceFeedback : false,
12033     
12034     indicatorpos : 'left',
12035     
12036     labellg : 0,
12037     labelmd : 0,
12038     labelsm : 0,
12039     labelxs : 0,
12040     
12041     capture : '',
12042     accept : '',
12043     
12044     parentLabelAlign : function()
12045     {
12046         var parent = this;
12047         while (parent.parent()) {
12048             parent = parent.parent();
12049             if (typeof(parent.labelAlign) !='undefined') {
12050                 return parent.labelAlign;
12051             }
12052         }
12053         return 'left';
12054         
12055     },
12056     
12057     getAutoCreate : function()
12058     {
12059         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12060         
12061         var id = Roo.id();
12062         
12063         var cfg = {};
12064         
12065         if(this.inputType != 'hidden'){
12066             cfg.cls = 'form-group' //input-group
12067         }
12068         
12069         var input =  {
12070             tag: 'input',
12071             id : id,
12072             type : this.inputType,
12073             value : this.value,
12074             cls : 'form-control',
12075             placeholder : this.placeholder || '',
12076             autocomplete : this.autocomplete || 'new-password'
12077         };
12078         if (this.inputType == 'file') {
12079             input.style = 'overflow:hidden'; // why not in CSS?
12080         }
12081         
12082         if(this.capture.length){
12083             input.capture = this.capture;
12084         }
12085         
12086         if(this.accept.length){
12087             input.accept = this.accept + "/*";
12088         }
12089         
12090         if(this.align){
12091             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12092         }
12093         
12094         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12095             input.maxLength = this.maxLength;
12096         }
12097         
12098         if (this.disabled) {
12099             input.disabled=true;
12100         }
12101         
12102         if (this.readOnly) {
12103             input.readonly=true;
12104         }
12105         
12106         if (this.name) {
12107             input.name = this.name;
12108         }
12109         
12110         if (this.size) {
12111             input.cls += ' input-' + this.size;
12112         }
12113         
12114         var settings=this;
12115         ['xs','sm','md','lg'].map(function(size){
12116             if (settings[size]) {
12117                 cfg.cls += ' col-' + size + '-' + settings[size];
12118             }
12119         });
12120         
12121         var inputblock = input;
12122         
12123         var feedback = {
12124             tag: 'span',
12125             cls: 'glyphicon form-control-feedback'
12126         };
12127             
12128         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12129             
12130             inputblock = {
12131                 cls : 'has-feedback',
12132                 cn :  [
12133                     input,
12134                     feedback
12135                 ] 
12136             };  
12137         }
12138         
12139         if (this.before || this.after) {
12140             
12141             inputblock = {
12142                 cls : 'input-group',
12143                 cn :  [] 
12144             };
12145             
12146             if (this.before && typeof(this.before) == 'string') {
12147                 
12148                 inputblock.cn.push({
12149                     tag :'span',
12150                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12151                     html : this.before
12152                 });
12153             }
12154             if (this.before && typeof(this.before) == 'object') {
12155                 this.before = Roo.factory(this.before);
12156                 
12157                 inputblock.cn.push({
12158                     tag :'span',
12159                     cls : 'roo-input-before input-group-prepend   input-group-' +
12160                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12161                 });
12162             }
12163             
12164             inputblock.cn.push(input);
12165             
12166             if (this.after && typeof(this.after) == 'string') {
12167                 inputblock.cn.push({
12168                     tag :'span',
12169                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12170                     html : this.after
12171                 });
12172             }
12173             if (this.after && typeof(this.after) == 'object') {
12174                 this.after = Roo.factory(this.after);
12175                 
12176                 inputblock.cn.push({
12177                     tag :'span',
12178                     cls : 'roo-input-after input-group-append  input-group-' +
12179                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12180                 });
12181             }
12182             
12183             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12184                 inputblock.cls += ' has-feedback';
12185                 inputblock.cn.push(feedback);
12186             }
12187         };
12188         var indicator = {
12189             tag : 'i',
12190             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12191             tooltip : 'This field is required'
12192         };
12193         if (this.allowBlank ) {
12194             indicator.style = this.allowBlank ? ' display:none' : '';
12195         }
12196         if (align ==='left' && this.fieldLabel.length) {
12197             
12198             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12199             
12200             cfg.cn = [
12201                 indicator,
12202                 {
12203                     tag: 'label',
12204                     'for' :  id,
12205                     cls : 'control-label col-form-label',
12206                     html : this.fieldLabel
12207
12208                 },
12209                 {
12210                     cls : "", 
12211                     cn: [
12212                         inputblock
12213                     ]
12214                 }
12215             ];
12216             
12217             var labelCfg = cfg.cn[1];
12218             var contentCfg = cfg.cn[2];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 cfg.cn = [
12222                     {
12223                         tag: 'label',
12224                         'for' :  id,
12225                         cls : 'control-label col-form-label',
12226                         cn : [
12227                             {
12228                                 tag : 'span',
12229                                 html : this.fieldLabel
12230                             },
12231                             indicator
12232                         ]
12233                     },
12234                     {
12235                         cls : "",
12236                         cn: [
12237                             inputblock
12238                         ]
12239                     }
12240
12241                 ];
12242                 
12243                 labelCfg = cfg.cn[0];
12244                 contentCfg = cfg.cn[1];
12245             
12246             }
12247             
12248             if(this.labelWidth > 12){
12249                 labelCfg.style = "width: " + this.labelWidth + 'px';
12250             }
12251             
12252             if(this.labelWidth < 13 && this.labelmd == 0){
12253                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12254             }
12255             
12256             if(this.labellg > 0){
12257                 labelCfg.cls += ' col-lg-' + this.labellg;
12258                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12259             }
12260             
12261             if(this.labelmd > 0){
12262                 labelCfg.cls += ' col-md-' + this.labelmd;
12263                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12264             }
12265             
12266             if(this.labelsm > 0){
12267                 labelCfg.cls += ' col-sm-' + this.labelsm;
12268                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12269             }
12270             
12271             if(this.labelxs > 0){
12272                 labelCfg.cls += ' col-xs-' + this.labelxs;
12273                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12274             }
12275             
12276             
12277         } else if ( this.fieldLabel.length) {
12278                 
12279             
12280             
12281             cfg.cn = [
12282                 {
12283                     tag : 'i',
12284                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12285                     tooltip : 'This field is required',
12286                     style : this.allowBlank ? ' display:none' : '' 
12287                 },
12288                 {
12289                     tag: 'label',
12290                    //cls : 'input-group-addon',
12291                     html : this.fieldLabel
12292
12293                 },
12294
12295                inputblock
12296
12297            ];
12298            
12299            if(this.indicatorpos == 'right'){
12300        
12301                 cfg.cn = [
12302                     {
12303                         tag: 'label',
12304                        //cls : 'input-group-addon',
12305                         html : this.fieldLabel
12306
12307                     },
12308                     {
12309                         tag : 'i',
12310                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12311                         tooltip : 'This field is required',
12312                         style : this.allowBlank ? ' display:none' : '' 
12313                     },
12314
12315                    inputblock
12316
12317                ];
12318
12319             }
12320
12321         } else {
12322             
12323             cfg.cn = [
12324
12325                     inputblock
12326
12327             ];
12328                 
12329                 
12330         };
12331         
12332         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12333            cfg.cls += ' navbar-form';
12334         }
12335         
12336         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12337             // on BS4 we do this only if not form 
12338             cfg.cls += ' navbar-form';
12339             cfg.tag = 'li';
12340         }
12341         
12342         return cfg;
12343         
12344     },
12345     /**
12346      * return the real input element.
12347      */
12348     inputEl: function ()
12349     {
12350         return this.el.select('input.form-control',true).first();
12351     },
12352     
12353     tooltipEl : function()
12354     {
12355         return this.inputEl();
12356     },
12357     
12358     indicatorEl : function()
12359     {
12360         if (Roo.bootstrap.version == 4) {
12361             return false; // not enabled in v4 yet.
12362         }
12363         
12364         var indicator = this.el.select('i.roo-required-indicator',true).first();
12365         
12366         if(!indicator){
12367             return false;
12368         }
12369         
12370         return indicator;
12371         
12372     },
12373     
12374     setDisabled : function(v)
12375     {
12376         var i  = this.inputEl().dom;
12377         if (!v) {
12378             i.removeAttribute('disabled');
12379             return;
12380             
12381         }
12382         i.setAttribute('disabled','true');
12383     },
12384     initEvents : function()
12385     {
12386           
12387         this.inputEl().on("keydown" , this.fireKey,  this);
12388         this.inputEl().on("focus", this.onFocus,  this);
12389         this.inputEl().on("blur", this.onBlur,  this);
12390         
12391         this.inputEl().relayEvent('keyup', this);
12392         this.inputEl().relayEvent('paste', this);
12393         
12394         this.indicator = this.indicatorEl();
12395         
12396         if(this.indicator){
12397             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12398         }
12399  
12400         // reference to original value for reset
12401         this.originalValue = this.getValue();
12402         //Roo.form.TextField.superclass.initEvents.call(this);
12403         if(this.validationEvent == 'keyup'){
12404             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12405             this.inputEl().on('keyup', this.filterValidation, this);
12406         }
12407         else if(this.validationEvent !== false){
12408             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12409         }
12410         
12411         if(this.selectOnFocus){
12412             this.on("focus", this.preFocus, this);
12413             
12414         }
12415         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12416             this.inputEl().on("keypress", this.filterKeys, this);
12417         } else {
12418             this.inputEl().relayEvent('keypress', this);
12419         }
12420        /* if(this.grow){
12421             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12422             this.el.on("click", this.autoSize,  this);
12423         }
12424         */
12425         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12426             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12427         }
12428         
12429         if (typeof(this.before) == 'object') {
12430             this.before.render(this.el.select('.roo-input-before',true).first());
12431         }
12432         if (typeof(this.after) == 'object') {
12433             this.after.render(this.el.select('.roo-input-after',true).first());
12434         }
12435         
12436         this.inputEl().on('change', this.onChange, this);
12437         
12438     },
12439     filterValidation : function(e){
12440         if(!e.isNavKeyPress()){
12441             this.validationTask.delay(this.validationDelay);
12442         }
12443     },
12444      /**
12445      * Validates the field value
12446      * @return {Boolean} True if the value is valid, else false
12447      */
12448     validate : function(){
12449         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12450         if(this.disabled || this.validateValue(this.getRawValue())){
12451             this.markValid();
12452             return true;
12453         }
12454         
12455         this.markInvalid();
12456         return false;
12457     },
12458     
12459     
12460     /**
12461      * Validates a value according to the field's validation rules and marks the field as invalid
12462      * if the validation fails
12463      * @param {Mixed} value The value to validate
12464      * @return {Boolean} True if the value is valid, else false
12465      */
12466     validateValue : function(value)
12467     {
12468         if(this.getVisibilityEl().hasClass('hidden')){
12469             return true;
12470         }
12471         
12472         if(value.length < 1)  { // if it's blank
12473             if(this.allowBlank){
12474                 return true;
12475             }
12476             return false;
12477         }
12478         
12479         if(value.length < this.minLength){
12480             return false;
12481         }
12482         if(value.length > this.maxLength){
12483             return false;
12484         }
12485         if(this.vtype){
12486             var vt = Roo.form.VTypes;
12487             if(!vt[this.vtype](value, this)){
12488                 return false;
12489             }
12490         }
12491         if(typeof this.validator == "function"){
12492             var msg = this.validator(value);
12493             if(msg !== true){
12494                 return false;
12495             }
12496             if (typeof(msg) == 'string') {
12497                 this.invalidText = msg;
12498             }
12499         }
12500         
12501         if(this.regex && !this.regex.test(value)){
12502             return false;
12503         }
12504         
12505         return true;
12506     },
12507     
12508      // private
12509     fireKey : function(e){
12510         //Roo.log('field ' + e.getKey());
12511         if(e.isNavKeyPress()){
12512             this.fireEvent("specialkey", this, e);
12513         }
12514     },
12515     focus : function (selectText){
12516         if(this.rendered){
12517             this.inputEl().focus();
12518             if(selectText === true){
12519                 this.inputEl().dom.select();
12520             }
12521         }
12522         return this;
12523     } ,
12524     
12525     onFocus : function(){
12526         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12527            // this.el.addClass(this.focusClass);
12528         }
12529         if(!this.hasFocus){
12530             this.hasFocus = true;
12531             this.startValue = this.getValue();
12532             this.fireEvent("focus", this);
12533         }
12534     },
12535     
12536     beforeBlur : Roo.emptyFn,
12537
12538     
12539     // private
12540     onBlur : function(){
12541         this.beforeBlur();
12542         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12543             //this.el.removeClass(this.focusClass);
12544         }
12545         this.hasFocus = false;
12546         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12547             this.validate();
12548         }
12549         var v = this.getValue();
12550         if(String(v) !== String(this.startValue)){
12551             this.fireEvent('change', this, v, this.startValue);
12552         }
12553         this.fireEvent("blur", this);
12554     },
12555     
12556     onChange : function(e)
12557     {
12558         var v = this.getValue();
12559         if(String(v) !== String(this.startValue)){
12560             this.fireEvent('change', this, v, this.startValue);
12561         }
12562         
12563     },
12564     
12565     /**
12566      * Resets the current field value to the originally loaded value and clears any validation messages
12567      */
12568     reset : function(){
12569         this.setValue(this.originalValue);
12570         this.validate();
12571     },
12572      /**
12573      * Returns the name of the field
12574      * @return {Mixed} name The name field
12575      */
12576     getName: function(){
12577         return this.name;
12578     },
12579      /**
12580      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12581      * @return {Mixed} value The field value
12582      */
12583     getValue : function(){
12584         
12585         var v = this.inputEl().getValue();
12586         
12587         return v;
12588     },
12589     /**
12590      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12591      * @return {Mixed} value The field value
12592      */
12593     getRawValue : function(){
12594         var v = this.inputEl().getValue();
12595         
12596         return v;
12597     },
12598     
12599     /**
12600      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12601      * @param {Mixed} value The value to set
12602      */
12603     setRawValue : function(v){
12604         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12605     },
12606     
12607     selectText : function(start, end){
12608         var v = this.getRawValue();
12609         if(v.length > 0){
12610             start = start === undefined ? 0 : start;
12611             end = end === undefined ? v.length : end;
12612             var d = this.inputEl().dom;
12613             if(d.setSelectionRange){
12614                 d.setSelectionRange(start, end);
12615             }else if(d.createTextRange){
12616                 var range = d.createTextRange();
12617                 range.moveStart("character", start);
12618                 range.moveEnd("character", v.length-end);
12619                 range.select();
12620             }
12621         }
12622     },
12623     
12624     /**
12625      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12626      * @param {Mixed} value The value to set
12627      */
12628     setValue : function(v){
12629         this.value = v;
12630         if(this.rendered){
12631             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12632             this.validate();
12633         }
12634     },
12635     
12636     /*
12637     processValue : function(value){
12638         if(this.stripCharsRe){
12639             var newValue = value.replace(this.stripCharsRe, '');
12640             if(newValue !== value){
12641                 this.setRawValue(newValue);
12642                 return newValue;
12643             }
12644         }
12645         return value;
12646     },
12647   */
12648     preFocus : function(){
12649         
12650         if(this.selectOnFocus){
12651             this.inputEl().dom.select();
12652         }
12653     },
12654     filterKeys : function(e){
12655         var k = e.getKey();
12656         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12657             return;
12658         }
12659         var c = e.getCharCode(), cc = String.fromCharCode(c);
12660         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12661             return;
12662         }
12663         if(!this.maskRe.test(cc)){
12664             e.stopEvent();
12665         }
12666     },
12667      /**
12668      * Clear any invalid styles/messages for this field
12669      */
12670     clearInvalid : function(){
12671         
12672         if(!this.el || this.preventMark){ // not rendered
12673             return;
12674         }
12675         
12676         
12677         this.el.removeClass([this.invalidClass, 'is-invalid']);
12678         
12679         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12680             
12681             var feedback = this.el.select('.form-control-feedback', true).first();
12682             
12683             if(feedback){
12684                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12685             }
12686             
12687         }
12688         
12689         if(this.indicator){
12690             this.indicator.removeClass('visible');
12691             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12692         }
12693         
12694         this.fireEvent('valid', this);
12695     },
12696     
12697      /**
12698      * Mark this field as valid
12699      */
12700     markValid : function()
12701     {
12702         if(!this.el  || this.preventMark){ // not rendered...
12703             return;
12704         }
12705         
12706         this.el.removeClass([this.invalidClass, this.validClass]);
12707         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12708
12709         var feedback = this.el.select('.form-control-feedback', true).first();
12710             
12711         if(feedback){
12712             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12713         }
12714         
12715         if(this.indicator){
12716             this.indicator.removeClass('visible');
12717             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12718         }
12719         
12720         if(this.disabled){
12721             return;
12722         }
12723         
12724            
12725         if(this.allowBlank && !this.getRawValue().length){
12726             return;
12727         }
12728         if (Roo.bootstrap.version == 3) {
12729             this.el.addClass(this.validClass);
12730         } else {
12731             this.inputEl().addClass('is-valid');
12732         }
12733
12734         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12735             
12736             var feedback = this.el.select('.form-control-feedback', true).first();
12737             
12738             if(feedback){
12739                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12740                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12741             }
12742             
12743         }
12744         
12745         this.fireEvent('valid', this);
12746     },
12747     
12748      /**
12749      * Mark this field as invalid
12750      * @param {String} msg The validation message
12751      */
12752     markInvalid : function(msg)
12753     {
12754         if(!this.el  || this.preventMark){ // not rendered
12755             return;
12756         }
12757         
12758         this.el.removeClass([this.invalidClass, this.validClass]);
12759         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12760         
12761         var feedback = this.el.select('.form-control-feedback', true).first();
12762             
12763         if(feedback){
12764             this.el.select('.form-control-feedback', true).first().removeClass(
12765                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12766         }
12767
12768         if(this.disabled){
12769             return;
12770         }
12771         
12772         if(this.allowBlank && !this.getRawValue().length){
12773             return;
12774         }
12775         
12776         if(this.indicator){
12777             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12778             this.indicator.addClass('visible');
12779         }
12780         if (Roo.bootstrap.version == 3) {
12781             this.el.addClass(this.invalidClass);
12782         } else {
12783             this.inputEl().addClass('is-invalid');
12784         }
12785         
12786         
12787         
12788         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12789             
12790             var feedback = this.el.select('.form-control-feedback', true).first();
12791             
12792             if(feedback){
12793                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12794                 
12795                 if(this.getValue().length || this.forceFeedback){
12796                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12797                 }
12798                 
12799             }
12800             
12801         }
12802         
12803         this.fireEvent('invalid', this, msg);
12804     },
12805     // private
12806     SafariOnKeyDown : function(event)
12807     {
12808         // this is a workaround for a password hang bug on chrome/ webkit.
12809         if (this.inputEl().dom.type != 'password') {
12810             return;
12811         }
12812         
12813         var isSelectAll = false;
12814         
12815         if(this.inputEl().dom.selectionEnd > 0){
12816             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12817         }
12818         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12819             event.preventDefault();
12820             this.setValue('');
12821             return;
12822         }
12823         
12824         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12825             
12826             event.preventDefault();
12827             // this is very hacky as keydown always get's upper case.
12828             //
12829             var cc = String.fromCharCode(event.getCharCode());
12830             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12831             
12832         }
12833     },
12834     adjustWidth : function(tag, w){
12835         tag = tag.toLowerCase();
12836         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12837             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12838                 if(tag == 'input'){
12839                     return w + 2;
12840                 }
12841                 if(tag == 'textarea'){
12842                     return w-2;
12843                 }
12844             }else if(Roo.isOpera){
12845                 if(tag == 'input'){
12846                     return w + 2;
12847                 }
12848                 if(tag == 'textarea'){
12849                     return w-2;
12850                 }
12851             }
12852         }
12853         return w;
12854     },
12855     
12856     setFieldLabel : function(v)
12857     {
12858         if(!this.rendered){
12859             return;
12860         }
12861         
12862         if(this.indicatorEl()){
12863             var ar = this.el.select('label > span',true);
12864             
12865             if (ar.elements.length) {
12866                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12867                 this.fieldLabel = v;
12868                 return;
12869             }
12870             
12871             var br = this.el.select('label',true);
12872             
12873             if(br.elements.length) {
12874                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12875                 this.fieldLabel = v;
12876                 return;
12877             }
12878             
12879             Roo.log('Cannot Found any of label > span || label in input');
12880             return;
12881         }
12882         
12883         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12884         this.fieldLabel = v;
12885         
12886         
12887     }
12888 });
12889
12890  
12891 /*
12892  * - LGPL
12893  *
12894  * Input
12895  * 
12896  */
12897
12898 /**
12899  * @class Roo.bootstrap.TextArea
12900  * @extends Roo.bootstrap.Input
12901  * Bootstrap TextArea class
12902  * @cfg {Number} cols Specifies the visible width of a text area
12903  * @cfg {Number} rows Specifies the visible number of lines in a text area
12904  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12905  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12906  * @cfg {string} html text
12907  * 
12908  * @constructor
12909  * Create a new TextArea
12910  * @param {Object} config The config object
12911  */
12912
12913 Roo.bootstrap.TextArea = function(config){
12914     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12915    
12916 };
12917
12918 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12919      
12920     cols : false,
12921     rows : 5,
12922     readOnly : false,
12923     warp : 'soft',
12924     resize : false,
12925     value: false,
12926     html: false,
12927     
12928     getAutoCreate : function(){
12929         
12930         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12931         
12932         var id = Roo.id();
12933         
12934         var cfg = {};
12935         
12936         if(this.inputType != 'hidden'){
12937             cfg.cls = 'form-group' //input-group
12938         }
12939         
12940         var input =  {
12941             tag: 'textarea',
12942             id : id,
12943             warp : this.warp,
12944             rows : this.rows,
12945             value : this.value || '',
12946             html: this.html || '',
12947             cls : 'form-control',
12948             placeholder : this.placeholder || '' 
12949             
12950         };
12951         
12952         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12953             input.maxLength = this.maxLength;
12954         }
12955         
12956         if(this.resize){
12957             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12958         }
12959         
12960         if(this.cols){
12961             input.cols = this.cols;
12962         }
12963         
12964         if (this.readOnly) {
12965             input.readonly = true;
12966         }
12967         
12968         if (this.name) {
12969             input.name = this.name;
12970         }
12971         
12972         if (this.size) {
12973             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12974         }
12975         
12976         var settings=this;
12977         ['xs','sm','md','lg'].map(function(size){
12978             if (settings[size]) {
12979                 cfg.cls += ' col-' + size + '-' + settings[size];
12980             }
12981         });
12982         
12983         var inputblock = input;
12984         
12985         if(this.hasFeedback && !this.allowBlank){
12986             
12987             var feedback = {
12988                 tag: 'span',
12989                 cls: 'glyphicon form-control-feedback'
12990             };
12991
12992             inputblock = {
12993                 cls : 'has-feedback',
12994                 cn :  [
12995                     input,
12996                     feedback
12997                 ] 
12998             };  
12999         }
13000         
13001         
13002         if (this.before || this.after) {
13003             
13004             inputblock = {
13005                 cls : 'input-group',
13006                 cn :  [] 
13007             };
13008             if (this.before) {
13009                 inputblock.cn.push({
13010                     tag :'span',
13011                     cls : 'input-group-addon',
13012                     html : this.before
13013                 });
13014             }
13015             
13016             inputblock.cn.push(input);
13017             
13018             if(this.hasFeedback && !this.allowBlank){
13019                 inputblock.cls += ' has-feedback';
13020                 inputblock.cn.push(feedback);
13021             }
13022             
13023             if (this.after) {
13024                 inputblock.cn.push({
13025                     tag :'span',
13026                     cls : 'input-group-addon',
13027                     html : this.after
13028                 });
13029             }
13030             
13031         }
13032         
13033         if (align ==='left' && this.fieldLabel.length) {
13034             cfg.cn = [
13035                 {
13036                     tag: 'label',
13037                     'for' :  id,
13038                     cls : 'control-label',
13039                     html : this.fieldLabel
13040                 },
13041                 {
13042                     cls : "",
13043                     cn: [
13044                         inputblock
13045                     ]
13046                 }
13047
13048             ];
13049             
13050             if(this.labelWidth > 12){
13051                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13052             }
13053
13054             if(this.labelWidth < 13 && this.labelmd == 0){
13055                 this.labelmd = this.labelWidth;
13056             }
13057
13058             if(this.labellg > 0){
13059                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13060                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13061             }
13062
13063             if(this.labelmd > 0){
13064                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13065                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13066             }
13067
13068             if(this.labelsm > 0){
13069                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13070                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13071             }
13072
13073             if(this.labelxs > 0){
13074                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13075                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13076             }
13077             
13078         } else if ( this.fieldLabel.length) {
13079             cfg.cn = [
13080
13081                {
13082                    tag: 'label',
13083                    //cls : 'input-group-addon',
13084                    html : this.fieldLabel
13085
13086                },
13087
13088                inputblock
13089
13090            ];
13091
13092         } else {
13093
13094             cfg.cn = [
13095
13096                 inputblock
13097
13098             ];
13099                 
13100         }
13101         
13102         if (this.disabled) {
13103             input.disabled=true;
13104         }
13105         
13106         return cfg;
13107         
13108     },
13109     /**
13110      * return the real textarea element.
13111      */
13112     inputEl: function ()
13113     {
13114         return this.el.select('textarea.form-control',true).first();
13115     },
13116     
13117     /**
13118      * Clear any invalid styles/messages for this field
13119      */
13120     clearInvalid : function()
13121     {
13122         
13123         if(!this.el || this.preventMark){ // not rendered
13124             return;
13125         }
13126         
13127         var label = this.el.select('label', true).first();
13128         var icon = this.el.select('i.fa-star', true).first();
13129         
13130         if(label && icon){
13131             icon.remove();
13132         }
13133         this.el.removeClass( this.validClass);
13134         this.inputEl().removeClass('is-invalid');
13135          
13136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13137             
13138             var feedback = this.el.select('.form-control-feedback', true).first();
13139             
13140             if(feedback){
13141                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13142             }
13143             
13144         }
13145         
13146         this.fireEvent('valid', this);
13147     },
13148     
13149      /**
13150      * Mark this field as valid
13151      */
13152     markValid : function()
13153     {
13154         if(!this.el  || this.preventMark){ // not rendered
13155             return;
13156         }
13157         
13158         this.el.removeClass([this.invalidClass, this.validClass]);
13159         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13160         
13161         var feedback = this.el.select('.form-control-feedback', true).first();
13162             
13163         if(feedback){
13164             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13165         }
13166
13167         if(this.disabled || this.allowBlank){
13168             return;
13169         }
13170         
13171         var label = this.el.select('label', true).first();
13172         var icon = this.el.select('i.fa-star', true).first();
13173         
13174         if(label && icon){
13175             icon.remove();
13176         }
13177         if (Roo.bootstrap.version == 3) {
13178             this.el.addClass(this.validClass);
13179         } else {
13180             this.inputEl().addClass('is-valid');
13181         }
13182         
13183         
13184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13185             
13186             var feedback = this.el.select('.form-control-feedback', true).first();
13187             
13188             if(feedback){
13189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13191             }
13192             
13193         }
13194         
13195         this.fireEvent('valid', this);
13196     },
13197     
13198      /**
13199      * Mark this field as invalid
13200      * @param {String} msg The validation message
13201      */
13202     markInvalid : function(msg)
13203     {
13204         if(!this.el  || this.preventMark){ // not rendered
13205             return;
13206         }
13207         
13208         this.el.removeClass([this.invalidClass, this.validClass]);
13209         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13210         
13211         var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213         if(feedback){
13214             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13215         }
13216
13217         if(this.disabled || this.allowBlank){
13218             return;
13219         }
13220         
13221         var label = this.el.select('label', true).first();
13222         var icon = this.el.select('i.fa-star', true).first();
13223         
13224         if(!this.getValue().length && label && !icon){
13225             this.el.createChild({
13226                 tag : 'i',
13227                 cls : 'text-danger fa fa-lg fa-star',
13228                 tooltip : 'This field is required',
13229                 style : 'margin-right:5px;'
13230             }, label, true);
13231         }
13232         
13233         if (Roo.bootstrap.version == 3) {
13234             this.el.addClass(this.invalidClass);
13235         } else {
13236             this.inputEl().addClass('is-invalid');
13237         }
13238         
13239         // fixme ... this may be depricated need to test..
13240         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13241             
13242             var feedback = this.el.select('.form-control-feedback', true).first();
13243             
13244             if(feedback){
13245                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13246                 
13247                 if(this.getValue().length || this.forceFeedback){
13248                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13249                 }
13250                 
13251             }
13252             
13253         }
13254         
13255         this.fireEvent('invalid', this, msg);
13256     }
13257 });
13258
13259  
13260 /*
13261  * - LGPL
13262  *
13263  * trigger field - base class for combo..
13264  * 
13265  */
13266  
13267 /**
13268  * @class Roo.bootstrap.TriggerField
13269  * @extends Roo.bootstrap.Input
13270  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13271  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13272  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13273  * for which you can provide a custom implementation.  For example:
13274  * <pre><code>
13275 var trigger = new Roo.bootstrap.TriggerField();
13276 trigger.onTriggerClick = myTriggerFn;
13277 trigger.applyTo('my-field');
13278 </code></pre>
13279  *
13280  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13281  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13282  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13283  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13284  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13285
13286  * @constructor
13287  * Create a new TriggerField.
13288  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13289  * to the base TextField)
13290  */
13291 Roo.bootstrap.TriggerField = function(config){
13292     this.mimicing = false;
13293     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13294 };
13295
13296 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13297     /**
13298      * @cfg {String} triggerClass A CSS class to apply to the trigger
13299      */
13300      /**
13301      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13302      */
13303     hideTrigger:false,
13304
13305     /**
13306      * @cfg {Boolean} removable (true|false) special filter default false
13307      */
13308     removable : false,
13309     
13310     /** @cfg {Boolean} grow @hide */
13311     /** @cfg {Number} growMin @hide */
13312     /** @cfg {Number} growMax @hide */
13313
13314     /**
13315      * @hide 
13316      * @method
13317      */
13318     autoSize: Roo.emptyFn,
13319     // private
13320     monitorTab : true,
13321     // private
13322     deferHeight : true,
13323
13324     
13325     actionMode : 'wrap',
13326     
13327     caret : false,
13328     
13329     
13330     getAutoCreate : function(){
13331        
13332         var align = this.labelAlign || this.parentLabelAlign();
13333         
13334         var id = Roo.id();
13335         
13336         var cfg = {
13337             cls: 'form-group' //input-group
13338         };
13339         
13340         
13341         var input =  {
13342             tag: 'input',
13343             id : id,
13344             type : this.inputType,
13345             cls : 'form-control',
13346             autocomplete: 'new-password',
13347             placeholder : this.placeholder || '' 
13348             
13349         };
13350         if (this.name) {
13351             input.name = this.name;
13352         }
13353         if (this.size) {
13354             input.cls += ' input-' + this.size;
13355         }
13356         
13357         if (this.disabled) {
13358             input.disabled=true;
13359         }
13360         
13361         var inputblock = input;
13362         
13363         if(this.hasFeedback && !this.allowBlank){
13364             
13365             var feedback = {
13366                 tag: 'span',
13367                 cls: 'glyphicon form-control-feedback'
13368             };
13369             
13370             if(this.removable && !this.editable  ){
13371                 inputblock = {
13372                     cls : 'has-feedback',
13373                     cn :  [
13374                         inputblock,
13375                         {
13376                             tag: 'button',
13377                             html : 'x',
13378                             cls : 'roo-combo-removable-btn close'
13379                         },
13380                         feedback
13381                     ] 
13382                 };
13383             } else {
13384                 inputblock = {
13385                     cls : 'has-feedback',
13386                     cn :  [
13387                         inputblock,
13388                         feedback
13389                     ] 
13390                 };
13391             }
13392
13393         } else {
13394             if(this.removable && !this.editable ){
13395                 inputblock = {
13396                     cls : 'roo-removable',
13397                     cn :  [
13398                         inputblock,
13399                         {
13400                             tag: 'button',
13401                             html : 'x',
13402                             cls : 'roo-combo-removable-btn close'
13403                         }
13404                     ] 
13405                 };
13406             }
13407         }
13408         
13409         if (this.before || this.after) {
13410             
13411             inputblock = {
13412                 cls : 'input-group',
13413                 cn :  [] 
13414             };
13415             if (this.before) {
13416                 inputblock.cn.push({
13417                     tag :'span',
13418                     cls : 'input-group-addon input-group-prepend input-group-text',
13419                     html : this.before
13420                 });
13421             }
13422             
13423             inputblock.cn.push(input);
13424             
13425             if(this.hasFeedback && !this.allowBlank){
13426                 inputblock.cls += ' has-feedback';
13427                 inputblock.cn.push(feedback);
13428             }
13429             
13430             if (this.after) {
13431                 inputblock.cn.push({
13432                     tag :'span',
13433                     cls : 'input-group-addon input-group-append input-group-text',
13434                     html : this.after
13435                 });
13436             }
13437             
13438         };
13439         
13440       
13441         
13442         var ibwrap = inputblock;
13443         
13444         if(this.multiple){
13445             ibwrap = {
13446                 tag: 'ul',
13447                 cls: 'roo-select2-choices',
13448                 cn:[
13449                     {
13450                         tag: 'li',
13451                         cls: 'roo-select2-search-field',
13452                         cn: [
13453
13454                             inputblock
13455                         ]
13456                     }
13457                 ]
13458             };
13459                 
13460         }
13461         
13462         var combobox = {
13463             cls: 'roo-select2-container input-group',
13464             cn: [
13465                  {
13466                     tag: 'input',
13467                     type : 'hidden',
13468                     cls: 'form-hidden-field'
13469                 },
13470                 ibwrap
13471             ]
13472         };
13473         
13474         if(!this.multiple && this.showToggleBtn){
13475             
13476             var caret = {
13477                         tag: 'span',
13478                         cls: 'caret'
13479              };
13480             if (this.caret != false) {
13481                 caret = {
13482                      tag: 'i',
13483                      cls: 'fa fa-' + this.caret
13484                 };
13485                 
13486             }
13487             
13488             combobox.cn.push({
13489                 tag :'span',
13490                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13491                 cn : [
13492                     Roo.bootstrap.version == 3 ? caret : '',
13493                     {
13494                         tag: 'span',
13495                         cls: 'combobox-clear',
13496                         cn  : [
13497                             {
13498                                 tag : 'i',
13499                                 cls: 'icon-remove'
13500                             }
13501                         ]
13502                     }
13503                 ]
13504
13505             })
13506         }
13507         
13508         if(this.multiple){
13509             combobox.cls += ' roo-select2-container-multi';
13510         }
13511          var indicator = {
13512             tag : 'i',
13513             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13514             tooltip : 'This field is required'
13515         };
13516         if (Roo.bootstrap.version == 4) {
13517             indicator = {
13518                 tag : 'i',
13519                 style : 'display:none'
13520             };
13521         }
13522         
13523         
13524         if (align ==='left' && this.fieldLabel.length) {
13525             
13526             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13527
13528             cfg.cn = [
13529                 indicator,
13530                 {
13531                     tag: 'label',
13532                     'for' :  id,
13533                     cls : 'control-label',
13534                     html : this.fieldLabel
13535
13536                 },
13537                 {
13538                     cls : "", 
13539                     cn: [
13540                         combobox
13541                     ]
13542                 }
13543
13544             ];
13545             
13546             var labelCfg = cfg.cn[1];
13547             var contentCfg = cfg.cn[2];
13548             
13549             if(this.indicatorpos == 'right'){
13550                 cfg.cn = [
13551                     {
13552                         tag: 'label',
13553                         'for' :  id,
13554                         cls : 'control-label',
13555                         cn : [
13556                             {
13557                                 tag : 'span',
13558                                 html : this.fieldLabel
13559                             },
13560                             indicator
13561                         ]
13562                     },
13563                     {
13564                         cls : "", 
13565                         cn: [
13566                             combobox
13567                         ]
13568                     }
13569
13570                 ];
13571                 
13572                 labelCfg = cfg.cn[0];
13573                 contentCfg = cfg.cn[1];
13574             }
13575             
13576             if(this.labelWidth > 12){
13577                 labelCfg.style = "width: " + this.labelWidth + 'px';
13578             }
13579             
13580             if(this.labelWidth < 13 && this.labelmd == 0){
13581                 this.labelmd = this.labelWidth;
13582             }
13583             
13584             if(this.labellg > 0){
13585                 labelCfg.cls += ' col-lg-' + this.labellg;
13586                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13587             }
13588             
13589             if(this.labelmd > 0){
13590                 labelCfg.cls += ' col-md-' + this.labelmd;
13591                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13592             }
13593             
13594             if(this.labelsm > 0){
13595                 labelCfg.cls += ' col-sm-' + this.labelsm;
13596                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13597             }
13598             
13599             if(this.labelxs > 0){
13600                 labelCfg.cls += ' col-xs-' + this.labelxs;
13601                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13602             }
13603             
13604         } else if ( this.fieldLabel.length) {
13605 //                Roo.log(" label");
13606             cfg.cn = [
13607                 indicator,
13608                {
13609                    tag: 'label',
13610                    //cls : 'input-group-addon',
13611                    html : this.fieldLabel
13612
13613                },
13614
13615                combobox
13616
13617             ];
13618             
13619             if(this.indicatorpos == 'right'){
13620                 
13621                 cfg.cn = [
13622                     {
13623                        tag: 'label',
13624                        cn : [
13625                            {
13626                                tag : 'span',
13627                                html : this.fieldLabel
13628                            },
13629                            indicator
13630                        ]
13631
13632                     },
13633                     combobox
13634
13635                 ];
13636
13637             }
13638
13639         } else {
13640             
13641 //                Roo.log(" no label && no align");
13642                 cfg = combobox
13643                      
13644                 
13645         }
13646         
13647         var settings=this;
13648         ['xs','sm','md','lg'].map(function(size){
13649             if (settings[size]) {
13650                 cfg.cls += ' col-' + size + '-' + settings[size];
13651             }
13652         });
13653         
13654         return cfg;
13655         
13656     },
13657     
13658     
13659     
13660     // private
13661     onResize : function(w, h){
13662 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13663 //        if(typeof w == 'number'){
13664 //            var x = w - this.trigger.getWidth();
13665 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13666 //            this.trigger.setStyle('left', x+'px');
13667 //        }
13668     },
13669
13670     // private
13671     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13672
13673     // private
13674     getResizeEl : function(){
13675         return this.inputEl();
13676     },
13677
13678     // private
13679     getPositionEl : function(){
13680         return this.inputEl();
13681     },
13682
13683     // private
13684     alignErrorIcon : function(){
13685         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13686     },
13687
13688     // private
13689     initEvents : function(){
13690         
13691         this.createList();
13692         
13693         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13694         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13695         if(!this.multiple && this.showToggleBtn){
13696             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13697             if(this.hideTrigger){
13698                 this.trigger.setDisplayed(false);
13699             }
13700             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13701         }
13702         
13703         if(this.multiple){
13704             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13705         }
13706         
13707         if(this.removable && !this.editable && !this.tickable){
13708             var close = this.closeTriggerEl();
13709             
13710             if(close){
13711                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13712                 close.on('click', this.removeBtnClick, this, close);
13713             }
13714         }
13715         
13716         //this.trigger.addClassOnOver('x-form-trigger-over');
13717         //this.trigger.addClassOnClick('x-form-trigger-click');
13718         
13719         //if(!this.width){
13720         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13721         //}
13722     },
13723     
13724     closeTriggerEl : function()
13725     {
13726         var close = this.el.select('.roo-combo-removable-btn', true).first();
13727         return close ? close : false;
13728     },
13729     
13730     removeBtnClick : function(e, h, el)
13731     {
13732         e.preventDefault();
13733         
13734         if(this.fireEvent("remove", this) !== false){
13735             this.reset();
13736             this.fireEvent("afterremove", this)
13737         }
13738     },
13739     
13740     createList : function()
13741     {
13742         this.list = Roo.get(document.body).createChild({
13743             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13744             cls: 'typeahead typeahead-long dropdown-menu shadow',
13745             style: 'display:none'
13746         });
13747         
13748         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13749         
13750     },
13751
13752     // private
13753     initTrigger : function(){
13754        
13755     },
13756
13757     // private
13758     onDestroy : function(){
13759         if(this.trigger){
13760             this.trigger.removeAllListeners();
13761           //  this.trigger.remove();
13762         }
13763         //if(this.wrap){
13764         //    this.wrap.remove();
13765         //}
13766         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13767     },
13768
13769     // private
13770     onFocus : function(){
13771         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13772         /*
13773         if(!this.mimicing){
13774             this.wrap.addClass('x-trigger-wrap-focus');
13775             this.mimicing = true;
13776             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13777             if(this.monitorTab){
13778                 this.el.on("keydown", this.checkTab, this);
13779             }
13780         }
13781         */
13782     },
13783
13784     // private
13785     checkTab : function(e){
13786         if(e.getKey() == e.TAB){
13787             this.triggerBlur();
13788         }
13789     },
13790
13791     // private
13792     onBlur : function(){
13793         // do nothing
13794     },
13795
13796     // private
13797     mimicBlur : function(e, t){
13798         /*
13799         if(!this.wrap.contains(t) && this.validateBlur()){
13800             this.triggerBlur();
13801         }
13802         */
13803     },
13804
13805     // private
13806     triggerBlur : function(){
13807         this.mimicing = false;
13808         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13809         if(this.monitorTab){
13810             this.el.un("keydown", this.checkTab, this);
13811         }
13812         //this.wrap.removeClass('x-trigger-wrap-focus');
13813         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13814     },
13815
13816     // private
13817     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13818     validateBlur : function(e, t){
13819         return true;
13820     },
13821
13822     // private
13823     onDisable : function(){
13824         this.inputEl().dom.disabled = true;
13825         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13826         //if(this.wrap){
13827         //    this.wrap.addClass('x-item-disabled');
13828         //}
13829     },
13830
13831     // private
13832     onEnable : function(){
13833         this.inputEl().dom.disabled = false;
13834         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13835         //if(this.wrap){
13836         //    this.el.removeClass('x-item-disabled');
13837         //}
13838     },
13839
13840     // private
13841     onShow : function(){
13842         var ae = this.getActionEl();
13843         
13844         if(ae){
13845             ae.dom.style.display = '';
13846             ae.dom.style.visibility = 'visible';
13847         }
13848     },
13849
13850     // private
13851     
13852     onHide : function(){
13853         var ae = this.getActionEl();
13854         ae.dom.style.display = 'none';
13855     },
13856
13857     /**
13858      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13859      * by an implementing function.
13860      * @method
13861      * @param {EventObject} e
13862      */
13863     onTriggerClick : Roo.emptyFn
13864 });
13865  
13866 /*
13867 * Licence: LGPL
13868 */
13869
13870 /**
13871  * @class Roo.bootstrap.CardUploader
13872  * @extends Roo.bootstrap.Button
13873  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13874  * @cfg {Number} errorTimeout default 3000
13875  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13876  * @cfg {Array}  html The button text.
13877
13878  *
13879  * @constructor
13880  * Create a new CardUploader
13881  * @param {Object} config The config object
13882  */
13883
13884 Roo.bootstrap.CardUploader = function(config){
13885     
13886  
13887     
13888     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13889     
13890     
13891     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13892         return r.data.id
13893      });
13894     
13895      this.addEvents({
13896          // raw events
13897         /**
13898          * @event preview
13899          * When a image is clicked on - and needs to display a slideshow or similar..
13900          * @param {Roo.bootstrap.Card} this
13901          * @param {Object} The image information data 
13902          *
13903          */
13904         'preview' : true,
13905          /**
13906          * @event download
13907          * When a the download link is clicked
13908          * @param {Roo.bootstrap.Card} this
13909          * @param {Object} The image information data  contains 
13910          */
13911         'download' : true
13912         
13913     });
13914 };
13915  
13916 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13917     
13918      
13919     errorTimeout : 3000,
13920      
13921     images : false,
13922    
13923     fileCollection : false,
13924     allowBlank : true,
13925     
13926     getAutoCreate : function()
13927     {
13928         
13929         var cfg =  {
13930             cls :'form-group' ,
13931             cn : [
13932                
13933                 {
13934                     tag: 'label',
13935                    //cls : 'input-group-addon',
13936                     html : this.fieldLabel
13937
13938                 },
13939
13940                 {
13941                     tag: 'input',
13942                     type : 'hidden',
13943                     name : this.name,
13944                     value : this.value,
13945                     cls : 'd-none  form-control'
13946                 },
13947                 
13948                 {
13949                     tag: 'input',
13950                     multiple : 'multiple',
13951                     type : 'file',
13952                     cls : 'd-none  roo-card-upload-selector'
13953                 },
13954                 
13955                 {
13956                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13957                 },
13958                 {
13959                     cls : 'card-columns roo-card-uploader-container'
13960                 }
13961
13962             ]
13963         };
13964            
13965          
13966         return cfg;
13967     },
13968     
13969     getChildContainer : function() /// what children are added to.
13970     {
13971         return this.containerEl;
13972     },
13973    
13974     getButtonContainer : function() /// what children are added to.
13975     {
13976         return this.el.select(".roo-card-uploader-button-container").first();
13977     },
13978    
13979     initEvents : function()
13980     {
13981         
13982         Roo.bootstrap.Input.prototype.initEvents.call(this);
13983         
13984         var t = this;
13985         this.addxtype({
13986             xns: Roo.bootstrap,
13987
13988             xtype : 'Button',
13989             container_method : 'getButtonContainer' ,            
13990             html :  this.html, // fix changable?
13991             cls : 'w-100 ',
13992             listeners : {
13993                 'click' : function(btn, e) {
13994                     t.onClick(e);
13995                 }
13996             }
13997         });
13998         
13999         
14000         
14001         
14002         this.urlAPI = (window.createObjectURL && window) || 
14003                                 (window.URL && URL.revokeObjectURL && URL) || 
14004                                 (window.webkitURL && webkitURL);
14005                         
14006          
14007          
14008          
14009         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14010         
14011         this.selectorEl.on('change', this.onFileSelected, this);
14012         if (this.images) {
14013             var t = this;
14014             this.images.forEach(function(img) {
14015                 t.addCard(img)
14016             });
14017             this.images = false;
14018         }
14019         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14020          
14021        
14022     },
14023     
14024    
14025     onClick : function(e)
14026     {
14027         e.preventDefault();
14028          
14029         this.selectorEl.dom.click();
14030          
14031     },
14032     
14033     onFileSelected : function(e)
14034     {
14035         e.preventDefault();
14036         
14037         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14038             return;
14039         }
14040         
14041         Roo.each(this.selectorEl.dom.files, function(file){    
14042             this.addFile(file);
14043         }, this);
14044          
14045     },
14046     
14047       
14048     
14049       
14050     
14051     addFile : function(file)
14052     {
14053            
14054         if(typeof(file) === 'string'){
14055             throw "Add file by name?"; // should not happen
14056             return;
14057         }
14058         
14059         if(!file || !this.urlAPI){
14060             return;
14061         }
14062         
14063         // file;
14064         // file.type;
14065         
14066         var _this = this;
14067         
14068         
14069         var url = _this.urlAPI.createObjectURL( file);
14070            
14071         this.addCard({
14072             id : Roo.bootstrap.CardUploader.ID--,
14073             is_uploaded : false,
14074             src : url,
14075             srcfile : file,
14076             title : file.name,
14077             mimetype : file.type,
14078             preview : false,
14079             is_deleted : 0
14080         });
14081         
14082     },
14083     
14084     /**
14085      * addCard - add an Attachment to the uploader
14086      * @param data - the data about the image to upload
14087      *
14088      * {
14089           id : 123
14090           title : "Title of file",
14091           is_uploaded : false,
14092           src : "http://.....",
14093           srcfile : { the File upload object },
14094           mimetype : file.type,
14095           preview : false,
14096           is_deleted : 0
14097           .. any other data...
14098         }
14099      *
14100      * 
14101     */
14102     
14103     addCard : function (data)
14104     {
14105         // hidden input element?
14106         // if the file is not an image...
14107         //then we need to use something other that and header_image
14108         var t = this;
14109         //   remove.....
14110         var footer = [
14111             {
14112                 xns : Roo.bootstrap,
14113                 xtype : 'CardFooter',
14114                  items: [
14115                     {
14116                         xns : Roo.bootstrap,
14117                         xtype : 'Element',
14118                         cls : 'd-flex',
14119                         items : [
14120                             
14121                             {
14122                                 xns : Roo.bootstrap,
14123                                 xtype : 'Button',
14124                                 html : String.format("<small>{0}</small>", data.title),
14125                                 cls : 'col-10 text-left',
14126                                 size: 'sm',
14127                                 weight: 'link',
14128                                 fa : 'download',
14129                                 listeners : {
14130                                     click : function() {
14131                                      
14132                                         t.fireEvent( "download", t, data );
14133                                     }
14134                                 }
14135                             },
14136                           
14137                             {
14138                                 xns : Roo.bootstrap,
14139                                 xtype : 'Button',
14140                                 style: 'max-height: 28px; ',
14141                                 size : 'sm',
14142                                 weight: 'danger',
14143                                 cls : 'col-2',
14144                                 fa : 'times',
14145                                 listeners : {
14146                                     click : function() {
14147                                         t.removeCard(data.id)
14148                                     }
14149                                 }
14150                             }
14151                         ]
14152                     }
14153                     
14154                 ] 
14155             }
14156             
14157         ];
14158         
14159         var cn = this.addxtype(
14160             {
14161                  
14162                 xns : Roo.bootstrap,
14163                 xtype : 'Card',
14164                 closeable : true,
14165                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14166                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14167                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14168                 data : data,
14169                 html : false,
14170                  
14171                 items : footer,
14172                 initEvents : function() {
14173                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14174                     var card = this;
14175                     this.imgEl = this.el.select('.card-img-top').first();
14176                     if (this.imgEl) {
14177                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14178                         this.imgEl.set({ 'pointer' : 'cursor' });
14179                                   
14180                     }
14181                     this.getCardFooter().addClass('p-1');
14182                     
14183                   
14184                 }
14185                 
14186             }
14187         );
14188         // dont' really need ot update items.
14189         // this.items.push(cn);
14190         this.fileCollection.add(cn);
14191         
14192         if (!data.srcfile) {
14193             this.updateInput();
14194             return;
14195         }
14196             
14197         var _t = this;
14198         var reader = new FileReader();
14199         reader.addEventListener("load", function() {  
14200             data.srcdata =  reader.result;
14201             _t.updateInput();
14202         });
14203         reader.readAsDataURL(data.srcfile);
14204         
14205         
14206         
14207     },
14208     removeCard : function(id)
14209     {
14210         
14211         var card  = this.fileCollection.get(id);
14212         card.data.is_deleted = 1;
14213         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14214         //this.fileCollection.remove(card);
14215         //this.items = this.items.filter(function(e) { return e != card });
14216         // dont' really need ot update items.
14217         card.el.dom.parentNode.removeChild(card.el.dom);
14218         this.updateInput();
14219
14220         
14221     },
14222     reset: function()
14223     {
14224         this.fileCollection.each(function(card) {
14225             if (card.el.dom && card.el.dom.parentNode) {
14226                 card.el.dom.parentNode.removeChild(card.el.dom);
14227             }
14228         });
14229         this.fileCollection.clear();
14230         this.updateInput();
14231     },
14232     
14233     updateInput : function()
14234     {
14235          var data = [];
14236         this.fileCollection.each(function(e) {
14237             data.push(e.data);
14238             
14239         });
14240         this.inputEl().dom.value = JSON.stringify(data);
14241         
14242         
14243         
14244     }
14245     
14246     
14247 });
14248
14249
14250 Roo.bootstrap.CardUploader.ID = -1;/*
14251  * Based on:
14252  * Ext JS Library 1.1.1
14253  * Copyright(c) 2006-2007, Ext JS, LLC.
14254  *
14255  * Originally Released Under LGPL - original licence link has changed is not relivant.
14256  *
14257  * Fork - LGPL
14258  * <script type="text/javascript">
14259  */
14260
14261
14262 /**
14263  * @class Roo.data.SortTypes
14264  * @singleton
14265  * Defines the default sorting (casting?) comparison functions used when sorting data.
14266  */
14267 Roo.data.SortTypes = {
14268     /**
14269      * Default sort that does nothing
14270      * @param {Mixed} s The value being converted
14271      * @return {Mixed} The comparison value
14272      */
14273     none : function(s){
14274         return s;
14275     },
14276     
14277     /**
14278      * The regular expression used to strip tags
14279      * @type {RegExp}
14280      * @property
14281      */
14282     stripTagsRE : /<\/?[^>]+>/gi,
14283     
14284     /**
14285      * Strips all HTML tags to sort on text only
14286      * @param {Mixed} s The value being converted
14287      * @return {String} The comparison value
14288      */
14289     asText : function(s){
14290         return String(s).replace(this.stripTagsRE, "");
14291     },
14292     
14293     /**
14294      * Strips all HTML tags to sort on text only - Case insensitive
14295      * @param {Mixed} s The value being converted
14296      * @return {String} The comparison value
14297      */
14298     asUCText : function(s){
14299         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14300     },
14301     
14302     /**
14303      * Case insensitive string
14304      * @param {Mixed} s The value being converted
14305      * @return {String} The comparison value
14306      */
14307     asUCString : function(s) {
14308         return String(s).toUpperCase();
14309     },
14310     
14311     /**
14312      * Date sorting
14313      * @param {Mixed} s The value being converted
14314      * @return {Number} The comparison value
14315      */
14316     asDate : function(s) {
14317         if(!s){
14318             return 0;
14319         }
14320         if(s instanceof Date){
14321             return s.getTime();
14322         }
14323         return Date.parse(String(s));
14324     },
14325     
14326     /**
14327      * Float sorting
14328      * @param {Mixed} s The value being converted
14329      * @return {Float} The comparison value
14330      */
14331     asFloat : function(s) {
14332         var val = parseFloat(String(s).replace(/,/g, ""));
14333         if(isNaN(val)) {
14334             val = 0;
14335         }
14336         return val;
14337     },
14338     
14339     /**
14340      * Integer sorting
14341      * @param {Mixed} s The value being converted
14342      * @return {Number} The comparison value
14343      */
14344     asInt : function(s) {
14345         var val = parseInt(String(s).replace(/,/g, ""));
14346         if(isNaN(val)) {
14347             val = 0;
14348         }
14349         return val;
14350     }
14351 };/*
14352  * Based on:
14353  * Ext JS Library 1.1.1
14354  * Copyright(c) 2006-2007, Ext JS, LLC.
14355  *
14356  * Originally Released Under LGPL - original licence link has changed is not relivant.
14357  *
14358  * Fork - LGPL
14359  * <script type="text/javascript">
14360  */
14361
14362 /**
14363 * @class Roo.data.Record
14364  * Instances of this class encapsulate both record <em>definition</em> information, and record
14365  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14366  * to access Records cached in an {@link Roo.data.Store} object.<br>
14367  * <p>
14368  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14369  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14370  * objects.<br>
14371  * <p>
14372  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14373  * @constructor
14374  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14375  * {@link #create}. The parameters are the same.
14376  * @param {Array} data An associative Array of data values keyed by the field name.
14377  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14378  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14379  * not specified an integer id is generated.
14380  */
14381 Roo.data.Record = function(data, id){
14382     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14383     this.data = data;
14384 };
14385
14386 /**
14387  * Generate a constructor for a specific record layout.
14388  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14389  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14390  * Each field definition object may contain the following properties: <ul>
14391  * <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,
14392  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14393  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14394  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14395  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14396  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14397  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14398  * this may be omitted.</p></li>
14399  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14400  * <ul><li>auto (Default, implies no conversion)</li>
14401  * <li>string</li>
14402  * <li>int</li>
14403  * <li>float</li>
14404  * <li>boolean</li>
14405  * <li>date</li></ul></p></li>
14406  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14407  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14408  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14409  * by the Reader into an object that will be stored in the Record. It is passed the
14410  * following parameters:<ul>
14411  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14412  * </ul></p></li>
14413  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14414  * </ul>
14415  * <br>usage:<br><pre><code>
14416 var TopicRecord = Roo.data.Record.create(
14417     {name: 'title', mapping: 'topic_title'},
14418     {name: 'author', mapping: 'username'},
14419     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14420     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14421     {name: 'lastPoster', mapping: 'user2'},
14422     {name: 'excerpt', mapping: 'post_text'}
14423 );
14424
14425 var myNewRecord = new TopicRecord({
14426     title: 'Do my job please',
14427     author: 'noobie',
14428     totalPosts: 1,
14429     lastPost: new Date(),
14430     lastPoster: 'Animal',
14431     excerpt: 'No way dude!'
14432 });
14433 myStore.add(myNewRecord);
14434 </code></pre>
14435  * @method create
14436  * @static
14437  */
14438 Roo.data.Record.create = function(o){
14439     var f = function(){
14440         f.superclass.constructor.apply(this, arguments);
14441     };
14442     Roo.extend(f, Roo.data.Record);
14443     var p = f.prototype;
14444     p.fields = new Roo.util.MixedCollection(false, function(field){
14445         return field.name;
14446     });
14447     for(var i = 0, len = o.length; i < len; i++){
14448         p.fields.add(new Roo.data.Field(o[i]));
14449     }
14450     f.getField = function(name){
14451         return p.fields.get(name);  
14452     };
14453     return f;
14454 };
14455
14456 Roo.data.Record.AUTO_ID = 1000;
14457 Roo.data.Record.EDIT = 'edit';
14458 Roo.data.Record.REJECT = 'reject';
14459 Roo.data.Record.COMMIT = 'commit';
14460
14461 Roo.data.Record.prototype = {
14462     /**
14463      * Readonly flag - true if this record has been modified.
14464      * @type Boolean
14465      */
14466     dirty : false,
14467     editing : false,
14468     error: null,
14469     modified: null,
14470
14471     // private
14472     join : function(store){
14473         this.store = store;
14474     },
14475
14476     /**
14477      * Set the named field to the specified value.
14478      * @param {String} name The name of the field to set.
14479      * @param {Object} value The value to set the field to.
14480      */
14481     set : function(name, value){
14482         if(this.data[name] == value){
14483             return;
14484         }
14485         this.dirty = true;
14486         if(!this.modified){
14487             this.modified = {};
14488         }
14489         if(typeof this.modified[name] == 'undefined'){
14490             this.modified[name] = this.data[name];
14491         }
14492         this.data[name] = value;
14493         if(!this.editing && this.store){
14494             this.store.afterEdit(this);
14495         }       
14496     },
14497
14498     /**
14499      * Get the value of the named field.
14500      * @param {String} name The name of the field to get the value of.
14501      * @return {Object} The value of the field.
14502      */
14503     get : function(name){
14504         return this.data[name]; 
14505     },
14506
14507     // private
14508     beginEdit : function(){
14509         this.editing = true;
14510         this.modified = {}; 
14511     },
14512
14513     // private
14514     cancelEdit : function(){
14515         this.editing = false;
14516         delete this.modified;
14517     },
14518
14519     // private
14520     endEdit : function(){
14521         this.editing = false;
14522         if(this.dirty && this.store){
14523             this.store.afterEdit(this);
14524         }
14525     },
14526
14527     /**
14528      * Usually called by the {@link Roo.data.Store} which owns the Record.
14529      * Rejects all changes made to the Record since either creation, or the last commit operation.
14530      * Modified fields are reverted to their original values.
14531      * <p>
14532      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14533      * of reject operations.
14534      */
14535     reject : function(){
14536         var m = this.modified;
14537         for(var n in m){
14538             if(typeof m[n] != "function"){
14539                 this.data[n] = m[n];
14540             }
14541         }
14542         this.dirty = false;
14543         delete this.modified;
14544         this.editing = false;
14545         if(this.store){
14546             this.store.afterReject(this);
14547         }
14548     },
14549
14550     /**
14551      * Usually called by the {@link Roo.data.Store} which owns the Record.
14552      * Commits all changes made to the Record since either creation, or the last commit operation.
14553      * <p>
14554      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14555      * of commit operations.
14556      */
14557     commit : function(){
14558         this.dirty = false;
14559         delete this.modified;
14560         this.editing = false;
14561         if(this.store){
14562             this.store.afterCommit(this);
14563         }
14564     },
14565
14566     // private
14567     hasError : function(){
14568         return this.error != null;
14569     },
14570
14571     // private
14572     clearError : function(){
14573         this.error = null;
14574     },
14575
14576     /**
14577      * Creates a copy of this record.
14578      * @param {String} id (optional) A new record id if you don't want to use this record's id
14579      * @return {Record}
14580      */
14581     copy : function(newId) {
14582         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14583     }
14584 };/*
14585  * Based on:
14586  * Ext JS Library 1.1.1
14587  * Copyright(c) 2006-2007, Ext JS, LLC.
14588  *
14589  * Originally Released Under LGPL - original licence link has changed is not relivant.
14590  *
14591  * Fork - LGPL
14592  * <script type="text/javascript">
14593  */
14594
14595
14596
14597 /**
14598  * @class Roo.data.Store
14599  * @extends Roo.util.Observable
14600  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14601  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14602  * <p>
14603  * 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
14604  * has no knowledge of the format of the data returned by the Proxy.<br>
14605  * <p>
14606  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14607  * instances from the data object. These records are cached and made available through accessor functions.
14608  * @constructor
14609  * Creates a new Store.
14610  * @param {Object} config A config object containing the objects needed for the Store to access data,
14611  * and read the data into Records.
14612  */
14613 Roo.data.Store = function(config){
14614     this.data = new Roo.util.MixedCollection(false);
14615     this.data.getKey = function(o){
14616         return o.id;
14617     };
14618     this.baseParams = {};
14619     // private
14620     this.paramNames = {
14621         "start" : "start",
14622         "limit" : "limit",
14623         "sort" : "sort",
14624         "dir" : "dir",
14625         "multisort" : "_multisort"
14626     };
14627
14628     if(config && config.data){
14629         this.inlineData = config.data;
14630         delete config.data;
14631     }
14632
14633     Roo.apply(this, config);
14634     
14635     if(this.reader){ // reader passed
14636         this.reader = Roo.factory(this.reader, Roo.data);
14637         this.reader.xmodule = this.xmodule || false;
14638         if(!this.recordType){
14639             this.recordType = this.reader.recordType;
14640         }
14641         if(this.reader.onMetaChange){
14642             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14643         }
14644     }
14645
14646     if(this.recordType){
14647         this.fields = this.recordType.prototype.fields;
14648     }
14649     this.modified = [];
14650
14651     this.addEvents({
14652         /**
14653          * @event datachanged
14654          * Fires when the data cache has changed, and a widget which is using this Store
14655          * as a Record cache should refresh its view.
14656          * @param {Store} this
14657          */
14658         datachanged : true,
14659         /**
14660          * @event metachange
14661          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14662          * @param {Store} this
14663          * @param {Object} meta The JSON metadata
14664          */
14665         metachange : true,
14666         /**
14667          * @event add
14668          * Fires when Records have been added to the Store
14669          * @param {Store} this
14670          * @param {Roo.data.Record[]} records The array of Records added
14671          * @param {Number} index The index at which the record(s) were added
14672          */
14673         add : true,
14674         /**
14675          * @event remove
14676          * Fires when a Record has been removed from the Store
14677          * @param {Store} this
14678          * @param {Roo.data.Record} record The Record that was removed
14679          * @param {Number} index The index at which the record was removed
14680          */
14681         remove : true,
14682         /**
14683          * @event update
14684          * Fires when a Record has been updated
14685          * @param {Store} this
14686          * @param {Roo.data.Record} record The Record that was updated
14687          * @param {String} operation The update operation being performed.  Value may be one of:
14688          * <pre><code>
14689  Roo.data.Record.EDIT
14690  Roo.data.Record.REJECT
14691  Roo.data.Record.COMMIT
14692          * </code></pre>
14693          */
14694         update : true,
14695         /**
14696          * @event clear
14697          * Fires when the data cache has been cleared.
14698          * @param {Store} this
14699          */
14700         clear : true,
14701         /**
14702          * @event beforeload
14703          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14704          * the load action will be canceled.
14705          * @param {Store} this
14706          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14707          */
14708         beforeload : true,
14709         /**
14710          * @event beforeloadadd
14711          * Fires after a new set of Records has been loaded.
14712          * @param {Store} this
14713          * @param {Roo.data.Record[]} records The Records that were loaded
14714          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14715          */
14716         beforeloadadd : true,
14717         /**
14718          * @event load
14719          * Fires after a new set of Records has been loaded, before they are added to the store.
14720          * @param {Store} this
14721          * @param {Roo.data.Record[]} records The Records that were loaded
14722          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14723          * @params {Object} return from reader
14724          */
14725         load : true,
14726         /**
14727          * @event loadexception
14728          * Fires if an exception occurs in the Proxy during loading.
14729          * Called with the signature of the Proxy's "loadexception" event.
14730          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14731          * 
14732          * @param {Proxy} 
14733          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14734          * @param {Object} load options 
14735          * @param {Object} jsonData from your request (normally this contains the Exception)
14736          */
14737         loadexception : true
14738     });
14739     
14740     if(this.proxy){
14741         this.proxy = Roo.factory(this.proxy, Roo.data);
14742         this.proxy.xmodule = this.xmodule || false;
14743         this.relayEvents(this.proxy,  ["loadexception"]);
14744     }
14745     this.sortToggle = {};
14746     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14747
14748     Roo.data.Store.superclass.constructor.call(this);
14749
14750     if(this.inlineData){
14751         this.loadData(this.inlineData);
14752         delete this.inlineData;
14753     }
14754 };
14755
14756 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14757      /**
14758     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14759     * without a remote query - used by combo/forms at present.
14760     */
14761     
14762     /**
14763     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14764     */
14765     /**
14766     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14767     */
14768     /**
14769     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14770     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14771     */
14772     /**
14773     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14774     * on any HTTP request
14775     */
14776     /**
14777     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14778     */
14779     /**
14780     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14781     */
14782     multiSort: false,
14783     /**
14784     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14785     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14786     */
14787     remoteSort : false,
14788
14789     /**
14790     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14791      * loaded or when a record is removed. (defaults to false).
14792     */
14793     pruneModifiedRecords : false,
14794
14795     // private
14796     lastOptions : null,
14797
14798     /**
14799      * Add Records to the Store and fires the add event.
14800      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14801      */
14802     add : function(records){
14803         records = [].concat(records);
14804         for(var i = 0, len = records.length; i < len; i++){
14805             records[i].join(this);
14806         }
14807         var index = this.data.length;
14808         this.data.addAll(records);
14809         this.fireEvent("add", this, records, index);
14810     },
14811
14812     /**
14813      * Remove a Record from the Store and fires the remove event.
14814      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14815      */
14816     remove : function(record){
14817         var index = this.data.indexOf(record);
14818         this.data.removeAt(index);
14819  
14820         if(this.pruneModifiedRecords){
14821             this.modified.remove(record);
14822         }
14823         this.fireEvent("remove", this, record, index);
14824     },
14825
14826     /**
14827      * Remove all Records from the Store and fires the clear event.
14828      */
14829     removeAll : function(){
14830         this.data.clear();
14831         if(this.pruneModifiedRecords){
14832             this.modified = [];
14833         }
14834         this.fireEvent("clear", this);
14835     },
14836
14837     /**
14838      * Inserts Records to the Store at the given index and fires the add event.
14839      * @param {Number} index The start index at which to insert the passed Records.
14840      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14841      */
14842     insert : function(index, records){
14843         records = [].concat(records);
14844         for(var i = 0, len = records.length; i < len; i++){
14845             this.data.insert(index, records[i]);
14846             records[i].join(this);
14847         }
14848         this.fireEvent("add", this, records, index);
14849     },
14850
14851     /**
14852      * Get the index within the cache of the passed Record.
14853      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14854      * @return {Number} The index of the passed Record. Returns -1 if not found.
14855      */
14856     indexOf : function(record){
14857         return this.data.indexOf(record);
14858     },
14859
14860     /**
14861      * Get the index within the cache of the Record with the passed id.
14862      * @param {String} id The id of the Record to find.
14863      * @return {Number} The index of the Record. Returns -1 if not found.
14864      */
14865     indexOfId : function(id){
14866         return this.data.indexOfKey(id);
14867     },
14868
14869     /**
14870      * Get the Record with the specified id.
14871      * @param {String} id The id of the Record to find.
14872      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14873      */
14874     getById : function(id){
14875         return this.data.key(id);
14876     },
14877
14878     /**
14879      * Get the Record at the specified index.
14880      * @param {Number} index The index of the Record to find.
14881      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14882      */
14883     getAt : function(index){
14884         return this.data.itemAt(index);
14885     },
14886
14887     /**
14888      * Returns a range of Records between specified indices.
14889      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14890      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14891      * @return {Roo.data.Record[]} An array of Records
14892      */
14893     getRange : function(start, end){
14894         return this.data.getRange(start, end);
14895     },
14896
14897     // private
14898     storeOptions : function(o){
14899         o = Roo.apply({}, o);
14900         delete o.callback;
14901         delete o.scope;
14902         this.lastOptions = o;
14903     },
14904
14905     /**
14906      * Loads the Record cache from the configured Proxy using the configured Reader.
14907      * <p>
14908      * If using remote paging, then the first load call must specify the <em>start</em>
14909      * and <em>limit</em> properties in the options.params property to establish the initial
14910      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14911      * <p>
14912      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14913      * and this call will return before the new data has been loaded. Perform any post-processing
14914      * in a callback function, or in a "load" event handler.</strong>
14915      * <p>
14916      * @param {Object} options An object containing properties which control loading options:<ul>
14917      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14918      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14919      * passed the following arguments:<ul>
14920      * <li>r : Roo.data.Record[]</li>
14921      * <li>options: Options object from the load call</li>
14922      * <li>success: Boolean success indicator</li></ul></li>
14923      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14924      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14925      * </ul>
14926      */
14927     load : function(options){
14928         options = options || {};
14929         if(this.fireEvent("beforeload", this, options) !== false){
14930             this.storeOptions(options);
14931             var p = Roo.apply(options.params || {}, this.baseParams);
14932             // if meta was not loaded from remote source.. try requesting it.
14933             if (!this.reader.metaFromRemote) {
14934                 p._requestMeta = 1;
14935             }
14936             if(this.sortInfo && this.remoteSort){
14937                 var pn = this.paramNames;
14938                 p[pn["sort"]] = this.sortInfo.field;
14939                 p[pn["dir"]] = this.sortInfo.direction;
14940             }
14941             if (this.multiSort) {
14942                 var pn = this.paramNames;
14943                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14944             }
14945             
14946             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14947         }
14948     },
14949
14950     /**
14951      * Reloads the Record cache from the configured Proxy using the configured Reader and
14952      * the options from the last load operation performed.
14953      * @param {Object} options (optional) An object containing properties which may override the options
14954      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14955      * the most recently used options are reused).
14956      */
14957     reload : function(options){
14958         this.load(Roo.applyIf(options||{}, this.lastOptions));
14959     },
14960
14961     // private
14962     // Called as a callback by the Reader during a load operation.
14963     loadRecords : function(o, options, success){
14964         if(!o || success === false){
14965             if(success !== false){
14966                 this.fireEvent("load", this, [], options, o);
14967             }
14968             if(options.callback){
14969                 options.callback.call(options.scope || this, [], options, false);
14970             }
14971             return;
14972         }
14973         // if data returned failure - throw an exception.
14974         if (o.success === false) {
14975             // show a message if no listener is registered.
14976             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14977                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14978             }
14979             // loadmask wil be hooked into this..
14980             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14981             return;
14982         }
14983         var r = o.records, t = o.totalRecords || r.length;
14984         
14985         this.fireEvent("beforeloadadd", this, r, options, o);
14986         
14987         if(!options || options.add !== true){
14988             if(this.pruneModifiedRecords){
14989                 this.modified = [];
14990             }
14991             for(var i = 0, len = r.length; i < len; i++){
14992                 r[i].join(this);
14993             }
14994             if(this.snapshot){
14995                 this.data = this.snapshot;
14996                 delete this.snapshot;
14997             }
14998             this.data.clear();
14999             this.data.addAll(r);
15000             this.totalLength = t;
15001             this.applySort();
15002             this.fireEvent("datachanged", this);
15003         }else{
15004             this.totalLength = Math.max(t, this.data.length+r.length);
15005             this.add(r);
15006         }
15007         
15008         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15009                 
15010             var e = new Roo.data.Record({});
15011
15012             e.set(this.parent.displayField, this.parent.emptyTitle);
15013             e.set(this.parent.valueField, '');
15014
15015             this.insert(0, e);
15016         }
15017             
15018         this.fireEvent("load", this, r, options, o);
15019         if(options.callback){
15020             options.callback.call(options.scope || this, r, options, true);
15021         }
15022     },
15023
15024
15025     /**
15026      * Loads data from a passed data block. A Reader which understands the format of the data
15027      * must have been configured in the constructor.
15028      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15029      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15030      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15031      */
15032     loadData : function(o, append){
15033         var r = this.reader.readRecords(o);
15034         this.loadRecords(r, {add: append}, true);
15035     },
15036     
15037      /**
15038      * using 'cn' the nested child reader read the child array into it's child stores.
15039      * @param {Object} rec The record with a 'children array
15040      */
15041     loadDataFromChildren : function(rec)
15042     {
15043         this.loadData(this.reader.toLoadData(rec));
15044     },
15045     
15046
15047     /**
15048      * Gets the number of cached records.
15049      * <p>
15050      * <em>If using paging, this may not be the total size of the dataset. If the data object
15051      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15052      * the data set size</em>
15053      */
15054     getCount : function(){
15055         return this.data.length || 0;
15056     },
15057
15058     /**
15059      * Gets the total number of records in the dataset as returned by the server.
15060      * <p>
15061      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15062      * the dataset size</em>
15063      */
15064     getTotalCount : function(){
15065         return this.totalLength || 0;
15066     },
15067
15068     /**
15069      * Returns the sort state of the Store as an object with two properties:
15070      * <pre><code>
15071  field {String} The name of the field by which the Records are sorted
15072  direction {String} The sort order, "ASC" or "DESC"
15073      * </code></pre>
15074      */
15075     getSortState : function(){
15076         return this.sortInfo;
15077     },
15078
15079     // private
15080     applySort : function(){
15081         if(this.sortInfo && !this.remoteSort){
15082             var s = this.sortInfo, f = s.field;
15083             var st = this.fields.get(f).sortType;
15084             var fn = function(r1, r2){
15085                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15086                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15087             };
15088             this.data.sort(s.direction, fn);
15089             if(this.snapshot && this.snapshot != this.data){
15090                 this.snapshot.sort(s.direction, fn);
15091             }
15092         }
15093     },
15094
15095     /**
15096      * Sets the default sort column and order to be used by the next load operation.
15097      * @param {String} fieldName The name of the field to sort by.
15098      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15099      */
15100     setDefaultSort : function(field, dir){
15101         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15102     },
15103
15104     /**
15105      * Sort the Records.
15106      * If remote sorting is used, the sort is performed on the server, and the cache is
15107      * reloaded. If local sorting is used, the cache is sorted internally.
15108      * @param {String} fieldName The name of the field to sort by.
15109      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15110      */
15111     sort : function(fieldName, dir){
15112         var f = this.fields.get(fieldName);
15113         if(!dir){
15114             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15115             
15116             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15117                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15118             }else{
15119                 dir = f.sortDir;
15120             }
15121         }
15122         this.sortToggle[f.name] = dir;
15123         this.sortInfo = {field: f.name, direction: dir};
15124         if(!this.remoteSort){
15125             this.applySort();
15126             this.fireEvent("datachanged", this);
15127         }else{
15128             this.load(this.lastOptions);
15129         }
15130     },
15131
15132     /**
15133      * Calls the specified function for each of the Records in the cache.
15134      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15135      * Returning <em>false</em> aborts and exits the iteration.
15136      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15137      */
15138     each : function(fn, scope){
15139         this.data.each(fn, scope);
15140     },
15141
15142     /**
15143      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15144      * (e.g., during paging).
15145      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15146      */
15147     getModifiedRecords : function(){
15148         return this.modified;
15149     },
15150
15151     // private
15152     createFilterFn : function(property, value, anyMatch){
15153         if(!value.exec){ // not a regex
15154             value = String(value);
15155             if(value.length == 0){
15156                 return false;
15157             }
15158             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15159         }
15160         return function(r){
15161             return value.test(r.data[property]);
15162         };
15163     },
15164
15165     /**
15166      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15167      * @param {String} property A field on your records
15168      * @param {Number} start The record index to start at (defaults to 0)
15169      * @param {Number} end The last record index to include (defaults to length - 1)
15170      * @return {Number} The sum
15171      */
15172     sum : function(property, start, end){
15173         var rs = this.data.items, v = 0;
15174         start = start || 0;
15175         end = (end || end === 0) ? end : rs.length-1;
15176
15177         for(var i = start; i <= end; i++){
15178             v += (rs[i].data[property] || 0);
15179         }
15180         return v;
15181     },
15182
15183     /**
15184      * Filter the records by a specified property.
15185      * @param {String} field A field on your records
15186      * @param {String/RegExp} value Either a string that the field
15187      * should start with or a RegExp to test against the field
15188      * @param {Boolean} anyMatch True to match any part not just the beginning
15189      */
15190     filter : function(property, value, anyMatch){
15191         var fn = this.createFilterFn(property, value, anyMatch);
15192         return fn ? this.filterBy(fn) : this.clearFilter();
15193     },
15194
15195     /**
15196      * Filter by a function. The specified function will be called with each
15197      * record in this data source. If the function returns true the record is included,
15198      * otherwise it is filtered.
15199      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15200      * @param {Object} scope (optional) The scope of the function (defaults to this)
15201      */
15202     filterBy : function(fn, scope){
15203         this.snapshot = this.snapshot || this.data;
15204         this.data = this.queryBy(fn, scope||this);
15205         this.fireEvent("datachanged", this);
15206     },
15207
15208     /**
15209      * Query the records by a specified property.
15210      * @param {String} field A field on your records
15211      * @param {String/RegExp} value Either a string that the field
15212      * should start with or a RegExp to test against the field
15213      * @param {Boolean} anyMatch True to match any part not just the beginning
15214      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15215      */
15216     query : function(property, value, anyMatch){
15217         var fn = this.createFilterFn(property, value, anyMatch);
15218         return fn ? this.queryBy(fn) : this.data.clone();
15219     },
15220
15221     /**
15222      * Query by a function. The specified function will be called with each
15223      * record in this data source. If the function returns true the record is included
15224      * in the results.
15225      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15226      * @param {Object} scope (optional) The scope of the function (defaults to this)
15227       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15228      **/
15229     queryBy : function(fn, scope){
15230         var data = this.snapshot || this.data;
15231         return data.filterBy(fn, scope||this);
15232     },
15233
15234     /**
15235      * Collects unique values for a particular dataIndex from this store.
15236      * @param {String} dataIndex The property to collect
15237      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15238      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15239      * @return {Array} An array of the unique values
15240      **/
15241     collect : function(dataIndex, allowNull, bypassFilter){
15242         var d = (bypassFilter === true && this.snapshot) ?
15243                 this.snapshot.items : this.data.items;
15244         var v, sv, r = [], l = {};
15245         for(var i = 0, len = d.length; i < len; i++){
15246             v = d[i].data[dataIndex];
15247             sv = String(v);
15248             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15249                 l[sv] = true;
15250                 r[r.length] = v;
15251             }
15252         }
15253         return r;
15254     },
15255
15256     /**
15257      * Revert to a view of the Record cache with no filtering applied.
15258      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15259      */
15260     clearFilter : function(suppressEvent){
15261         if(this.snapshot && this.snapshot != this.data){
15262             this.data = this.snapshot;
15263             delete this.snapshot;
15264             if(suppressEvent !== true){
15265                 this.fireEvent("datachanged", this);
15266             }
15267         }
15268     },
15269
15270     // private
15271     afterEdit : function(record){
15272         if(this.modified.indexOf(record) == -1){
15273             this.modified.push(record);
15274         }
15275         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15276     },
15277     
15278     // private
15279     afterReject : function(record){
15280         this.modified.remove(record);
15281         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15282     },
15283
15284     // private
15285     afterCommit : function(record){
15286         this.modified.remove(record);
15287         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15288     },
15289
15290     /**
15291      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15292      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15293      */
15294     commitChanges : function(){
15295         var m = this.modified.slice(0);
15296         this.modified = [];
15297         for(var i = 0, len = m.length; i < len; i++){
15298             m[i].commit();
15299         }
15300     },
15301
15302     /**
15303      * Cancel outstanding changes on all changed records.
15304      */
15305     rejectChanges : function(){
15306         var m = this.modified.slice(0);
15307         this.modified = [];
15308         for(var i = 0, len = m.length; i < len; i++){
15309             m[i].reject();
15310         }
15311     },
15312
15313     onMetaChange : function(meta, rtype, o){
15314         this.recordType = rtype;
15315         this.fields = rtype.prototype.fields;
15316         delete this.snapshot;
15317         this.sortInfo = meta.sortInfo || this.sortInfo;
15318         this.modified = [];
15319         this.fireEvent('metachange', this, this.reader.meta);
15320     },
15321     
15322     moveIndex : function(data, type)
15323     {
15324         var index = this.indexOf(data);
15325         
15326         var newIndex = index + type;
15327         
15328         this.remove(data);
15329         
15330         this.insert(newIndex, data);
15331         
15332     }
15333 });/*
15334  * Based on:
15335  * Ext JS Library 1.1.1
15336  * Copyright(c) 2006-2007, Ext JS, LLC.
15337  *
15338  * Originally Released Under LGPL - original licence link has changed is not relivant.
15339  *
15340  * Fork - LGPL
15341  * <script type="text/javascript">
15342  */
15343
15344 /**
15345  * @class Roo.data.SimpleStore
15346  * @extends Roo.data.Store
15347  * Small helper class to make creating Stores from Array data easier.
15348  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15349  * @cfg {Array} fields An array of field definition objects, or field name strings.
15350  * @cfg {Object} an existing reader (eg. copied from another store)
15351  * @cfg {Array} data The multi-dimensional array of data
15352  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15353  * @cfg {Roo.data.Reader} reader  [not-required] 
15354  * @constructor
15355  * @param {Object} config
15356  */
15357 Roo.data.SimpleStore = function(config)
15358 {
15359     Roo.data.SimpleStore.superclass.constructor.call(this, {
15360         isLocal : true,
15361         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15362                 id: config.id
15363             },
15364             Roo.data.Record.create(config.fields)
15365         ),
15366         proxy : new Roo.data.MemoryProxy(config.data)
15367     });
15368     this.load();
15369 };
15370 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15371  * Based on:
15372  * Ext JS Library 1.1.1
15373  * Copyright(c) 2006-2007, Ext JS, LLC.
15374  *
15375  * Originally Released Under LGPL - original licence link has changed is not relivant.
15376  *
15377  * Fork - LGPL
15378  * <script type="text/javascript">
15379  */
15380
15381 /**
15382 /**
15383  * @extends Roo.data.Store
15384  * @class Roo.data.JsonStore
15385  * Small helper class to make creating Stores for JSON data easier. <br/>
15386 <pre><code>
15387 var store = new Roo.data.JsonStore({
15388     url: 'get-images.php',
15389     root: 'images',
15390     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15391 });
15392 </code></pre>
15393  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15394  * JsonReader and HttpProxy (unless inline data is provided).</b>
15395  * @cfg {Array} fields An array of field definition objects, or field name strings.
15396  * @constructor
15397  * @param {Object} config
15398  */
15399 Roo.data.JsonStore = function(c){
15400     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15401         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15402         reader: new Roo.data.JsonReader(c, c.fields)
15403     }));
15404 };
15405 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15406  * Based on:
15407  * Ext JS Library 1.1.1
15408  * Copyright(c) 2006-2007, Ext JS, LLC.
15409  *
15410  * Originally Released Under LGPL - original licence link has changed is not relivant.
15411  *
15412  * Fork - LGPL
15413  * <script type="text/javascript">
15414  */
15415
15416  
15417 Roo.data.Field = function(config){
15418     if(typeof config == "string"){
15419         config = {name: config};
15420     }
15421     Roo.apply(this, config);
15422     
15423     if(!this.type){
15424         this.type = "auto";
15425     }
15426     
15427     var st = Roo.data.SortTypes;
15428     // named sortTypes are supported, here we look them up
15429     if(typeof this.sortType == "string"){
15430         this.sortType = st[this.sortType];
15431     }
15432     
15433     // set default sortType for strings and dates
15434     if(!this.sortType){
15435         switch(this.type){
15436             case "string":
15437                 this.sortType = st.asUCString;
15438                 break;
15439             case "date":
15440                 this.sortType = st.asDate;
15441                 break;
15442             default:
15443                 this.sortType = st.none;
15444         }
15445     }
15446
15447     // define once
15448     var stripRe = /[\$,%]/g;
15449
15450     // prebuilt conversion function for this field, instead of
15451     // switching every time we're reading a value
15452     if(!this.convert){
15453         var cv, dateFormat = this.dateFormat;
15454         switch(this.type){
15455             case "":
15456             case "auto":
15457             case undefined:
15458                 cv = function(v){ return v; };
15459                 break;
15460             case "string":
15461                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15462                 break;
15463             case "int":
15464                 cv = function(v){
15465                     return v !== undefined && v !== null && v !== '' ?
15466                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15467                     };
15468                 break;
15469             case "float":
15470                 cv = function(v){
15471                     return v !== undefined && v !== null && v !== '' ?
15472                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15473                     };
15474                 break;
15475             case "bool":
15476             case "boolean":
15477                 cv = function(v){ return v === true || v === "true" || v == 1; };
15478                 break;
15479             case "date":
15480                 cv = function(v){
15481                     if(!v){
15482                         return '';
15483                     }
15484                     if(v instanceof Date){
15485                         return v;
15486                     }
15487                     if(dateFormat){
15488                         if(dateFormat == "timestamp"){
15489                             return new Date(v*1000);
15490                         }
15491                         return Date.parseDate(v, dateFormat);
15492                     }
15493                     var parsed = Date.parse(v);
15494                     return parsed ? new Date(parsed) : null;
15495                 };
15496              break;
15497             
15498         }
15499         this.convert = cv;
15500     }
15501 };
15502
15503 Roo.data.Field.prototype = {
15504     dateFormat: null,
15505     defaultValue: "",
15506     mapping: null,
15507     sortType : null,
15508     sortDir : "ASC"
15509 };/*
15510  * Based on:
15511  * Ext JS Library 1.1.1
15512  * Copyright(c) 2006-2007, Ext JS, LLC.
15513  *
15514  * Originally Released Under LGPL - original licence link has changed is not relivant.
15515  *
15516  * Fork - LGPL
15517  * <script type="text/javascript">
15518  */
15519  
15520 // Base class for reading structured data from a data source.  This class is intended to be
15521 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15522
15523 /**
15524  * @class Roo.data.DataReader
15525  * @abstract
15526  * Base class for reading structured data from a data source.  This class is intended to be
15527  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15528  */
15529
15530 Roo.data.DataReader = function(meta, recordType){
15531     
15532     this.meta = meta;
15533     
15534     this.recordType = recordType instanceof Array ? 
15535         Roo.data.Record.create(recordType) : recordType;
15536 };
15537
15538 Roo.data.DataReader.prototype = {
15539     
15540     
15541     readerType : 'Data',
15542      /**
15543      * Create an empty record
15544      * @param {Object} data (optional) - overlay some values
15545      * @return {Roo.data.Record} record created.
15546      */
15547     newRow :  function(d) {
15548         var da =  {};
15549         this.recordType.prototype.fields.each(function(c) {
15550             switch( c.type) {
15551                 case 'int' : da[c.name] = 0; break;
15552                 case 'date' : da[c.name] = new Date(); break;
15553                 case 'float' : da[c.name] = 0.0; break;
15554                 case 'boolean' : da[c.name] = false; break;
15555                 default : da[c.name] = ""; break;
15556             }
15557             
15558         });
15559         return new this.recordType(Roo.apply(da, d));
15560     }
15561     
15562     
15563 };/*
15564  * Based on:
15565  * Ext JS Library 1.1.1
15566  * Copyright(c) 2006-2007, Ext JS, LLC.
15567  *
15568  * Originally Released Under LGPL - original licence link has changed is not relivant.
15569  *
15570  * Fork - LGPL
15571  * <script type="text/javascript">
15572  */
15573
15574 /**
15575  * @class Roo.data.DataProxy
15576  * @extends Roo.data.Observable
15577  * @abstract
15578  * This class is an abstract base class for implementations which provide retrieval of
15579  * unformatted data objects.<br>
15580  * <p>
15581  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15582  * (of the appropriate type which knows how to parse the data object) to provide a block of
15583  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15584  * <p>
15585  * Custom implementations must implement the load method as described in
15586  * {@link Roo.data.HttpProxy#load}.
15587  */
15588 Roo.data.DataProxy = function(){
15589     this.addEvents({
15590         /**
15591          * @event beforeload
15592          * Fires before a network request is made to retrieve a data object.
15593          * @param {Object} This DataProxy object.
15594          * @param {Object} params The params parameter to the load function.
15595          */
15596         beforeload : true,
15597         /**
15598          * @event load
15599          * Fires before the load method's callback is called.
15600          * @param {Object} This DataProxy object.
15601          * @param {Object} o The data object.
15602          * @param {Object} arg The callback argument object passed to the load function.
15603          */
15604         load : true,
15605         /**
15606          * @event loadexception
15607          * Fires if an Exception occurs during data retrieval.
15608          * @param {Object} This DataProxy object.
15609          * @param {Object} o The data object.
15610          * @param {Object} arg The callback argument object passed to the load function.
15611          * @param {Object} e The Exception.
15612          */
15613         loadexception : true
15614     });
15615     Roo.data.DataProxy.superclass.constructor.call(this);
15616 };
15617
15618 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15619
15620     /**
15621      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15622      */
15623 /*
15624  * Based on:
15625  * Ext JS Library 1.1.1
15626  * Copyright(c) 2006-2007, Ext JS, LLC.
15627  *
15628  * Originally Released Under LGPL - original licence link has changed is not relivant.
15629  *
15630  * Fork - LGPL
15631  * <script type="text/javascript">
15632  */
15633 /**
15634  * @class Roo.data.MemoryProxy
15635  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15636  * to the Reader when its load method is called.
15637  * @constructor
15638  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15639  */
15640 Roo.data.MemoryProxy = function(data){
15641     if (data.data) {
15642         data = data.data;
15643     }
15644     Roo.data.MemoryProxy.superclass.constructor.call(this);
15645     this.data = data;
15646 };
15647
15648 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15649     
15650     /**
15651      * Load data from the requested source (in this case an in-memory
15652      * data object passed to the constructor), read the data object into
15653      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15654      * process that block using the passed callback.
15655      * @param {Object} params This parameter is not used by the MemoryProxy class.
15656      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15657      * object into a block of Roo.data.Records.
15658      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15659      * The function must be passed <ul>
15660      * <li>The Record block object</li>
15661      * <li>The "arg" argument from the load function</li>
15662      * <li>A boolean success indicator</li>
15663      * </ul>
15664      * @param {Object} scope The scope in which to call the callback
15665      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15666      */
15667     load : function(params, reader, callback, scope, arg){
15668         params = params || {};
15669         var result;
15670         try {
15671             result = reader.readRecords(params.data ? params.data :this.data);
15672         }catch(e){
15673             this.fireEvent("loadexception", this, arg, null, e);
15674             callback.call(scope, null, arg, false);
15675             return;
15676         }
15677         callback.call(scope, result, arg, true);
15678     },
15679     
15680     // private
15681     update : function(params, records){
15682         
15683     }
15684 });/*
15685  * Based on:
15686  * Ext JS Library 1.1.1
15687  * Copyright(c) 2006-2007, Ext JS, LLC.
15688  *
15689  * Originally Released Under LGPL - original licence link has changed is not relivant.
15690  *
15691  * Fork - LGPL
15692  * <script type="text/javascript">
15693  */
15694 /**
15695  * @class Roo.data.HttpProxy
15696  * @extends Roo.data.DataProxy
15697  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15698  * configured to reference a certain URL.<br><br>
15699  * <p>
15700  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15701  * from which the running page was served.<br><br>
15702  * <p>
15703  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15704  * <p>
15705  * Be aware that to enable the browser to parse an XML document, the server must set
15706  * the Content-Type header in the HTTP response to "text/xml".
15707  * @constructor
15708  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15709  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15710  * will be used to make the request.
15711  */
15712 Roo.data.HttpProxy = function(conn){
15713     Roo.data.HttpProxy.superclass.constructor.call(this);
15714     // is conn a conn config or a real conn?
15715     this.conn = conn;
15716     this.useAjax = !conn || !conn.events;
15717   
15718 };
15719
15720 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15721     // thse are take from connection...
15722     
15723     /**
15724      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15725      */
15726     /**
15727      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15728      * extra parameters to each request made by this object. (defaults to undefined)
15729      */
15730     /**
15731      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15732      *  to each request made by this object. (defaults to undefined)
15733      */
15734     /**
15735      * @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)
15736      */
15737     /**
15738      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15739      */
15740      /**
15741      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15742      * @type Boolean
15743      */
15744   
15745
15746     /**
15747      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15748      * @type Boolean
15749      */
15750     /**
15751      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15752      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15753      * a finer-grained basis than the DataProxy events.
15754      */
15755     getConnection : function(){
15756         return this.useAjax ? Roo.Ajax : this.conn;
15757     },
15758
15759     /**
15760      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15761      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15762      * process that block using the passed callback.
15763      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15764      * for the request to the remote server.
15765      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15766      * object into a block of Roo.data.Records.
15767      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15768      * The function must be passed <ul>
15769      * <li>The Record block object</li>
15770      * <li>The "arg" argument from the load function</li>
15771      * <li>A boolean success indicator</li>
15772      * </ul>
15773      * @param {Object} scope The scope in which to call the callback
15774      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15775      */
15776     load : function(params, reader, callback, scope, arg){
15777         if(this.fireEvent("beforeload", this, params) !== false){
15778             var  o = {
15779                 params : params || {},
15780                 request: {
15781                     callback : callback,
15782                     scope : scope,
15783                     arg : arg
15784                 },
15785                 reader: reader,
15786                 callback : this.loadResponse,
15787                 scope: this
15788             };
15789             if(this.useAjax){
15790                 Roo.applyIf(o, this.conn);
15791                 if(this.activeRequest){
15792                     Roo.Ajax.abort(this.activeRequest);
15793                 }
15794                 this.activeRequest = Roo.Ajax.request(o);
15795             }else{
15796                 this.conn.request(o);
15797             }
15798         }else{
15799             callback.call(scope||this, null, arg, false);
15800         }
15801     },
15802
15803     // private
15804     loadResponse : function(o, success, response){
15805         delete this.activeRequest;
15806         if(!success){
15807             this.fireEvent("loadexception", this, o, response);
15808             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15809             return;
15810         }
15811         var result;
15812         try {
15813             result = o.reader.read(response);
15814         }catch(e){
15815             this.fireEvent("loadexception", this, o, response, e);
15816             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15817             return;
15818         }
15819         
15820         this.fireEvent("load", this, o, o.request.arg);
15821         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15822     },
15823
15824     // private
15825     update : function(dataSet){
15826
15827     },
15828
15829     // private
15830     updateResponse : function(dataSet){
15831
15832     }
15833 });/*
15834  * Based on:
15835  * Ext JS Library 1.1.1
15836  * Copyright(c) 2006-2007, Ext JS, LLC.
15837  *
15838  * Originally Released Under LGPL - original licence link has changed is not relivant.
15839  *
15840  * Fork - LGPL
15841  * <script type="text/javascript">
15842  */
15843
15844 /**
15845  * @class Roo.data.ScriptTagProxy
15846  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15847  * other than the originating domain of the running page.<br><br>
15848  * <p>
15849  * <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
15850  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15851  * <p>
15852  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15853  * source code that is used as the source inside a &lt;script> tag.<br><br>
15854  * <p>
15855  * In order for the browser to process the returned data, the server must wrap the data object
15856  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15857  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15858  * depending on whether the callback name was passed:
15859  * <p>
15860  * <pre><code>
15861 boolean scriptTag = false;
15862 String cb = request.getParameter("callback");
15863 if (cb != null) {
15864     scriptTag = true;
15865     response.setContentType("text/javascript");
15866 } else {
15867     response.setContentType("application/x-json");
15868 }
15869 Writer out = response.getWriter();
15870 if (scriptTag) {
15871     out.write(cb + "(");
15872 }
15873 out.print(dataBlock.toJsonString());
15874 if (scriptTag) {
15875     out.write(");");
15876 }
15877 </pre></code>
15878  *
15879  * @constructor
15880  * @param {Object} config A configuration object.
15881  */
15882 Roo.data.ScriptTagProxy = function(config){
15883     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15884     Roo.apply(this, config);
15885     this.head = document.getElementsByTagName("head")[0];
15886 };
15887
15888 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15889
15890 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15891     /**
15892      * @cfg {String} url The URL from which to request the data object.
15893      */
15894     /**
15895      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15896      */
15897     timeout : 30000,
15898     /**
15899      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15900      * the server the name of the callback function set up by the load call to process the returned data object.
15901      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15902      * javascript output which calls this named function passing the data object as its only parameter.
15903      */
15904     callbackParam : "callback",
15905     /**
15906      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15907      * name to the request.
15908      */
15909     nocache : true,
15910
15911     /**
15912      * Load data from the configured URL, read the data object into
15913      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15914      * process that block using the passed callback.
15915      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15916      * for the request to the remote server.
15917      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15918      * object into a block of Roo.data.Records.
15919      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15920      * The function must be passed <ul>
15921      * <li>The Record block object</li>
15922      * <li>The "arg" argument from the load function</li>
15923      * <li>A boolean success indicator</li>
15924      * </ul>
15925      * @param {Object} scope The scope in which to call the callback
15926      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15927      */
15928     load : function(params, reader, callback, scope, arg){
15929         if(this.fireEvent("beforeload", this, params) !== false){
15930
15931             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15932
15933             var url = this.url;
15934             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15935             if(this.nocache){
15936                 url += "&_dc=" + (new Date().getTime());
15937             }
15938             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15939             var trans = {
15940                 id : transId,
15941                 cb : "stcCallback"+transId,
15942                 scriptId : "stcScript"+transId,
15943                 params : params,
15944                 arg : arg,
15945                 url : url,
15946                 callback : callback,
15947                 scope : scope,
15948                 reader : reader
15949             };
15950             var conn = this;
15951
15952             window[trans.cb] = function(o){
15953                 conn.handleResponse(o, trans);
15954             };
15955
15956             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15957
15958             if(this.autoAbort !== false){
15959                 this.abort();
15960             }
15961
15962             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15963
15964             var script = document.createElement("script");
15965             script.setAttribute("src", url);
15966             script.setAttribute("type", "text/javascript");
15967             script.setAttribute("id", trans.scriptId);
15968             this.head.appendChild(script);
15969
15970             this.trans = trans;
15971         }else{
15972             callback.call(scope||this, null, arg, false);
15973         }
15974     },
15975
15976     // private
15977     isLoading : function(){
15978         return this.trans ? true : false;
15979     },
15980
15981     /**
15982      * Abort the current server request.
15983      */
15984     abort : function(){
15985         if(this.isLoading()){
15986             this.destroyTrans(this.trans);
15987         }
15988     },
15989
15990     // private
15991     destroyTrans : function(trans, isLoaded){
15992         this.head.removeChild(document.getElementById(trans.scriptId));
15993         clearTimeout(trans.timeoutId);
15994         if(isLoaded){
15995             window[trans.cb] = undefined;
15996             try{
15997                 delete window[trans.cb];
15998             }catch(e){}
15999         }else{
16000             // if hasn't been loaded, wait for load to remove it to prevent script error
16001             window[trans.cb] = function(){
16002                 window[trans.cb] = undefined;
16003                 try{
16004                     delete window[trans.cb];
16005                 }catch(e){}
16006             };
16007         }
16008     },
16009
16010     // private
16011     handleResponse : function(o, trans){
16012         this.trans = false;
16013         this.destroyTrans(trans, true);
16014         var result;
16015         try {
16016             result = trans.reader.readRecords(o);
16017         }catch(e){
16018             this.fireEvent("loadexception", this, o, trans.arg, e);
16019             trans.callback.call(trans.scope||window, null, trans.arg, false);
16020             return;
16021         }
16022         this.fireEvent("load", this, o, trans.arg);
16023         trans.callback.call(trans.scope||window, result, trans.arg, true);
16024     },
16025
16026     // private
16027     handleFailure : function(trans){
16028         this.trans = false;
16029         this.destroyTrans(trans, false);
16030         this.fireEvent("loadexception", this, null, trans.arg);
16031         trans.callback.call(trans.scope||window, null, trans.arg, false);
16032     }
16033 });/*
16034  * Based on:
16035  * Ext JS Library 1.1.1
16036  * Copyright(c) 2006-2007, Ext JS, LLC.
16037  *
16038  * Originally Released Under LGPL - original licence link has changed is not relivant.
16039  *
16040  * Fork - LGPL
16041  * <script type="text/javascript">
16042  */
16043
16044 /**
16045  * @class Roo.data.JsonReader
16046  * @extends Roo.data.DataReader
16047  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16048  * based on mappings in a provided Roo.data.Record constructor.
16049  * 
16050  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16051  * in the reply previously. 
16052  * 
16053  * <p>
16054  * Example code:
16055  * <pre><code>
16056 var RecordDef = Roo.data.Record.create([
16057     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16058     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16059 ]);
16060 var myReader = new Roo.data.JsonReader({
16061     totalProperty: "results",    // The property which contains the total dataset size (optional)
16062     root: "rows",                // The property which contains an Array of row objects
16063     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16064 }, RecordDef);
16065 </code></pre>
16066  * <p>
16067  * This would consume a JSON file like this:
16068  * <pre><code>
16069 { 'results': 2, 'rows': [
16070     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16071     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16072 }
16073 </code></pre>
16074  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16075  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16076  * paged from the remote server.
16077  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16078  * @cfg {String} root name of the property which contains the Array of row objects.
16079  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16080  * @cfg {Array} fields Array of field definition objects
16081  * @constructor
16082  * Create a new JsonReader
16083  * @param {Object} meta Metadata configuration options
16084  * @param {Object} recordType Either an Array of field definition objects,
16085  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16086  */
16087 Roo.data.JsonReader = function(meta, recordType){
16088     
16089     meta = meta || {};
16090     // set some defaults:
16091     Roo.applyIf(meta, {
16092         totalProperty: 'total',
16093         successProperty : 'success',
16094         root : 'data',
16095         id : 'id'
16096     });
16097     
16098     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16099 };
16100 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16101     
16102     readerType : 'Json',
16103     
16104     /**
16105      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16106      * Used by Store query builder to append _requestMeta to params.
16107      * 
16108      */
16109     metaFromRemote : false,
16110     /**
16111      * This method is only used by a DataProxy which has retrieved data from a remote server.
16112      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16113      * @return {Object} data A data block which is used by an Roo.data.Store object as
16114      * a cache of Roo.data.Records.
16115      */
16116     read : function(response){
16117         var json = response.responseText;
16118        
16119         var o = /* eval:var:o */ eval("("+json+")");
16120         if(!o) {
16121             throw {message: "JsonReader.read: Json object not found"};
16122         }
16123         
16124         if(o.metaData){
16125             
16126             delete this.ef;
16127             this.metaFromRemote = true;
16128             this.meta = o.metaData;
16129             this.recordType = Roo.data.Record.create(o.metaData.fields);
16130             this.onMetaChange(this.meta, this.recordType, o);
16131         }
16132         return this.readRecords(o);
16133     },
16134
16135     // private function a store will implement
16136     onMetaChange : function(meta, recordType, o){
16137
16138     },
16139
16140     /**
16141          * @ignore
16142          */
16143     simpleAccess: function(obj, subsc) {
16144         return obj[subsc];
16145     },
16146
16147         /**
16148          * @ignore
16149          */
16150     getJsonAccessor: function(){
16151         var re = /[\[\.]/;
16152         return function(expr) {
16153             try {
16154                 return(re.test(expr))
16155                     ? new Function("obj", "return obj." + expr)
16156                     : function(obj){
16157                         return obj[expr];
16158                     };
16159             } catch(e){}
16160             return Roo.emptyFn;
16161         };
16162     }(),
16163
16164     /**
16165      * Create a data block containing Roo.data.Records from an XML document.
16166      * @param {Object} o An object which contains an Array of row objects in the property specified
16167      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16168      * which contains the total size of the dataset.
16169      * @return {Object} data A data block which is used by an Roo.data.Store object as
16170      * a cache of Roo.data.Records.
16171      */
16172     readRecords : function(o){
16173         /**
16174          * After any data loads, the raw JSON data is available for further custom processing.
16175          * @type Object
16176          */
16177         this.o = o;
16178         var s = this.meta, Record = this.recordType,
16179             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16180
16181 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16182         if (!this.ef) {
16183             if(s.totalProperty) {
16184                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16185                 }
16186                 if(s.successProperty) {
16187                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16188                 }
16189                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16190                 if (s.id) {
16191                         var g = this.getJsonAccessor(s.id);
16192                         this.getId = function(rec) {
16193                                 var r = g(rec);  
16194                                 return (r === undefined || r === "") ? null : r;
16195                         };
16196                 } else {
16197                         this.getId = function(){return null;};
16198                 }
16199             this.ef = [];
16200             for(var jj = 0; jj < fl; jj++){
16201                 f = fi[jj];
16202                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16203                 this.ef[jj] = this.getJsonAccessor(map);
16204             }
16205         }
16206
16207         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16208         if(s.totalProperty){
16209             var vt = parseInt(this.getTotal(o), 10);
16210             if(!isNaN(vt)){
16211                 totalRecords = vt;
16212             }
16213         }
16214         if(s.successProperty){
16215             var vs = this.getSuccess(o);
16216             if(vs === false || vs === 'false'){
16217                 success = false;
16218             }
16219         }
16220         var records = [];
16221         for(var i = 0; i < c; i++){
16222                 var n = root[i];
16223             var values = {};
16224             var id = this.getId(n);
16225             for(var j = 0; j < fl; j++){
16226                 f = fi[j];
16227             var v = this.ef[j](n);
16228             if (!f.convert) {
16229                 Roo.log('missing convert for ' + f.name);
16230                 Roo.log(f);
16231                 continue;
16232             }
16233             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16234             }
16235             var record = new Record(values, id);
16236             record.json = n;
16237             records[i] = record;
16238         }
16239         return {
16240             raw : o,
16241             success : success,
16242             records : records,
16243             totalRecords : totalRecords
16244         };
16245     },
16246     // used when loading children.. @see loadDataFromChildren
16247     toLoadData: function(rec)
16248     {
16249         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16250         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16251         return { data : data, total : data.length };
16252         
16253     }
16254 });/*
16255  * Based on:
16256  * Ext JS Library 1.1.1
16257  * Copyright(c) 2006-2007, Ext JS, LLC.
16258  *
16259  * Originally Released Under LGPL - original licence link has changed is not relivant.
16260  *
16261  * Fork - LGPL
16262  * <script type="text/javascript">
16263  */
16264
16265 /**
16266  * @class Roo.data.ArrayReader
16267  * @extends Roo.data.DataReader
16268  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16269  * Each element of that Array represents a row of data fields. The
16270  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16271  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16272  * <p>
16273  * Example code:.
16274  * <pre><code>
16275 var RecordDef = Roo.data.Record.create([
16276     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16277     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16278 ]);
16279 var myReader = new Roo.data.ArrayReader({
16280     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16281 }, RecordDef);
16282 </code></pre>
16283  * <p>
16284  * This would consume an Array like this:
16285  * <pre><code>
16286 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16287   </code></pre>
16288  
16289  * @constructor
16290  * Create a new JsonReader
16291  * @param {Object} meta Metadata configuration options.
16292  * @param {Object|Array} recordType Either an Array of field definition objects
16293  * 
16294  * @cfg {Array} fields Array of field definition objects
16295  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16296  * as specified to {@link Roo.data.Record#create},
16297  * or an {@link Roo.data.Record} object
16298  *
16299  * 
16300  * created using {@link Roo.data.Record#create}.
16301  */
16302 Roo.data.ArrayReader = function(meta, recordType)
16303 {    
16304     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16305 };
16306
16307 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16308     
16309       /**
16310      * Create a data block containing Roo.data.Records from an XML document.
16311      * @param {Object} o An Array of row objects which represents the dataset.
16312      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16313      * a cache of Roo.data.Records.
16314      */
16315     readRecords : function(o)
16316     {
16317         var sid = this.meta ? this.meta.id : null;
16318         var recordType = this.recordType, fields = recordType.prototype.fields;
16319         var records = [];
16320         var root = o;
16321         for(var i = 0; i < root.length; i++){
16322             var n = root[i];
16323             var values = {};
16324             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16325             for(var j = 0, jlen = fields.length; j < jlen; j++){
16326                 var f = fields.items[j];
16327                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16328                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16329                 v = f.convert(v);
16330                 values[f.name] = v;
16331             }
16332             var record = new recordType(values, id);
16333             record.json = n;
16334             records[records.length] = record;
16335         }
16336         return {
16337             records : records,
16338             totalRecords : records.length
16339         };
16340     },
16341     // used when loading children.. @see loadDataFromChildren
16342     toLoadData: function(rec)
16343     {
16344         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16345         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16346         
16347     }
16348     
16349     
16350 });/*
16351  * - LGPL
16352  * * 
16353  */
16354
16355 /**
16356  * @class Roo.bootstrap.ComboBox
16357  * @extends Roo.bootstrap.TriggerField
16358  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16359  * @cfg {Boolean} append (true|false) default false
16360  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16361  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16362  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16363  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16364  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16365  * @cfg {Boolean} animate default true
16366  * @cfg {Boolean} emptyResultText only for touch device
16367  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16368  * @cfg {String} emptyTitle default ''
16369  * @cfg {Number} width fixed with? experimental
16370  * @constructor
16371  * Create a new ComboBox.
16372  * @param {Object} config Configuration options
16373  */
16374 Roo.bootstrap.ComboBox = function(config){
16375     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16376     this.addEvents({
16377         /**
16378          * @event expand
16379          * Fires when the dropdown list is expanded
16380         * @param {Roo.bootstrap.ComboBox} combo This combo box
16381         */
16382         'expand' : true,
16383         /**
16384          * @event collapse
16385          * Fires when the dropdown list is collapsed
16386         * @param {Roo.bootstrap.ComboBox} combo This combo box
16387         */
16388         'collapse' : true,
16389         /**
16390          * @event beforeselect
16391          * Fires before a list item is selected. Return false to cancel the selection.
16392         * @param {Roo.bootstrap.ComboBox} combo This combo box
16393         * @param {Roo.data.Record} record The data record returned from the underlying store
16394         * @param {Number} index The index of the selected item in the dropdown list
16395         */
16396         'beforeselect' : true,
16397         /**
16398          * @event select
16399          * Fires when a list item is selected
16400         * @param {Roo.bootstrap.ComboBox} combo This combo box
16401         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16402         * @param {Number} index The index of the selected item in the dropdown list
16403         */
16404         'select' : true,
16405         /**
16406          * @event beforequery
16407          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16408          * The event object passed has these properties:
16409         * @param {Roo.bootstrap.ComboBox} combo This combo box
16410         * @param {String} query The query
16411         * @param {Boolean} forceAll true to force "all" query
16412         * @param {Boolean} cancel true to cancel the query
16413         * @param {Object} e The query event object
16414         */
16415         'beforequery': true,
16416          /**
16417          * @event add
16418          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16419         * @param {Roo.bootstrap.ComboBox} combo This combo box
16420         */
16421         'add' : true,
16422         /**
16423          * @event edit
16424          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16425         * @param {Roo.bootstrap.ComboBox} combo This combo box
16426         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16427         */
16428         'edit' : true,
16429         /**
16430          * @event remove
16431          * Fires when the remove value from the combobox array
16432         * @param {Roo.bootstrap.ComboBox} combo This combo box
16433         */
16434         'remove' : true,
16435         /**
16436          * @event afterremove
16437          * Fires when the remove value from the combobox array
16438         * @param {Roo.bootstrap.ComboBox} combo This combo box
16439         */
16440         'afterremove' : true,
16441         /**
16442          * @event specialfilter
16443          * Fires when specialfilter
16444             * @param {Roo.bootstrap.ComboBox} combo This combo box
16445             */
16446         'specialfilter' : true,
16447         /**
16448          * @event tick
16449          * Fires when tick the element
16450             * @param {Roo.bootstrap.ComboBox} combo This combo box
16451             */
16452         'tick' : true,
16453         /**
16454          * @event touchviewdisplay
16455          * Fires when touch view require special display (default is using displayField)
16456             * @param {Roo.bootstrap.ComboBox} combo This combo box
16457             * @param {Object} cfg set html .
16458             */
16459         'touchviewdisplay' : true
16460         
16461     });
16462     
16463     this.item = [];
16464     this.tickItems = [];
16465     
16466     this.selectedIndex = -1;
16467     if(this.mode == 'local'){
16468         if(config.queryDelay === undefined){
16469             this.queryDelay = 10;
16470         }
16471         if(config.minChars === undefined){
16472             this.minChars = 0;
16473         }
16474     }
16475 };
16476
16477 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16478      
16479     /**
16480      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16481      * rendering into an Roo.Editor, defaults to false)
16482      */
16483     /**
16484      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16485      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16486      */
16487     /**
16488      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16489      */
16490     /**
16491      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16492      * the dropdown list (defaults to undefined, with no header element)
16493      */
16494
16495      /**
16496      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16497      */
16498      
16499      /**
16500      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16501      */
16502     listWidth: undefined,
16503     /**
16504      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16505      * mode = 'remote' or 'text' if mode = 'local')
16506      */
16507     displayField: undefined,
16508     
16509     /**
16510      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16511      * mode = 'remote' or 'value' if mode = 'local'). 
16512      * Note: use of a valueField requires the user make a selection
16513      * in order for a value to be mapped.
16514      */
16515     valueField: undefined,
16516     /**
16517      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16518      */
16519     modalTitle : '',
16520     
16521     /**
16522      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16523      * field's data value (defaults to the underlying DOM element's name)
16524      */
16525     hiddenName: undefined,
16526     /**
16527      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16528      */
16529     listClass: '',
16530     /**
16531      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16532      */
16533     selectedClass: 'active',
16534     
16535     /**
16536      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16537      */
16538     shadow:'sides',
16539     /**
16540      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16541      * anchor positions (defaults to 'tl-bl')
16542      */
16543     listAlign: 'tl-bl?',
16544     /**
16545      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16546      */
16547     maxHeight: 300,
16548     /**
16549      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16550      * query specified by the allQuery config option (defaults to 'query')
16551      */
16552     triggerAction: 'query',
16553     /**
16554      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16555      * (defaults to 4, does not apply if editable = false)
16556      */
16557     minChars : 4,
16558     /**
16559      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16560      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16561      */
16562     typeAhead: false,
16563     /**
16564      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16565      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16566      */
16567     queryDelay: 500,
16568     /**
16569      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16570      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16571      */
16572     pageSize: 0,
16573     /**
16574      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16575      * when editable = true (defaults to false)
16576      */
16577     selectOnFocus:false,
16578     /**
16579      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16580      */
16581     queryParam: 'query',
16582     /**
16583      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16584      * when mode = 'remote' (defaults to 'Loading...')
16585      */
16586     loadingText: 'Loading...',
16587     /**
16588      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16589      */
16590     resizable: false,
16591     /**
16592      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16593      */
16594     handleHeight : 8,
16595     /**
16596      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16597      * traditional select (defaults to true)
16598      */
16599     editable: true,
16600     /**
16601      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16602      */
16603     allQuery: '',
16604     /**
16605      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16606      */
16607     mode: 'remote',
16608     /**
16609      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16610      * listWidth has a higher value)
16611      */
16612     minListWidth : 70,
16613     /**
16614      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16615      * allow the user to set arbitrary text into the field (defaults to false)
16616      */
16617     forceSelection:false,
16618     /**
16619      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16620      * if typeAhead = true (defaults to 250)
16621      */
16622     typeAheadDelay : 250,
16623     /**
16624      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16625      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16626      */
16627     valueNotFoundText : undefined,
16628     /**
16629      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16630      */
16631     blockFocus : false,
16632     
16633     /**
16634      * @cfg {Boolean} disableClear Disable showing of clear button.
16635      */
16636     disableClear : false,
16637     /**
16638      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16639      */
16640     alwaysQuery : false,
16641     
16642     /**
16643      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16644      */
16645     multiple : false,
16646     
16647     /**
16648      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16649      */
16650     invalidClass : "has-warning",
16651     
16652     /**
16653      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16654      */
16655     validClass : "has-success",
16656     
16657     /**
16658      * @cfg {Boolean} specialFilter (true|false) special filter default false
16659      */
16660     specialFilter : false,
16661     
16662     /**
16663      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16664      */
16665     mobileTouchView : true,
16666     
16667     /**
16668      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16669      */
16670     useNativeIOS : false,
16671     
16672     /**
16673      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16674      */
16675     mobile_restrict_height : false,
16676     
16677     ios_options : false,
16678     
16679     //private
16680     addicon : false,
16681     editicon: false,
16682     
16683     page: 0,
16684     hasQuery: false,
16685     append: false,
16686     loadNext: false,
16687     autoFocus : true,
16688     tickable : false,
16689     btnPosition : 'right',
16690     triggerList : true,
16691     showToggleBtn : true,
16692     animate : true,
16693     emptyResultText: 'Empty',
16694     triggerText : 'Select',
16695     emptyTitle : '',
16696     width : false,
16697     
16698     // element that contains real text value.. (when hidden is used..)
16699     
16700     getAutoCreate : function()
16701     {   
16702         var cfg = false;
16703         //render
16704         /*
16705          * Render classic select for iso
16706          */
16707         
16708         if(Roo.isIOS && this.useNativeIOS){
16709             cfg = this.getAutoCreateNativeIOS();
16710             return cfg;
16711         }
16712         
16713         /*
16714          * Touch Devices
16715          */
16716         
16717         if(Roo.isTouch && this.mobileTouchView){
16718             cfg = this.getAutoCreateTouchView();
16719             return cfg;;
16720         }
16721         
16722         /*
16723          *  Normal ComboBox
16724          */
16725         if(!this.tickable){
16726             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16727             return cfg;
16728         }
16729         
16730         /*
16731          *  ComboBox with tickable selections
16732          */
16733              
16734         var align = this.labelAlign || this.parentLabelAlign();
16735         
16736         cfg = {
16737             cls : 'form-group roo-combobox-tickable' //input-group
16738         };
16739         
16740         var btn_text_select = '';
16741         var btn_text_done = '';
16742         var btn_text_cancel = '';
16743         
16744         if (this.btn_text_show) {
16745             btn_text_select = 'Select';
16746             btn_text_done = 'Done';
16747             btn_text_cancel = 'Cancel'; 
16748         }
16749         
16750         var buttons = {
16751             tag : 'div',
16752             cls : 'tickable-buttons',
16753             cn : [
16754                 {
16755                     tag : 'button',
16756                     type : 'button',
16757                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16758                     //html : this.triggerText
16759                     html: btn_text_select
16760                 },
16761                 {
16762                     tag : 'button',
16763                     type : 'button',
16764                     name : 'ok',
16765                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16766                     //html : 'Done'
16767                     html: btn_text_done
16768                 },
16769                 {
16770                     tag : 'button',
16771                     type : 'button',
16772                     name : 'cancel',
16773                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16774                     //html : 'Cancel'
16775                     html: btn_text_cancel
16776                 }
16777             ]
16778         };
16779         
16780         if(this.editable){
16781             buttons.cn.unshift({
16782                 tag: 'input',
16783                 cls: 'roo-select2-search-field-input'
16784             });
16785         }
16786         
16787         var _this = this;
16788         
16789         Roo.each(buttons.cn, function(c){
16790             if (_this.size) {
16791                 c.cls += ' btn-' + _this.size;
16792             }
16793
16794             if (_this.disabled) {
16795                 c.disabled = true;
16796             }
16797         });
16798         
16799         var box = {
16800             tag: 'div',
16801             style : 'display: contents',
16802             cn: [
16803                 {
16804                     tag: 'input',
16805                     type : 'hidden',
16806                     cls: 'form-hidden-field'
16807                 },
16808                 {
16809                     tag: 'ul',
16810                     cls: 'roo-select2-choices',
16811                     cn:[
16812                         {
16813                             tag: 'li',
16814                             cls: 'roo-select2-search-field',
16815                             cn: [
16816                                 buttons
16817                             ]
16818                         }
16819                     ]
16820                 }
16821             ]
16822         };
16823         
16824         var combobox = {
16825             cls: 'roo-select2-container input-group roo-select2-container-multi',
16826             cn: [
16827                 
16828                 box
16829 //                {
16830 //                    tag: 'ul',
16831 //                    cls: 'typeahead typeahead-long dropdown-menu',
16832 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16833 //                }
16834             ]
16835         };
16836         
16837         if(this.hasFeedback && !this.allowBlank){
16838             
16839             var feedback = {
16840                 tag: 'span',
16841                 cls: 'glyphicon form-control-feedback'
16842             };
16843
16844             combobox.cn.push(feedback);
16845         }
16846         
16847         
16848         
16849         var indicator = {
16850             tag : 'i',
16851             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16852             tooltip : 'This field is required'
16853         };
16854         if (Roo.bootstrap.version == 4) {
16855             indicator = {
16856                 tag : 'i',
16857                 style : 'display:none'
16858             };
16859         }
16860         if (align ==='left' && this.fieldLabel.length) {
16861             
16862             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16863             
16864             cfg.cn = [
16865                 indicator,
16866                 {
16867                     tag: 'label',
16868                     'for' :  id,
16869                     cls : 'control-label col-form-label',
16870                     html : this.fieldLabel
16871
16872                 },
16873                 {
16874                     cls : "", 
16875                     cn: [
16876                         combobox
16877                     ]
16878                 }
16879
16880             ];
16881             
16882             var labelCfg = cfg.cn[1];
16883             var contentCfg = cfg.cn[2];
16884             
16885
16886             if(this.indicatorpos == 'right'){
16887                 
16888                 cfg.cn = [
16889                     {
16890                         tag: 'label',
16891                         'for' :  id,
16892                         cls : 'control-label col-form-label',
16893                         cn : [
16894                             {
16895                                 tag : 'span',
16896                                 html : this.fieldLabel
16897                             },
16898                             indicator
16899                         ]
16900                     },
16901                     {
16902                         cls : "",
16903                         cn: [
16904                             combobox
16905                         ]
16906                     }
16907
16908                 ];
16909                 
16910                 
16911                 
16912                 labelCfg = cfg.cn[0];
16913                 contentCfg = cfg.cn[1];
16914             
16915             }
16916             
16917             if(this.labelWidth > 12){
16918                 labelCfg.style = "width: " + this.labelWidth + 'px';
16919             }
16920             if(this.width * 1 > 0){
16921                 contentCfg.style = "width: " + this.width + 'px';
16922             }
16923             if(this.labelWidth < 13 && this.labelmd == 0){
16924                 this.labelmd = this.labelWidth;
16925             }
16926             
16927             if(this.labellg > 0){
16928                 labelCfg.cls += ' col-lg-' + this.labellg;
16929                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16930             }
16931             
16932             if(this.labelmd > 0){
16933                 labelCfg.cls += ' col-md-' + this.labelmd;
16934                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16935             }
16936             
16937             if(this.labelsm > 0){
16938                 labelCfg.cls += ' col-sm-' + this.labelsm;
16939                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16940             }
16941             
16942             if(this.labelxs > 0){
16943                 labelCfg.cls += ' col-xs-' + this.labelxs;
16944                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16945             }
16946                 
16947                 
16948         } else if ( this.fieldLabel.length) {
16949 //                Roo.log(" label");
16950                  cfg.cn = [
16951                    indicator,
16952                     {
16953                         tag: 'label',
16954                         //cls : 'input-group-addon',
16955                         html : this.fieldLabel
16956                     },
16957                     combobox
16958                 ];
16959                 
16960                 if(this.indicatorpos == 'right'){
16961                     cfg.cn = [
16962                         {
16963                             tag: 'label',
16964                             //cls : 'input-group-addon',
16965                             html : this.fieldLabel
16966                         },
16967                         indicator,
16968                         combobox
16969                     ];
16970                     
16971                 }
16972
16973         } else {
16974             
16975 //                Roo.log(" no label && no align");
16976                 cfg = combobox
16977                      
16978                 
16979         }
16980          
16981         var settings=this;
16982         ['xs','sm','md','lg'].map(function(size){
16983             if (settings[size]) {
16984                 cfg.cls += ' col-' + size + '-' + settings[size];
16985             }
16986         });
16987         
16988         return cfg;
16989         
16990     },
16991     
16992     _initEventsCalled : false,
16993     
16994     // private
16995     initEvents: function()
16996     {   
16997         if (this._initEventsCalled) { // as we call render... prevent looping...
16998             return;
16999         }
17000         this._initEventsCalled = true;
17001         
17002         if (!this.store) {
17003             throw "can not find store for combo";
17004         }
17005         
17006         this.indicator = this.indicatorEl();
17007         
17008         this.store = Roo.factory(this.store, Roo.data);
17009         this.store.parent = this;
17010         
17011         // if we are building from html. then this element is so complex, that we can not really
17012         // use the rendered HTML.
17013         // so we have to trash and replace the previous code.
17014         if (Roo.XComponent.build_from_html) {
17015             // remove this element....
17016             var e = this.el.dom, k=0;
17017             while (e ) { e = e.previousSibling;  ++k;}
17018
17019             this.el.remove();
17020             
17021             this.el=false;
17022             this.rendered = false;
17023             
17024             this.render(this.parent().getChildContainer(true), k);
17025         }
17026         
17027         if(Roo.isIOS && this.useNativeIOS){
17028             this.initIOSView();
17029             return;
17030         }
17031         
17032         /*
17033          * Touch Devices
17034          */
17035         
17036         if(Roo.isTouch && this.mobileTouchView){
17037             this.initTouchView();
17038             return;
17039         }
17040         
17041         if(this.tickable){
17042             this.initTickableEvents();
17043             return;
17044         }
17045         
17046         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17047         
17048         if(this.hiddenName){
17049             
17050             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17051             
17052             this.hiddenField.dom.value =
17053                 this.hiddenValue !== undefined ? this.hiddenValue :
17054                 this.value !== undefined ? this.value : '';
17055
17056             // prevent input submission
17057             this.el.dom.removeAttribute('name');
17058             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17059              
17060              
17061         }
17062         //if(Roo.isGecko){
17063         //    this.el.dom.setAttribute('autocomplete', 'off');
17064         //}
17065         
17066         var cls = 'x-combo-list';
17067         
17068         //this.list = new Roo.Layer({
17069         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17070         //});
17071         
17072         var _this = this;
17073         
17074         (function(){
17075             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17076             _this.list.setWidth(lw);
17077         }).defer(100);
17078         
17079         this.list.on('mouseover', this.onViewOver, this);
17080         this.list.on('mousemove', this.onViewMove, this);
17081         this.list.on('scroll', this.onViewScroll, this);
17082         
17083         /*
17084         this.list.swallowEvent('mousewheel');
17085         this.assetHeight = 0;
17086
17087         if(this.title){
17088             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17089             this.assetHeight += this.header.getHeight();
17090         }
17091
17092         this.innerList = this.list.createChild({cls:cls+'-inner'});
17093         this.innerList.on('mouseover', this.onViewOver, this);
17094         this.innerList.on('mousemove', this.onViewMove, this);
17095         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17096         
17097         if(this.allowBlank && !this.pageSize && !this.disableClear){
17098             this.footer = this.list.createChild({cls:cls+'-ft'});
17099             this.pageTb = new Roo.Toolbar(this.footer);
17100            
17101         }
17102         if(this.pageSize){
17103             this.footer = this.list.createChild({cls:cls+'-ft'});
17104             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17105                     {pageSize: this.pageSize});
17106             
17107         }
17108         
17109         if (this.pageTb && this.allowBlank && !this.disableClear) {
17110             var _this = this;
17111             this.pageTb.add(new Roo.Toolbar.Fill(), {
17112                 cls: 'x-btn-icon x-btn-clear',
17113                 text: '&#160;',
17114                 handler: function()
17115                 {
17116                     _this.collapse();
17117                     _this.clearValue();
17118                     _this.onSelect(false, -1);
17119                 }
17120             });
17121         }
17122         if (this.footer) {
17123             this.assetHeight += this.footer.getHeight();
17124         }
17125         */
17126             
17127         if(!this.tpl){
17128             this.tpl = Roo.bootstrap.version == 4 ?
17129                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17130                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17131         }
17132
17133         this.view = new Roo.View(this.list, this.tpl, {
17134             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17135         });
17136         //this.view.wrapEl.setDisplayed(false);
17137         this.view.on('click', this.onViewClick, this);
17138         
17139         
17140         this.store.on('beforeload', this.onBeforeLoad, this);
17141         this.store.on('load', this.onLoad, this);
17142         this.store.on('loadexception', this.onLoadException, this);
17143         /*
17144         if(this.resizable){
17145             this.resizer = new Roo.Resizable(this.list,  {
17146                pinned:true, handles:'se'
17147             });
17148             this.resizer.on('resize', function(r, w, h){
17149                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17150                 this.listWidth = w;
17151                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17152                 this.restrictHeight();
17153             }, this);
17154             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17155         }
17156         */
17157         if(!this.editable){
17158             this.editable = true;
17159             this.setEditable(false);
17160         }
17161         
17162         /*
17163         
17164         if (typeof(this.events.add.listeners) != 'undefined') {
17165             
17166             this.addicon = this.wrap.createChild(
17167                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17168        
17169             this.addicon.on('click', function(e) {
17170                 this.fireEvent('add', this);
17171             }, this);
17172         }
17173         if (typeof(this.events.edit.listeners) != 'undefined') {
17174             
17175             this.editicon = this.wrap.createChild(
17176                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17177             if (this.addicon) {
17178                 this.editicon.setStyle('margin-left', '40px');
17179             }
17180             this.editicon.on('click', function(e) {
17181                 
17182                 // we fire even  if inothing is selected..
17183                 this.fireEvent('edit', this, this.lastData );
17184                 
17185             }, this);
17186         }
17187         */
17188         
17189         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17190             "up" : function(e){
17191                 this.inKeyMode = true;
17192                 this.selectPrev();
17193             },
17194
17195             "down" : function(e){
17196                 if(!this.isExpanded()){
17197                     this.onTriggerClick();
17198                 }else{
17199                     this.inKeyMode = true;
17200                     this.selectNext();
17201                 }
17202             },
17203
17204             "enter" : function(e){
17205 //                this.onViewClick();
17206                 //return true;
17207                 this.collapse();
17208                 
17209                 if(this.fireEvent("specialkey", this, e)){
17210                     this.onViewClick(false);
17211                 }
17212                 
17213                 return true;
17214             },
17215
17216             "esc" : function(e){
17217                 this.collapse();
17218             },
17219
17220             "tab" : function(e){
17221                 this.collapse();
17222                 
17223                 if(this.fireEvent("specialkey", this, e)){
17224                     this.onViewClick(false);
17225                 }
17226                 
17227                 return true;
17228             },
17229
17230             scope : this,
17231
17232             doRelay : function(foo, bar, hname){
17233                 if(hname == 'down' || this.scope.isExpanded()){
17234                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17235                 }
17236                 return true;
17237             },
17238
17239             forceKeyDown: true
17240         });
17241         
17242         
17243         this.queryDelay = Math.max(this.queryDelay || 10,
17244                 this.mode == 'local' ? 10 : 250);
17245         
17246         
17247         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17248         
17249         if(this.typeAhead){
17250             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17251         }
17252         if(this.editable !== false){
17253             this.inputEl().on("keyup", this.onKeyUp, this);
17254         }
17255         if(this.forceSelection){
17256             this.inputEl().on('blur', this.doForce, this);
17257         }
17258         
17259         if(this.multiple){
17260             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17261             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17262         }
17263     },
17264     
17265     initTickableEvents: function()
17266     {   
17267         this.createList();
17268         
17269         if(this.hiddenName){
17270             
17271             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17272             
17273             this.hiddenField.dom.value =
17274                 this.hiddenValue !== undefined ? this.hiddenValue :
17275                 this.value !== undefined ? this.value : '';
17276
17277             // prevent input submission
17278             this.el.dom.removeAttribute('name');
17279             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17280              
17281              
17282         }
17283         
17284 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17285         
17286         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17287         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17288         if(this.triggerList){
17289             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17290         }
17291          
17292         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17293         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17294         
17295         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17296         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17297         
17298         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17299         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17300         
17301         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17302         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17303         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17304         
17305         this.okBtn.hide();
17306         this.cancelBtn.hide();
17307         
17308         var _this = this;
17309         
17310         (function(){
17311             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17312             _this.list.setWidth(lw);
17313         }).defer(100);
17314         
17315         this.list.on('mouseover', this.onViewOver, this);
17316         this.list.on('mousemove', this.onViewMove, this);
17317         
17318         this.list.on('scroll', this.onViewScroll, this);
17319         
17320         if(!this.tpl){
17321             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17322                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17323         }
17324
17325         this.view = new Roo.View(this.list, this.tpl, {
17326             singleSelect:true,
17327             tickable:true,
17328             parent:this,
17329             store: this.store,
17330             selectedClass: this.selectedClass
17331         });
17332         
17333         //this.view.wrapEl.setDisplayed(false);
17334         this.view.on('click', this.onViewClick, this);
17335         
17336         
17337         
17338         this.store.on('beforeload', this.onBeforeLoad, this);
17339         this.store.on('load', this.onLoad, this);
17340         this.store.on('loadexception', this.onLoadException, this);
17341         
17342         if(this.editable){
17343             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17344                 "up" : function(e){
17345                     this.inKeyMode = true;
17346                     this.selectPrev();
17347                 },
17348
17349                 "down" : function(e){
17350                     this.inKeyMode = true;
17351                     this.selectNext();
17352                 },
17353
17354                 "enter" : function(e){
17355                     if(this.fireEvent("specialkey", this, e)){
17356                         this.onViewClick(false);
17357                     }
17358                     
17359                     return true;
17360                 },
17361
17362                 "esc" : function(e){
17363                     this.onTickableFooterButtonClick(e, false, false);
17364                 },
17365
17366                 "tab" : function(e){
17367                     this.fireEvent("specialkey", this, e);
17368                     
17369                     this.onTickableFooterButtonClick(e, false, false);
17370                     
17371                     return true;
17372                 },
17373
17374                 scope : this,
17375
17376                 doRelay : function(e, fn, key){
17377                     if(this.scope.isExpanded()){
17378                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17379                     }
17380                     return true;
17381                 },
17382
17383                 forceKeyDown: true
17384             });
17385         }
17386         
17387         this.queryDelay = Math.max(this.queryDelay || 10,
17388                 this.mode == 'local' ? 10 : 250);
17389         
17390         
17391         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17392         
17393         if(this.typeAhead){
17394             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17395         }
17396         
17397         if(this.editable !== false){
17398             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17399         }
17400         
17401         this.indicator = this.indicatorEl();
17402         
17403         if(this.indicator){
17404             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17405             this.indicator.hide();
17406         }
17407         
17408     },
17409
17410     onDestroy : function(){
17411         if(this.view){
17412             this.view.setStore(null);
17413             this.view.el.removeAllListeners();
17414             this.view.el.remove();
17415             this.view.purgeListeners();
17416         }
17417         if(this.list){
17418             this.list.dom.innerHTML  = '';
17419         }
17420         
17421         if(this.store){
17422             this.store.un('beforeload', this.onBeforeLoad, this);
17423             this.store.un('load', this.onLoad, this);
17424             this.store.un('loadexception', this.onLoadException, this);
17425         }
17426         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17427     },
17428
17429     // private
17430     fireKey : function(e){
17431         if(e.isNavKeyPress() && !this.list.isVisible()){
17432             this.fireEvent("specialkey", this, e);
17433         }
17434     },
17435
17436     // private
17437     onResize: function(w, h)
17438     {
17439         
17440         
17441 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17442 //        
17443 //        if(typeof w != 'number'){
17444 //            // we do not handle it!?!?
17445 //            return;
17446 //        }
17447 //        var tw = this.trigger.getWidth();
17448 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17449 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17450 //        var x = w - tw;
17451 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17452 //            
17453 //        //this.trigger.setStyle('left', x+'px');
17454 //        
17455 //        if(this.list && this.listWidth === undefined){
17456 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17457 //            this.list.setWidth(lw);
17458 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17459 //        }
17460         
17461     
17462         
17463     },
17464
17465     /**
17466      * Allow or prevent the user from directly editing the field text.  If false is passed,
17467      * the user will only be able to select from the items defined in the dropdown list.  This method
17468      * is the runtime equivalent of setting the 'editable' config option at config time.
17469      * @param {Boolean} value True to allow the user to directly edit the field text
17470      */
17471     setEditable : function(value){
17472         if(value == this.editable){
17473             return;
17474         }
17475         this.editable = value;
17476         if(!value){
17477             this.inputEl().dom.setAttribute('readOnly', true);
17478             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17479             this.inputEl().addClass('x-combo-noedit');
17480         }else{
17481             this.inputEl().dom.removeAttribute('readOnly');
17482             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17483             this.inputEl().removeClass('x-combo-noedit');
17484         }
17485     },
17486
17487     // private
17488     
17489     onBeforeLoad : function(combo,opts){
17490         if(!this.hasFocus){
17491             return;
17492         }
17493          if (!opts.add) {
17494             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17495          }
17496         this.restrictHeight();
17497         this.selectedIndex = -1;
17498     },
17499
17500     // private
17501     onLoad : function(){
17502         
17503         this.hasQuery = false;
17504         
17505         if(!this.hasFocus){
17506             return;
17507         }
17508         
17509         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17510             this.loading.hide();
17511         }
17512         
17513         if(this.store.getCount() > 0){
17514             
17515             this.expand();
17516             this.restrictHeight();
17517             if(this.lastQuery == this.allQuery){
17518                 if(this.editable && !this.tickable){
17519                     this.inputEl().dom.select();
17520                 }
17521                 
17522                 if(
17523                     !this.selectByValue(this.value, true) &&
17524                     this.autoFocus && 
17525                     (
17526                         !this.store.lastOptions ||
17527                         typeof(this.store.lastOptions.add) == 'undefined' || 
17528                         this.store.lastOptions.add != true
17529                     )
17530                 ){
17531                     this.select(0, true);
17532                 }
17533             }else{
17534                 if(this.autoFocus){
17535                     this.selectNext();
17536                 }
17537                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17538                     this.taTask.delay(this.typeAheadDelay);
17539                 }
17540             }
17541         }else{
17542             this.onEmptyResults();
17543         }
17544         
17545         //this.el.focus();
17546     },
17547     // private
17548     onLoadException : function()
17549     {
17550         this.hasQuery = false;
17551         
17552         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17553             this.loading.hide();
17554         }
17555         
17556         if(this.tickable && this.editable){
17557             return;
17558         }
17559         
17560         this.collapse();
17561         // only causes errors at present
17562         //Roo.log(this.store.reader.jsonData);
17563         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17564             // fixme
17565             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17566         //}
17567         
17568         
17569     },
17570     // private
17571     onTypeAhead : function(){
17572         if(this.store.getCount() > 0){
17573             var r = this.store.getAt(0);
17574             var newValue = r.data[this.displayField];
17575             var len = newValue.length;
17576             var selStart = this.getRawValue().length;
17577             
17578             if(selStart != len){
17579                 this.setRawValue(newValue);
17580                 this.selectText(selStart, newValue.length);
17581             }
17582         }
17583     },
17584
17585     // private
17586     onSelect : function(record, index){
17587         
17588         if(this.fireEvent('beforeselect', this, record, index) !== false){
17589         
17590             this.setFromData(index > -1 ? record.data : false);
17591             
17592             this.collapse();
17593             this.fireEvent('select', this, record, index);
17594         }
17595     },
17596
17597     /**
17598      * Returns the currently selected field value or empty string if no value is set.
17599      * @return {String} value The selected value
17600      */
17601     getValue : function()
17602     {
17603         if(Roo.isIOS && this.useNativeIOS){
17604             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17605         }
17606         
17607         if(this.multiple){
17608             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17609         }
17610         
17611         if(this.valueField){
17612             return typeof this.value != 'undefined' ? this.value : '';
17613         }else{
17614             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17615         }
17616     },
17617     
17618     getRawValue : function()
17619     {
17620         if(Roo.isIOS && this.useNativeIOS){
17621             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17622         }
17623         
17624         var v = this.inputEl().getValue();
17625         
17626         return v;
17627     },
17628
17629     /**
17630      * Clears any text/value currently set in the field
17631      */
17632     clearValue : function(){
17633         
17634         if(this.hiddenField){
17635             this.hiddenField.dom.value = '';
17636         }
17637         this.value = '';
17638         this.setRawValue('');
17639         this.lastSelectionText = '';
17640         this.lastData = false;
17641         
17642         var close = this.closeTriggerEl();
17643         
17644         if(close){
17645             close.hide();
17646         }
17647         
17648         this.validate();
17649         
17650     },
17651
17652     /**
17653      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17654      * will be displayed in the field.  If the value does not match the data value of an existing item,
17655      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17656      * Otherwise the field will be blank (although the value will still be set).
17657      * @param {String} value The value to match
17658      */
17659     setValue : function(v)
17660     {
17661         if(Roo.isIOS && this.useNativeIOS){
17662             this.setIOSValue(v);
17663             return;
17664         }
17665         
17666         if(this.multiple){
17667             this.syncValue();
17668             return;
17669         }
17670         
17671         var text = v;
17672         if(this.valueField){
17673             var r = this.findRecord(this.valueField, v);
17674             if(r){
17675                 text = r.data[this.displayField];
17676             }else if(this.valueNotFoundText !== undefined){
17677                 text = this.valueNotFoundText;
17678             }
17679         }
17680         this.lastSelectionText = text;
17681         if(this.hiddenField){
17682             this.hiddenField.dom.value = v;
17683         }
17684         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17685         this.value = v;
17686         
17687         var close = this.closeTriggerEl();
17688         
17689         if(close){
17690             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17691         }
17692         
17693         this.validate();
17694     },
17695     /**
17696      * @property {Object} the last set data for the element
17697      */
17698     
17699     lastData : false,
17700     /**
17701      * Sets the value of the field based on a object which is related to the record format for the store.
17702      * @param {Object} value the value to set as. or false on reset?
17703      */
17704     setFromData : function(o){
17705         
17706         if(this.multiple){
17707             this.addItem(o);
17708             return;
17709         }
17710             
17711         var dv = ''; // display value
17712         var vv = ''; // value value..
17713         this.lastData = o;
17714         if (this.displayField) {
17715             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17716         } else {
17717             // this is an error condition!!!
17718             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17719         }
17720         
17721         if(this.valueField){
17722             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17723         }
17724         
17725         var close = this.closeTriggerEl();
17726         
17727         if(close){
17728             if(dv.length || vv * 1 > 0){
17729                 close.show() ;
17730                 this.blockFocus=true;
17731             } else {
17732                 close.hide();
17733             }             
17734         }
17735         
17736         if(this.hiddenField){
17737             this.hiddenField.dom.value = vv;
17738             
17739             this.lastSelectionText = dv;
17740             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17741             this.value = vv;
17742             return;
17743         }
17744         // no hidden field.. - we store the value in 'value', but still display
17745         // display field!!!!
17746         this.lastSelectionText = dv;
17747         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17748         this.value = vv;
17749         
17750         
17751         
17752     },
17753     // private
17754     reset : function(){
17755         // overridden so that last data is reset..
17756         
17757         if(this.multiple){
17758             this.clearItem();
17759             return;
17760         }
17761         
17762         this.setValue(this.originalValue);
17763         //this.clearInvalid();
17764         this.lastData = false;
17765         if (this.view) {
17766             this.view.clearSelections();
17767         }
17768         
17769         this.validate();
17770     },
17771     // private
17772     findRecord : function(prop, value){
17773         var record;
17774         if(this.store.getCount() > 0){
17775             this.store.each(function(r){
17776                 if(r.data[prop] == value){
17777                     record = r;
17778                     return false;
17779                 }
17780                 return true;
17781             });
17782         }
17783         return record;
17784     },
17785     
17786     getName: function()
17787     {
17788         // returns hidden if it's set..
17789         if (!this.rendered) {return ''};
17790         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17791         
17792     },
17793     // private
17794     onViewMove : function(e, t){
17795         this.inKeyMode = false;
17796     },
17797
17798     // private
17799     onViewOver : function(e, t){
17800         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17801             return;
17802         }
17803         var item = this.view.findItemFromChild(t);
17804         
17805         if(item){
17806             var index = this.view.indexOf(item);
17807             this.select(index, false);
17808         }
17809     },
17810
17811     // private
17812     onViewClick : function(view, doFocus, el, e)
17813     {
17814         var index = this.view.getSelectedIndexes()[0];
17815         
17816         var r = this.store.getAt(index);
17817         
17818         if(this.tickable){
17819             
17820             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17821                 return;
17822             }
17823             
17824             var rm = false;
17825             var _this = this;
17826             
17827             Roo.each(this.tickItems, function(v,k){
17828                 
17829                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17830                     Roo.log(v);
17831                     _this.tickItems.splice(k, 1);
17832                     
17833                     if(typeof(e) == 'undefined' && view == false){
17834                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17835                     }
17836                     
17837                     rm = true;
17838                     return;
17839                 }
17840             });
17841             
17842             if(rm){
17843                 return;
17844             }
17845             
17846             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17847                 this.tickItems.push(r.data);
17848             }
17849             
17850             if(typeof(e) == 'undefined' && view == false){
17851                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17852             }
17853                     
17854             return;
17855         }
17856         
17857         if(r){
17858             this.onSelect(r, index);
17859         }
17860         if(doFocus !== false && !this.blockFocus){
17861             this.inputEl().focus();
17862         }
17863     },
17864
17865     // private
17866     restrictHeight : function(){
17867         //this.innerList.dom.style.height = '';
17868         //var inner = this.innerList.dom;
17869         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17870         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17871         //this.list.beginUpdate();
17872         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17873         this.list.alignTo(this.inputEl(), this.listAlign);
17874         this.list.alignTo(this.inputEl(), this.listAlign);
17875         //this.list.endUpdate();
17876     },
17877
17878     // private
17879     onEmptyResults : function(){
17880         
17881         if(this.tickable && this.editable){
17882             this.hasFocus = false;
17883             this.restrictHeight();
17884             return;
17885         }
17886         
17887         this.collapse();
17888     },
17889
17890     /**
17891      * Returns true if the dropdown list is expanded, else false.
17892      */
17893     isExpanded : function(){
17894         return this.list.isVisible();
17895     },
17896
17897     /**
17898      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17899      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17900      * @param {String} value The data value of the item to select
17901      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17902      * selected item if it is not currently in view (defaults to true)
17903      * @return {Boolean} True if the value matched an item in the list, else false
17904      */
17905     selectByValue : function(v, scrollIntoView){
17906         if(v !== undefined && v !== null){
17907             var r = this.findRecord(this.valueField || this.displayField, v);
17908             if(r){
17909                 this.select(this.store.indexOf(r), scrollIntoView);
17910                 return true;
17911             }
17912         }
17913         return false;
17914     },
17915
17916     /**
17917      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17918      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17919      * @param {Number} index The zero-based index of the list item to select
17920      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17921      * selected item if it is not currently in view (defaults to true)
17922      */
17923     select : function(index, scrollIntoView){
17924         this.selectedIndex = index;
17925         this.view.select(index);
17926         if(scrollIntoView !== false){
17927             var el = this.view.getNode(index);
17928             /*
17929              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17930              */
17931             if(el){
17932                 this.list.scrollChildIntoView(el, false);
17933             }
17934         }
17935     },
17936
17937     // private
17938     selectNext : function(){
17939         var ct = this.store.getCount();
17940         if(ct > 0){
17941             if(this.selectedIndex == -1){
17942                 this.select(0);
17943             }else if(this.selectedIndex < ct-1){
17944                 this.select(this.selectedIndex+1);
17945             }
17946         }
17947     },
17948
17949     // private
17950     selectPrev : function(){
17951         var ct = this.store.getCount();
17952         if(ct > 0){
17953             if(this.selectedIndex == -1){
17954                 this.select(0);
17955             }else if(this.selectedIndex != 0){
17956                 this.select(this.selectedIndex-1);
17957             }
17958         }
17959     },
17960
17961     // private
17962     onKeyUp : function(e){
17963         if(this.editable !== false && !e.isSpecialKey()){
17964             this.lastKey = e.getKey();
17965             this.dqTask.delay(this.queryDelay);
17966         }
17967     },
17968
17969     // private
17970     validateBlur : function(){
17971         return !this.list || !this.list.isVisible();   
17972     },
17973
17974     // private
17975     initQuery : function(){
17976         
17977         var v = this.getRawValue();
17978         
17979         if(this.tickable && this.editable){
17980             v = this.tickableInputEl().getValue();
17981         }
17982         
17983         this.doQuery(v);
17984     },
17985
17986     // private
17987     doForce : function(){
17988         if(this.inputEl().dom.value.length > 0){
17989             this.inputEl().dom.value =
17990                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17991              
17992         }
17993     },
17994
17995     /**
17996      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17997      * query allowing the query action to be canceled if needed.
17998      * @param {String} query The SQL query to execute
17999      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18000      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18001      * saved in the current store (defaults to false)
18002      */
18003     doQuery : function(q, forceAll){
18004         
18005         if(q === undefined || q === null){
18006             q = '';
18007         }
18008         var qe = {
18009             query: q,
18010             forceAll: forceAll,
18011             combo: this,
18012             cancel:false
18013         };
18014         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18015             return false;
18016         }
18017         q = qe.query;
18018         
18019         forceAll = qe.forceAll;
18020         if(forceAll === true || (q.length >= this.minChars)){
18021             
18022             this.hasQuery = true;
18023             
18024             if(this.lastQuery != q || this.alwaysQuery){
18025                 this.lastQuery = q;
18026                 if(this.mode == 'local'){
18027                     this.selectedIndex = -1;
18028                     if(forceAll){
18029                         this.store.clearFilter();
18030                     }else{
18031                         
18032                         if(this.specialFilter){
18033                             this.fireEvent('specialfilter', this);
18034                             this.onLoad();
18035                             return;
18036                         }
18037                         
18038                         this.store.filter(this.displayField, q);
18039                     }
18040                     
18041                     this.store.fireEvent("datachanged", this.store);
18042                     
18043                     this.onLoad();
18044                     
18045                     
18046                 }else{
18047                     
18048                     this.store.baseParams[this.queryParam] = q;
18049                     
18050                     var options = {params : this.getParams(q)};
18051                     
18052                     if(this.loadNext){
18053                         options.add = true;
18054                         options.params.start = this.page * this.pageSize;
18055                     }
18056                     
18057                     this.store.load(options);
18058                     
18059                     /*
18060                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18061                      *  we should expand the list on onLoad
18062                      *  so command out it
18063                      */
18064 //                    this.expand();
18065                 }
18066             }else{
18067                 this.selectedIndex = -1;
18068                 this.onLoad();   
18069             }
18070         }
18071         
18072         this.loadNext = false;
18073     },
18074     
18075     // private
18076     getParams : function(q){
18077         var p = {};
18078         //p[this.queryParam] = q;
18079         
18080         if(this.pageSize){
18081             p.start = 0;
18082             p.limit = this.pageSize;
18083         }
18084         return p;
18085     },
18086
18087     /**
18088      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18089      */
18090     collapse : function(){
18091         if(!this.isExpanded()){
18092             return;
18093         }
18094         
18095         this.list.hide();
18096         
18097         this.hasFocus = false;
18098         
18099         if(this.tickable){
18100             this.okBtn.hide();
18101             this.cancelBtn.hide();
18102             this.trigger.show();
18103             
18104             if(this.editable){
18105                 this.tickableInputEl().dom.value = '';
18106                 this.tickableInputEl().blur();
18107             }
18108             
18109         }
18110         
18111         Roo.get(document).un('mousedown', this.collapseIf, this);
18112         Roo.get(document).un('mousewheel', this.collapseIf, this);
18113         if (!this.editable) {
18114             Roo.get(document).un('keydown', this.listKeyPress, this);
18115         }
18116         this.fireEvent('collapse', this);
18117         
18118         this.validate();
18119     },
18120
18121     // private
18122     collapseIf : function(e){
18123         var in_combo  = e.within(this.el);
18124         var in_list =  e.within(this.list);
18125         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18126         
18127         if (in_combo || in_list || is_list) {
18128             //e.stopPropagation();
18129             return;
18130         }
18131         
18132         if(this.tickable){
18133             this.onTickableFooterButtonClick(e, false, false);
18134         }
18135
18136         this.collapse();
18137         
18138     },
18139
18140     /**
18141      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18142      */
18143     expand : function(){
18144        
18145         if(this.isExpanded() || !this.hasFocus){
18146             return;
18147         }
18148         
18149         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18150         this.list.setWidth(lw);
18151         
18152         Roo.log('expand');
18153         
18154         this.list.show();
18155         
18156         this.restrictHeight();
18157         
18158         if(this.tickable){
18159             
18160             this.tickItems = Roo.apply([], this.item);
18161             
18162             this.okBtn.show();
18163             this.cancelBtn.show();
18164             this.trigger.hide();
18165             
18166             if(this.editable){
18167                 this.tickableInputEl().focus();
18168             }
18169             
18170         }
18171         
18172         Roo.get(document).on('mousedown', this.collapseIf, this);
18173         Roo.get(document).on('mousewheel', this.collapseIf, this);
18174         if (!this.editable) {
18175             Roo.get(document).on('keydown', this.listKeyPress, this);
18176         }
18177         
18178         this.fireEvent('expand', this);
18179     },
18180
18181     // private
18182     // Implements the default empty TriggerField.onTriggerClick function
18183     onTriggerClick : function(e)
18184     {
18185         Roo.log('trigger click');
18186         
18187         if(this.disabled || !this.triggerList){
18188             return;
18189         }
18190         
18191         this.page = 0;
18192         this.loadNext = false;
18193         
18194         if(this.isExpanded()){
18195             this.collapse();
18196             if (!this.blockFocus) {
18197                 this.inputEl().focus();
18198             }
18199             
18200         }else {
18201             this.hasFocus = true;
18202             if(this.triggerAction == 'all') {
18203                 this.doQuery(this.allQuery, true);
18204             } else {
18205                 this.doQuery(this.getRawValue());
18206             }
18207             if (!this.blockFocus) {
18208                 this.inputEl().focus();
18209             }
18210         }
18211     },
18212     
18213     onTickableTriggerClick : function(e)
18214     {
18215         if(this.disabled){
18216             return;
18217         }
18218         
18219         this.page = 0;
18220         this.loadNext = false;
18221         this.hasFocus = true;
18222         
18223         if(this.triggerAction == 'all') {
18224             this.doQuery(this.allQuery, true);
18225         } else {
18226             this.doQuery(this.getRawValue());
18227         }
18228     },
18229     
18230     onSearchFieldClick : function(e)
18231     {
18232         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18233             this.onTickableFooterButtonClick(e, false, false);
18234             return;
18235         }
18236         
18237         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18238             return;
18239         }
18240         
18241         this.page = 0;
18242         this.loadNext = false;
18243         this.hasFocus = true;
18244         
18245         if(this.triggerAction == 'all') {
18246             this.doQuery(this.allQuery, true);
18247         } else {
18248             this.doQuery(this.getRawValue());
18249         }
18250     },
18251     
18252     listKeyPress : function(e)
18253     {
18254         //Roo.log('listkeypress');
18255         // scroll to first matching element based on key pres..
18256         if (e.isSpecialKey()) {
18257             return false;
18258         }
18259         var k = String.fromCharCode(e.getKey()).toUpperCase();
18260         //Roo.log(k);
18261         var match  = false;
18262         var csel = this.view.getSelectedNodes();
18263         var cselitem = false;
18264         if (csel.length) {
18265             var ix = this.view.indexOf(csel[0]);
18266             cselitem  = this.store.getAt(ix);
18267             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18268                 cselitem = false;
18269             }
18270             
18271         }
18272         
18273         this.store.each(function(v) { 
18274             if (cselitem) {
18275                 // start at existing selection.
18276                 if (cselitem.id == v.id) {
18277                     cselitem = false;
18278                 }
18279                 return true;
18280             }
18281                 
18282             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18283                 match = this.store.indexOf(v);
18284                 return false;
18285             }
18286             return true;
18287         }, this);
18288         
18289         if (match === false) {
18290             return true; // no more action?
18291         }
18292         // scroll to?
18293         this.view.select(match);
18294         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18295         sn.scrollIntoView(sn.dom.parentNode, false);
18296     },
18297     
18298     onViewScroll : function(e, t){
18299         
18300         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){
18301             return;
18302         }
18303         
18304         this.hasQuery = true;
18305         
18306         this.loading = this.list.select('.loading', true).first();
18307         
18308         if(this.loading === null){
18309             this.list.createChild({
18310                 tag: 'div',
18311                 cls: 'loading roo-select2-more-results roo-select2-active',
18312                 html: 'Loading more results...'
18313             });
18314             
18315             this.loading = this.list.select('.loading', true).first();
18316             
18317             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18318             
18319             this.loading.hide();
18320         }
18321         
18322         this.loading.show();
18323         
18324         var _combo = this;
18325         
18326         this.page++;
18327         this.loadNext = true;
18328         
18329         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18330         
18331         return;
18332     },
18333     
18334     addItem : function(o)
18335     {   
18336         var dv = ''; // display value
18337         
18338         if (this.displayField) {
18339             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18340         } else {
18341             // this is an error condition!!!
18342             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18343         }
18344         
18345         if(!dv.length){
18346             return;
18347         }
18348         
18349         var choice = this.choices.createChild({
18350             tag: 'li',
18351             cls: 'roo-select2-search-choice',
18352             cn: [
18353                 {
18354                     tag: 'div',
18355                     html: dv
18356                 },
18357                 {
18358                     tag: 'a',
18359                     href: '#',
18360                     cls: 'roo-select2-search-choice-close fa fa-times',
18361                     tabindex: '-1'
18362                 }
18363             ]
18364             
18365         }, this.searchField);
18366         
18367         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18368         
18369         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18370         
18371         this.item.push(o);
18372         
18373         this.lastData = o;
18374         
18375         this.syncValue();
18376         
18377         this.inputEl().dom.value = '';
18378         
18379         this.validate();
18380     },
18381     
18382     onRemoveItem : function(e, _self, o)
18383     {
18384         e.preventDefault();
18385         
18386         this.lastItem = Roo.apply([], this.item);
18387         
18388         var index = this.item.indexOf(o.data) * 1;
18389         
18390         if( index < 0){
18391             Roo.log('not this item?!');
18392             return;
18393         }
18394         
18395         this.item.splice(index, 1);
18396         o.item.remove();
18397         
18398         this.syncValue();
18399         
18400         this.fireEvent('remove', this, e);
18401         
18402         this.validate();
18403         
18404     },
18405     
18406     syncValue : function()
18407     {
18408         if(!this.item.length){
18409             this.clearValue();
18410             return;
18411         }
18412             
18413         var value = [];
18414         var _this = this;
18415         Roo.each(this.item, function(i){
18416             if(_this.valueField){
18417                 value.push(i[_this.valueField]);
18418                 return;
18419             }
18420
18421             value.push(i);
18422         });
18423
18424         this.value = value.join(',');
18425
18426         if(this.hiddenField){
18427             this.hiddenField.dom.value = this.value;
18428         }
18429         
18430         this.store.fireEvent("datachanged", this.store);
18431         
18432         this.validate();
18433     },
18434     
18435     clearItem : function()
18436     {
18437         if(!this.multiple){
18438             return;
18439         }
18440         
18441         this.item = [];
18442         
18443         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18444            c.remove();
18445         });
18446         
18447         this.syncValue();
18448         
18449         this.validate();
18450         
18451         if(this.tickable && !Roo.isTouch){
18452             this.view.refresh();
18453         }
18454     },
18455     
18456     inputEl: function ()
18457     {
18458         if(Roo.isIOS && this.useNativeIOS){
18459             return this.el.select('select.roo-ios-select', true).first();
18460         }
18461         
18462         if(Roo.isTouch && this.mobileTouchView){
18463             return this.el.select('input.form-control',true).first();
18464         }
18465         
18466         if(this.tickable){
18467             return this.searchField;
18468         }
18469         
18470         return this.el.select('input.form-control',true).first();
18471     },
18472     
18473     onTickableFooterButtonClick : function(e, btn, el)
18474     {
18475         e.preventDefault();
18476         
18477         this.lastItem = Roo.apply([], this.item);
18478         
18479         if(btn && btn.name == 'cancel'){
18480             this.tickItems = Roo.apply([], this.item);
18481             this.collapse();
18482             return;
18483         }
18484         
18485         this.clearItem();
18486         
18487         var _this = this;
18488         
18489         Roo.each(this.tickItems, function(o){
18490             _this.addItem(o);
18491         });
18492         
18493         this.collapse();
18494         
18495     },
18496     
18497     validate : function()
18498     {
18499         if(this.getVisibilityEl().hasClass('hidden')){
18500             return true;
18501         }
18502         
18503         var v = this.getRawValue();
18504         
18505         if(this.multiple){
18506             v = this.getValue();
18507         }
18508         
18509         if(this.disabled || this.allowBlank || v.length){
18510             this.markValid();
18511             return true;
18512         }
18513         
18514         this.markInvalid();
18515         return false;
18516     },
18517     
18518     tickableInputEl : function()
18519     {
18520         if(!this.tickable || !this.editable){
18521             return this.inputEl();
18522         }
18523         
18524         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18525     },
18526     
18527     
18528     getAutoCreateTouchView : function()
18529     {
18530         var id = Roo.id();
18531         
18532         var cfg = {
18533             cls: 'form-group' //input-group
18534         };
18535         
18536         var input =  {
18537             tag: 'input',
18538             id : id,
18539             type : this.inputType,
18540             cls : 'form-control x-combo-noedit',
18541             autocomplete: 'new-password',
18542             placeholder : this.placeholder || '',
18543             readonly : true
18544         };
18545         
18546         if (this.name) {
18547             input.name = this.name;
18548         }
18549         
18550         if (this.size) {
18551             input.cls += ' input-' + this.size;
18552         }
18553         
18554         if (this.disabled) {
18555             input.disabled = true;
18556         }
18557         
18558         var inputblock = {
18559             cls : 'roo-combobox-wrap',
18560             cn : [
18561                 input
18562             ]
18563         };
18564         
18565         if(this.before){
18566             inputblock.cls += ' input-group';
18567             
18568             inputblock.cn.unshift({
18569                 tag :'span',
18570                 cls : 'input-group-addon input-group-prepend input-group-text',
18571                 html : this.before
18572             });
18573         }
18574         
18575         if(this.removable && !this.multiple){
18576             inputblock.cls += ' roo-removable';
18577             
18578             inputblock.cn.push({
18579                 tag: 'button',
18580                 html : 'x',
18581                 cls : 'roo-combo-removable-btn close'
18582             });
18583         }
18584
18585         if(this.hasFeedback && !this.allowBlank){
18586             
18587             inputblock.cls += ' has-feedback';
18588             
18589             inputblock.cn.push({
18590                 tag: 'span',
18591                 cls: 'glyphicon form-control-feedback'
18592             });
18593             
18594         }
18595         
18596         if (this.after) {
18597             
18598             inputblock.cls += (this.before) ? '' : ' input-group';
18599             
18600             inputblock.cn.push({
18601                 tag :'span',
18602                 cls : 'input-group-addon input-group-append input-group-text',
18603                 html : this.after
18604             });
18605         }
18606
18607         
18608         var ibwrap = inputblock;
18609         
18610         if(this.multiple){
18611             ibwrap = {
18612                 tag: 'ul',
18613                 cls: 'roo-select2-choices',
18614                 cn:[
18615                     {
18616                         tag: 'li',
18617                         cls: 'roo-select2-search-field',
18618                         cn: [
18619
18620                             inputblock
18621                         ]
18622                     }
18623                 ]
18624             };
18625         
18626             
18627         }
18628         
18629         var combobox = {
18630             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18631             cn: [
18632                 {
18633                     tag: 'input',
18634                     type : 'hidden',
18635                     cls: 'form-hidden-field'
18636                 },
18637                 ibwrap
18638             ]
18639         };
18640         
18641         if(!this.multiple && this.showToggleBtn){
18642             
18643             var caret = {
18644                 cls: 'caret'
18645             };
18646             
18647             if (this.caret != false) {
18648                 caret = {
18649                      tag: 'i',
18650                      cls: 'fa fa-' + this.caret
18651                 };
18652                 
18653             }
18654             
18655             combobox.cn.push({
18656                 tag :'span',
18657                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18658                 cn : [
18659                     Roo.bootstrap.version == 3 ? caret : '',
18660                     {
18661                         tag: 'span',
18662                         cls: 'combobox-clear',
18663                         cn  : [
18664                             {
18665                                 tag : 'i',
18666                                 cls: 'icon-remove'
18667                             }
18668                         ]
18669                     }
18670                 ]
18671
18672             })
18673         }
18674         
18675         if(this.multiple){
18676             combobox.cls += ' roo-select2-container-multi';
18677         }
18678         
18679         var required =  this.allowBlank ?  {
18680                     tag : 'i',
18681                     style: 'display: none'
18682                 } : {
18683                    tag : 'i',
18684                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18685                    tooltip : 'This field is required'
18686                 };
18687         
18688         var align = this.labelAlign || this.parentLabelAlign();
18689         
18690         if (align ==='left' && this.fieldLabel.length) {
18691
18692             cfg.cn = [
18693                 required,
18694                 {
18695                     tag: 'label',
18696                     cls : 'control-label col-form-label',
18697                     html : this.fieldLabel
18698
18699                 },
18700                 {
18701                     cls : 'roo-combobox-wrap ', 
18702                     cn: [
18703                         combobox
18704                     ]
18705                 }
18706             ];
18707             
18708             var labelCfg = cfg.cn[1];
18709             var contentCfg = cfg.cn[2];
18710             
18711
18712             if(this.indicatorpos == 'right'){
18713                 cfg.cn = [
18714                     {
18715                         tag: 'label',
18716                         'for' :  id,
18717                         cls : 'control-label col-form-label',
18718                         cn : [
18719                             {
18720                                 tag : 'span',
18721                                 html : this.fieldLabel
18722                             },
18723                             required
18724                         ]
18725                     },
18726                     {
18727                         cls : "roo-combobox-wrap ",
18728                         cn: [
18729                             combobox
18730                         ]
18731                     }
18732
18733                 ];
18734                 
18735                 labelCfg = cfg.cn[0];
18736                 contentCfg = cfg.cn[1];
18737             }
18738             
18739            
18740             
18741             if(this.labelWidth > 12){
18742                 labelCfg.style = "width: " + this.labelWidth + 'px';
18743             }
18744            
18745             if(this.labelWidth < 13 && this.labelmd == 0){
18746                 this.labelmd = this.labelWidth;
18747             }
18748             
18749             if(this.labellg > 0){
18750                 labelCfg.cls += ' col-lg-' + this.labellg;
18751                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18752             }
18753             
18754             if(this.labelmd > 0){
18755                 labelCfg.cls += ' col-md-' + this.labelmd;
18756                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18757             }
18758             
18759             if(this.labelsm > 0){
18760                 labelCfg.cls += ' col-sm-' + this.labelsm;
18761                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18762             }
18763             
18764             if(this.labelxs > 0){
18765                 labelCfg.cls += ' col-xs-' + this.labelxs;
18766                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18767             }
18768                 
18769                 
18770         } else if ( this.fieldLabel.length) {
18771             cfg.cn = [
18772                required,
18773                 {
18774                     tag: 'label',
18775                     cls : 'control-label',
18776                     html : this.fieldLabel
18777
18778                 },
18779                 {
18780                     cls : '', 
18781                     cn: [
18782                         combobox
18783                     ]
18784                 }
18785             ];
18786             
18787             if(this.indicatorpos == 'right'){
18788                 cfg.cn = [
18789                     {
18790                         tag: 'label',
18791                         cls : 'control-label',
18792                         html : this.fieldLabel,
18793                         cn : [
18794                             required
18795                         ]
18796                     },
18797                     {
18798                         cls : '', 
18799                         cn: [
18800                             combobox
18801                         ]
18802                     }
18803                 ];
18804             }
18805         } else {
18806             cfg.cn = combobox;    
18807         }
18808         
18809         
18810         var settings = this;
18811         
18812         ['xs','sm','md','lg'].map(function(size){
18813             if (settings[size]) {
18814                 cfg.cls += ' col-' + size + '-' + settings[size];
18815             }
18816         });
18817         
18818         return cfg;
18819     },
18820     
18821     initTouchView : function()
18822     {
18823         this.renderTouchView();
18824         
18825         this.touchViewEl.on('scroll', function(){
18826             this.el.dom.scrollTop = 0;
18827         }, this);
18828         
18829         this.originalValue = this.getValue();
18830         
18831         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18832         
18833         this.inputEl().on("click", this.showTouchView, this);
18834         if (this.triggerEl) {
18835             this.triggerEl.on("click", this.showTouchView, this);
18836         }
18837         
18838         
18839         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18840         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18841         
18842         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18843         
18844         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18845         this.store.on('load', this.onTouchViewLoad, this);
18846         this.store.on('loadexception', this.onTouchViewLoadException, this);
18847         
18848         if(this.hiddenName){
18849             
18850             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18851             
18852             this.hiddenField.dom.value =
18853                 this.hiddenValue !== undefined ? this.hiddenValue :
18854                 this.value !== undefined ? this.value : '';
18855         
18856             this.el.dom.removeAttribute('name');
18857             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18858         }
18859         
18860         if(this.multiple){
18861             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18862             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18863         }
18864         
18865         if(this.removable && !this.multiple){
18866             var close = this.closeTriggerEl();
18867             if(close){
18868                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18869                 close.on('click', this.removeBtnClick, this, close);
18870             }
18871         }
18872         /*
18873          * fix the bug in Safari iOS8
18874          */
18875         this.inputEl().on("focus", function(e){
18876             document.activeElement.blur();
18877         }, this);
18878         
18879         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18880         
18881         return;
18882         
18883         
18884     },
18885     
18886     renderTouchView : function()
18887     {
18888         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18889         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         
18891         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18892         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893         
18894         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18895         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896         this.touchViewBodyEl.setStyle('overflow', 'auto');
18897         
18898         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18899         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900         
18901         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18902         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18903         
18904     },
18905     
18906     showTouchView : function()
18907     {
18908         if(this.disabled){
18909             return;
18910         }
18911         
18912         this.touchViewHeaderEl.hide();
18913
18914         if(this.modalTitle.length){
18915             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18916             this.touchViewHeaderEl.show();
18917         }
18918
18919         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18920         this.touchViewEl.show();
18921
18922         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18923         
18924         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18925         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18926
18927         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18928
18929         if(this.modalTitle.length){
18930             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18931         }
18932         
18933         this.touchViewBodyEl.setHeight(bodyHeight);
18934
18935         if(this.animate){
18936             var _this = this;
18937             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18938         }else{
18939             this.touchViewEl.addClass(['in','show']);
18940         }
18941         
18942         if(this._touchViewMask){
18943             Roo.get(document.body).addClass("x-body-masked");
18944             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18945             this._touchViewMask.setStyle('z-index', 10000);
18946             this._touchViewMask.addClass('show');
18947         }
18948         
18949         this.doTouchViewQuery();
18950         
18951     },
18952     
18953     hideTouchView : function()
18954     {
18955         this.touchViewEl.removeClass(['in','show']);
18956
18957         if(this.animate){
18958             var _this = this;
18959             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18960         }else{
18961             this.touchViewEl.setStyle('display', 'none');
18962         }
18963         
18964         if(this._touchViewMask){
18965             this._touchViewMask.removeClass('show');
18966             Roo.get(document.body).removeClass("x-body-masked");
18967         }
18968     },
18969     
18970     setTouchViewValue : function()
18971     {
18972         if(this.multiple){
18973             this.clearItem();
18974         
18975             var _this = this;
18976
18977             Roo.each(this.tickItems, function(o){
18978                 this.addItem(o);
18979             }, this);
18980         }
18981         
18982         this.hideTouchView();
18983     },
18984     
18985     doTouchViewQuery : function()
18986     {
18987         var qe = {
18988             query: '',
18989             forceAll: true,
18990             combo: this,
18991             cancel:false
18992         };
18993         
18994         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18995             return false;
18996         }
18997         
18998         if(!this.alwaysQuery || this.mode == 'local'){
18999             this.onTouchViewLoad();
19000             return;
19001         }
19002         
19003         this.store.load();
19004     },
19005     
19006     onTouchViewBeforeLoad : function(combo,opts)
19007     {
19008         return;
19009     },
19010
19011     // private
19012     onTouchViewLoad : function()
19013     {
19014         if(this.store.getCount() < 1){
19015             this.onTouchViewEmptyResults();
19016             return;
19017         }
19018         
19019         this.clearTouchView();
19020         
19021         var rawValue = this.getRawValue();
19022         
19023         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19024         
19025         this.tickItems = [];
19026         
19027         this.store.data.each(function(d, rowIndex){
19028             var row = this.touchViewListGroup.createChild(template);
19029             
19030             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19031                 row.addClass(d.data.cls);
19032             }
19033             
19034             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19035                 var cfg = {
19036                     data : d.data,
19037                     html : d.data[this.displayField]
19038                 };
19039                 
19040                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19041                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19042                 }
19043             }
19044             row.removeClass('selected');
19045             if(!this.multiple && this.valueField &&
19046                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19047             {
19048                 // radio buttons..
19049                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19050                 row.addClass('selected');
19051             }
19052             
19053             if(this.multiple && this.valueField &&
19054                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19055             {
19056                 
19057                 // checkboxes...
19058                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19059                 this.tickItems.push(d.data);
19060             }
19061             
19062             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19063             
19064         }, this);
19065         
19066         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19067         
19068         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19069
19070         if(this.modalTitle.length){
19071             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19072         }
19073
19074         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19075         
19076         if(this.mobile_restrict_height && listHeight < bodyHeight){
19077             this.touchViewBodyEl.setHeight(listHeight);
19078         }
19079         
19080         var _this = this;
19081         
19082         if(firstChecked && listHeight > bodyHeight){
19083             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19084         }
19085         
19086     },
19087     
19088     onTouchViewLoadException : function()
19089     {
19090         this.hideTouchView();
19091     },
19092     
19093     onTouchViewEmptyResults : function()
19094     {
19095         this.clearTouchView();
19096         
19097         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19098         
19099         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19100         
19101     },
19102     
19103     clearTouchView : function()
19104     {
19105         this.touchViewListGroup.dom.innerHTML = '';
19106     },
19107     
19108     onTouchViewClick : function(e, el, o)
19109     {
19110         e.preventDefault();
19111         
19112         var row = o.row;
19113         var rowIndex = o.rowIndex;
19114         
19115         var r = this.store.getAt(rowIndex);
19116         
19117         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19118             
19119             if(!this.multiple){
19120                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19121                     c.dom.removeAttribute('checked');
19122                 }, this);
19123
19124                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19125
19126                 this.setFromData(r.data);
19127
19128                 var close = this.closeTriggerEl();
19129
19130                 if(close){
19131                     close.show();
19132                 }
19133
19134                 this.hideTouchView();
19135
19136                 this.fireEvent('select', this, r, rowIndex);
19137
19138                 return;
19139             }
19140
19141             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19142                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19143                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19144                 return;
19145             }
19146
19147             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19148             this.addItem(r.data);
19149             this.tickItems.push(r.data);
19150         }
19151     },
19152     
19153     getAutoCreateNativeIOS : function()
19154     {
19155         var cfg = {
19156             cls: 'form-group' //input-group,
19157         };
19158         
19159         var combobox =  {
19160             tag: 'select',
19161             cls : 'roo-ios-select'
19162         };
19163         
19164         if (this.name) {
19165             combobox.name = this.name;
19166         }
19167         
19168         if (this.disabled) {
19169             combobox.disabled = true;
19170         }
19171         
19172         var settings = this;
19173         
19174         ['xs','sm','md','lg'].map(function(size){
19175             if (settings[size]) {
19176                 cfg.cls += ' col-' + size + '-' + settings[size];
19177             }
19178         });
19179         
19180         cfg.cn = combobox;
19181         
19182         return cfg;
19183         
19184     },
19185     
19186     initIOSView : function()
19187     {
19188         this.store.on('load', this.onIOSViewLoad, this);
19189         
19190         return;
19191     },
19192     
19193     onIOSViewLoad : function()
19194     {
19195         if(this.store.getCount() < 1){
19196             return;
19197         }
19198         
19199         this.clearIOSView();
19200         
19201         if(this.allowBlank) {
19202             
19203             var default_text = '-- SELECT --';
19204             
19205             if(this.placeholder.length){
19206                 default_text = this.placeholder;
19207             }
19208             
19209             if(this.emptyTitle.length){
19210                 default_text += ' - ' + this.emptyTitle + ' -';
19211             }
19212             
19213             var opt = this.inputEl().createChild({
19214                 tag: 'option',
19215                 value : 0,
19216                 html : default_text
19217             });
19218             
19219             var o = {};
19220             o[this.valueField] = 0;
19221             o[this.displayField] = default_text;
19222             
19223             this.ios_options.push({
19224                 data : o,
19225                 el : opt
19226             });
19227             
19228         }
19229         
19230         this.store.data.each(function(d, rowIndex){
19231             
19232             var html = '';
19233             
19234             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19235                 html = d.data[this.displayField];
19236             }
19237             
19238             var value = '';
19239             
19240             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19241                 value = d.data[this.valueField];
19242             }
19243             
19244             var option = {
19245                 tag: 'option',
19246                 value : value,
19247                 html : html
19248             };
19249             
19250             if(this.value == d.data[this.valueField]){
19251                 option['selected'] = true;
19252             }
19253             
19254             var opt = this.inputEl().createChild(option);
19255             
19256             this.ios_options.push({
19257                 data : d.data,
19258                 el : opt
19259             });
19260             
19261         }, this);
19262         
19263         this.inputEl().on('change', function(){
19264            this.fireEvent('select', this);
19265         }, this);
19266         
19267     },
19268     
19269     clearIOSView: function()
19270     {
19271         this.inputEl().dom.innerHTML = '';
19272         
19273         this.ios_options = [];
19274     },
19275     
19276     setIOSValue: function(v)
19277     {
19278         this.value = v;
19279         
19280         if(!this.ios_options){
19281             return;
19282         }
19283         
19284         Roo.each(this.ios_options, function(opts){
19285            
19286            opts.el.dom.removeAttribute('selected');
19287            
19288            if(opts.data[this.valueField] != v){
19289                return;
19290            }
19291            
19292            opts.el.dom.setAttribute('selected', true);
19293            
19294         }, this);
19295     }
19296
19297     /** 
19298     * @cfg {Boolean} grow 
19299     * @hide 
19300     */
19301     /** 
19302     * @cfg {Number} growMin 
19303     * @hide 
19304     */
19305     /** 
19306     * @cfg {Number} growMax 
19307     * @hide 
19308     */
19309     /**
19310      * @hide
19311      * @method autoSize
19312      */
19313 });
19314
19315 Roo.apply(Roo.bootstrap.ComboBox,  {
19316     
19317     header : {
19318         tag: 'div',
19319         cls: 'modal-header',
19320         cn: [
19321             {
19322                 tag: 'h4',
19323                 cls: 'modal-title'
19324             }
19325         ]
19326     },
19327     
19328     body : {
19329         tag: 'div',
19330         cls: 'modal-body',
19331         cn: [
19332             {
19333                 tag: 'ul',
19334                 cls: 'list-group'
19335             }
19336         ]
19337     },
19338     
19339     listItemRadio : {
19340         tag: 'li',
19341         cls: 'list-group-item',
19342         cn: [
19343             {
19344                 tag: 'span',
19345                 cls: 'roo-combobox-list-group-item-value'
19346             },
19347             {
19348                 tag: 'div',
19349                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19350                 cn: [
19351                     {
19352                         tag: 'input',
19353                         type: 'radio'
19354                     },
19355                     {
19356                         tag: 'label'
19357                     }
19358                 ]
19359             }
19360         ]
19361     },
19362     
19363     listItemCheckbox : {
19364         tag: 'li',
19365         cls: 'list-group-item',
19366         cn: [
19367             {
19368                 tag: 'span',
19369                 cls: 'roo-combobox-list-group-item-value'
19370             },
19371             {
19372                 tag: 'div',
19373                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19374                 cn: [
19375                     {
19376                         tag: 'input',
19377                         type: 'checkbox'
19378                     },
19379                     {
19380                         tag: 'label'
19381                     }
19382                 ]
19383             }
19384         ]
19385     },
19386     
19387     emptyResult : {
19388         tag: 'div',
19389         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19390     },
19391     
19392     footer : {
19393         tag: 'div',
19394         cls: 'modal-footer',
19395         cn: [
19396             {
19397                 tag: 'div',
19398                 cls: 'row',
19399                 cn: [
19400                     {
19401                         tag: 'div',
19402                         cls: 'col-xs-6 text-left',
19403                         cn: {
19404                             tag: 'button',
19405                             cls: 'btn btn-danger roo-touch-view-cancel',
19406                             html: 'Cancel'
19407                         }
19408                     },
19409                     {
19410                         tag: 'div',
19411                         cls: 'col-xs-6 text-right',
19412                         cn: {
19413                             tag: 'button',
19414                             cls: 'btn btn-success roo-touch-view-ok',
19415                             html: 'OK'
19416                         }
19417                     }
19418                 ]
19419             }
19420         ]
19421         
19422     }
19423 });
19424
19425 Roo.apply(Roo.bootstrap.ComboBox,  {
19426     
19427     touchViewTemplate : {
19428         tag: 'div',
19429         cls: 'modal fade roo-combobox-touch-view',
19430         cn: [
19431             {
19432                 tag: 'div',
19433                 cls: 'modal-dialog',
19434                 style : 'position:fixed', // we have to fix position....
19435                 cn: [
19436                     {
19437                         tag: 'div',
19438                         cls: 'modal-content',
19439                         cn: [
19440                             Roo.bootstrap.ComboBox.header,
19441                             Roo.bootstrap.ComboBox.body,
19442                             Roo.bootstrap.ComboBox.footer
19443                         ]
19444                     }
19445                 ]
19446             }
19447         ]
19448     }
19449 });/*
19450  * Based on:
19451  * Ext JS Library 1.1.1
19452  * Copyright(c) 2006-2007, Ext JS, LLC.
19453  *
19454  * Originally Released Under LGPL - original licence link has changed is not relivant.
19455  *
19456  * Fork - LGPL
19457  * <script type="text/javascript">
19458  */
19459
19460 /**
19461  * @class Roo.View
19462  * @extends Roo.util.Observable
19463  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19464  * This class also supports single and multi selection modes. <br>
19465  * Create a data model bound view:
19466  <pre><code>
19467  var store = new Roo.data.Store(...);
19468
19469  var view = new Roo.View({
19470     el : "my-element",
19471     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19472  
19473     singleSelect: true,
19474     selectedClass: "ydataview-selected",
19475     store: store
19476  });
19477
19478  // listen for node click?
19479  view.on("click", function(vw, index, node, e){
19480  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19481  });
19482
19483  // load XML data
19484  dataModel.load("foobar.xml");
19485  </code></pre>
19486  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19487  * <br><br>
19488  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19489  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19490  * 
19491  * Note: old style constructor is still suported (container, template, config)
19492  * 
19493  * @constructor
19494  * Create a new View
19495  * @param {Object} config The config object
19496  * 
19497  */
19498 Roo.View = function(config, depreciated_tpl, depreciated_config){
19499     
19500     this.parent = false;
19501     
19502     if (typeof(depreciated_tpl) == 'undefined') {
19503         // new way.. - universal constructor.
19504         Roo.apply(this, config);
19505         this.el  = Roo.get(this.el);
19506     } else {
19507         // old format..
19508         this.el  = Roo.get(config);
19509         this.tpl = depreciated_tpl;
19510         Roo.apply(this, depreciated_config);
19511     }
19512     this.wrapEl  = this.el.wrap().wrap();
19513     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19514     
19515     
19516     if(typeof(this.tpl) == "string"){
19517         this.tpl = new Roo.Template(this.tpl);
19518     } else {
19519         // support xtype ctors..
19520         this.tpl = new Roo.factory(this.tpl, Roo);
19521     }
19522     
19523     
19524     this.tpl.compile();
19525     
19526     /** @private */
19527     this.addEvents({
19528         /**
19529          * @event beforeclick
19530          * Fires before a click is processed. Returns false to cancel the default action.
19531          * @param {Roo.View} this
19532          * @param {Number} index The index of the target node
19533          * @param {HTMLElement} node The target node
19534          * @param {Roo.EventObject} e The raw event object
19535          */
19536             "beforeclick" : true,
19537         /**
19538          * @event click
19539          * Fires when a template node is clicked.
19540          * @param {Roo.View} this
19541          * @param {Number} index The index of the target node
19542          * @param {HTMLElement} node The target node
19543          * @param {Roo.EventObject} e The raw event object
19544          */
19545             "click" : true,
19546         /**
19547          * @event dblclick
19548          * Fires when a template node is double clicked.
19549          * @param {Roo.View} this
19550          * @param {Number} index The index of the target node
19551          * @param {HTMLElement} node The target node
19552          * @param {Roo.EventObject} e The raw event object
19553          */
19554             "dblclick" : true,
19555         /**
19556          * @event contextmenu
19557          * Fires when a template node is right clicked.
19558          * @param {Roo.View} this
19559          * @param {Number} index The index of the target node
19560          * @param {HTMLElement} node The target node
19561          * @param {Roo.EventObject} e The raw event object
19562          */
19563             "contextmenu" : true,
19564         /**
19565          * @event selectionchange
19566          * Fires when the selected nodes change.
19567          * @param {Roo.View} this
19568          * @param {Array} selections Array of the selected nodes
19569          */
19570             "selectionchange" : true,
19571     
19572         /**
19573          * @event beforeselect
19574          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19575          * @param {Roo.View} this
19576          * @param {HTMLElement} node The node to be selected
19577          * @param {Array} selections Array of currently selected nodes
19578          */
19579             "beforeselect" : true,
19580         /**
19581          * @event preparedata
19582          * Fires on every row to render, to allow you to change the data.
19583          * @param {Roo.View} this
19584          * @param {Object} data to be rendered (change this)
19585          */
19586           "preparedata" : true
19587           
19588           
19589         });
19590
19591
19592
19593     this.el.on({
19594         "click": this.onClick,
19595         "dblclick": this.onDblClick,
19596         "contextmenu": this.onContextMenu,
19597         scope:this
19598     });
19599
19600     this.selections = [];
19601     this.nodes = [];
19602     this.cmp = new Roo.CompositeElementLite([]);
19603     if(this.store){
19604         this.store = Roo.factory(this.store, Roo.data);
19605         this.setStore(this.store, true);
19606     }
19607     
19608     if ( this.footer && this.footer.xtype) {
19609            
19610          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19611         
19612         this.footer.dataSource = this.store;
19613         this.footer.container = fctr;
19614         this.footer = Roo.factory(this.footer, Roo);
19615         fctr.insertFirst(this.el);
19616         
19617         // this is a bit insane - as the paging toolbar seems to detach the el..
19618 //        dom.parentNode.parentNode.parentNode
19619          // they get detached?
19620     }
19621     
19622     
19623     Roo.View.superclass.constructor.call(this);
19624     
19625     
19626 };
19627
19628 Roo.extend(Roo.View, Roo.util.Observable, {
19629     
19630      /**
19631      * @cfg {Roo.data.Store} store Data store to load data from.
19632      */
19633     store : false,
19634     
19635     /**
19636      * @cfg {String|Roo.Element} el The container element.
19637      */
19638     el : '',
19639     
19640     /**
19641      * @cfg {String|Roo.Template} tpl The template used by this View 
19642      */
19643     tpl : false,
19644     /**
19645      * @cfg {String} dataName the named area of the template to use as the data area
19646      *                          Works with domtemplates roo-name="name"
19647      */
19648     dataName: false,
19649     /**
19650      * @cfg {String} selectedClass The css class to add to selected nodes
19651      */
19652     selectedClass : "x-view-selected",
19653      /**
19654      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19655      */
19656     emptyText : "",
19657     
19658     /**
19659      * @cfg {String} text to display on mask (default Loading)
19660      */
19661     mask : false,
19662     /**
19663      * @cfg {Boolean} multiSelect Allow multiple selection
19664      */
19665     multiSelect : false,
19666     /**
19667      * @cfg {Boolean} singleSelect Allow single selection
19668      */
19669     singleSelect:  false,
19670     
19671     /**
19672      * @cfg {Boolean} toggleSelect - selecting 
19673      */
19674     toggleSelect : false,
19675     
19676     /**
19677      * @cfg {Boolean} tickable - selecting 
19678      */
19679     tickable : false,
19680     
19681     /**
19682      * Returns the element this view is bound to.
19683      * @return {Roo.Element}
19684      */
19685     getEl : function(){
19686         return this.wrapEl;
19687     },
19688     
19689     
19690
19691     /**
19692      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19693      */
19694     refresh : function(){
19695         //Roo.log('refresh');
19696         var t = this.tpl;
19697         
19698         // if we are using something like 'domtemplate', then
19699         // the what gets used is:
19700         // t.applySubtemplate(NAME, data, wrapping data..)
19701         // the outer template then get' applied with
19702         //     the store 'extra data'
19703         // and the body get's added to the
19704         //      roo-name="data" node?
19705         //      <span class='roo-tpl-{name}'></span> ?????
19706         
19707         
19708         
19709         this.clearSelections();
19710         this.el.update("");
19711         var html = [];
19712         var records = this.store.getRange();
19713         if(records.length < 1) {
19714             
19715             // is this valid??  = should it render a template??
19716             
19717             this.el.update(this.emptyText);
19718             return;
19719         }
19720         var el = this.el;
19721         if (this.dataName) {
19722             this.el.update(t.apply(this.store.meta)); //????
19723             el = this.el.child('.roo-tpl-' + this.dataName);
19724         }
19725         
19726         for(var i = 0, len = records.length; i < len; i++){
19727             var data = this.prepareData(records[i].data, i, records[i]);
19728             this.fireEvent("preparedata", this, data, i, records[i]);
19729             
19730             var d = Roo.apply({}, data);
19731             
19732             if(this.tickable){
19733                 Roo.apply(d, {'roo-id' : Roo.id()});
19734                 
19735                 var _this = this;
19736             
19737                 Roo.each(this.parent.item, function(item){
19738                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19739                         return;
19740                     }
19741                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19742                 });
19743             }
19744             
19745             html[html.length] = Roo.util.Format.trim(
19746                 this.dataName ?
19747                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19748                     t.apply(d)
19749             );
19750         }
19751         
19752         
19753         
19754         el.update(html.join(""));
19755         this.nodes = el.dom.childNodes;
19756         this.updateIndexes(0);
19757     },
19758     
19759
19760     /**
19761      * Function to override to reformat the data that is sent to
19762      * the template for each node.
19763      * DEPRICATED - use the preparedata event handler.
19764      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19765      * a JSON object for an UpdateManager bound view).
19766      */
19767     prepareData : function(data, index, record)
19768     {
19769         this.fireEvent("preparedata", this, data, index, record);
19770         return data;
19771     },
19772
19773     onUpdate : function(ds, record){
19774         // Roo.log('on update');   
19775         this.clearSelections();
19776         var index = this.store.indexOf(record);
19777         var n = this.nodes[index];
19778         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19779         n.parentNode.removeChild(n);
19780         this.updateIndexes(index, index);
19781     },
19782
19783     
19784     
19785 // --------- FIXME     
19786     onAdd : function(ds, records, index)
19787     {
19788         //Roo.log(['on Add', ds, records, index] );        
19789         this.clearSelections();
19790         if(this.nodes.length == 0){
19791             this.refresh();
19792             return;
19793         }
19794         var n = this.nodes[index];
19795         for(var i = 0, len = records.length; i < len; i++){
19796             var d = this.prepareData(records[i].data, i, records[i]);
19797             if(n){
19798                 this.tpl.insertBefore(n, d);
19799             }else{
19800                 
19801                 this.tpl.append(this.el, d);
19802             }
19803         }
19804         this.updateIndexes(index);
19805     },
19806
19807     onRemove : function(ds, record, index){
19808        // Roo.log('onRemove');
19809         this.clearSelections();
19810         var el = this.dataName  ?
19811             this.el.child('.roo-tpl-' + this.dataName) :
19812             this.el; 
19813         
19814         el.dom.removeChild(this.nodes[index]);
19815         this.updateIndexes(index);
19816     },
19817
19818     /**
19819      * Refresh an individual node.
19820      * @param {Number} index
19821      */
19822     refreshNode : function(index){
19823         this.onUpdate(this.store, this.store.getAt(index));
19824     },
19825
19826     updateIndexes : function(startIndex, endIndex){
19827         var ns = this.nodes;
19828         startIndex = startIndex || 0;
19829         endIndex = endIndex || ns.length - 1;
19830         for(var i = startIndex; i <= endIndex; i++){
19831             ns[i].nodeIndex = i;
19832         }
19833     },
19834
19835     /**
19836      * Changes the data store this view uses and refresh the view.
19837      * @param {Store} store
19838      */
19839     setStore : function(store, initial){
19840         if(!initial && this.store){
19841             this.store.un("datachanged", this.refresh);
19842             this.store.un("add", this.onAdd);
19843             this.store.un("remove", this.onRemove);
19844             this.store.un("update", this.onUpdate);
19845             this.store.un("clear", this.refresh);
19846             this.store.un("beforeload", this.onBeforeLoad);
19847             this.store.un("load", this.onLoad);
19848             this.store.un("loadexception", this.onLoad);
19849         }
19850         if(store){
19851           
19852             store.on("datachanged", this.refresh, this);
19853             store.on("add", this.onAdd, this);
19854             store.on("remove", this.onRemove, this);
19855             store.on("update", this.onUpdate, this);
19856             store.on("clear", this.refresh, this);
19857             store.on("beforeload", this.onBeforeLoad, this);
19858             store.on("load", this.onLoad, this);
19859             store.on("loadexception", this.onLoad, this);
19860         }
19861         
19862         if(store){
19863             this.refresh();
19864         }
19865     },
19866     /**
19867      * onbeforeLoad - masks the loading area.
19868      *
19869      */
19870     onBeforeLoad : function(store,opts)
19871     {
19872          //Roo.log('onBeforeLoad');   
19873         if (!opts.add) {
19874             this.el.update("");
19875         }
19876         this.el.mask(this.mask ? this.mask : "Loading" ); 
19877     },
19878     onLoad : function ()
19879     {
19880         this.el.unmask();
19881     },
19882     
19883
19884     /**
19885      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19886      * @param {HTMLElement} node
19887      * @return {HTMLElement} The template node
19888      */
19889     findItemFromChild : function(node){
19890         var el = this.dataName  ?
19891             this.el.child('.roo-tpl-' + this.dataName,true) :
19892             this.el.dom; 
19893         
19894         if(!node || node.parentNode == el){
19895                     return node;
19896             }
19897             var p = node.parentNode;
19898             while(p && p != el){
19899             if(p.parentNode == el){
19900                 return p;
19901             }
19902             p = p.parentNode;
19903         }
19904             return null;
19905     },
19906
19907     /** @ignore */
19908     onClick : function(e){
19909         var item = this.findItemFromChild(e.getTarget());
19910         if(item){
19911             var index = this.indexOf(item);
19912             if(this.onItemClick(item, index, e) !== false){
19913                 this.fireEvent("click", this, index, item, e);
19914             }
19915         }else{
19916             this.clearSelections();
19917         }
19918     },
19919
19920     /** @ignore */
19921     onContextMenu : function(e){
19922         var item = this.findItemFromChild(e.getTarget());
19923         if(item){
19924             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19925         }
19926     },
19927
19928     /** @ignore */
19929     onDblClick : function(e){
19930         var item = this.findItemFromChild(e.getTarget());
19931         if(item){
19932             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19933         }
19934     },
19935
19936     onItemClick : function(item, index, e)
19937     {
19938         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19939             return false;
19940         }
19941         if (this.toggleSelect) {
19942             var m = this.isSelected(item) ? 'unselect' : 'select';
19943             //Roo.log(m);
19944             var _t = this;
19945             _t[m](item, true, false);
19946             return true;
19947         }
19948         if(this.multiSelect || this.singleSelect){
19949             if(this.multiSelect && e.shiftKey && this.lastSelection){
19950                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19951             }else{
19952                 this.select(item, this.multiSelect && e.ctrlKey);
19953                 this.lastSelection = item;
19954             }
19955             
19956             if(!this.tickable){
19957                 e.preventDefault();
19958             }
19959             
19960         }
19961         return true;
19962     },
19963
19964     /**
19965      * Get the number of selected nodes.
19966      * @return {Number}
19967      */
19968     getSelectionCount : function(){
19969         return this.selections.length;
19970     },
19971
19972     /**
19973      * Get the currently selected nodes.
19974      * @return {Array} An array of HTMLElements
19975      */
19976     getSelectedNodes : function(){
19977         return this.selections;
19978     },
19979
19980     /**
19981      * Get the indexes of the selected nodes.
19982      * @return {Array}
19983      */
19984     getSelectedIndexes : function(){
19985         var indexes = [], s = this.selections;
19986         for(var i = 0, len = s.length; i < len; i++){
19987             indexes.push(s[i].nodeIndex);
19988         }
19989         return indexes;
19990     },
19991
19992     /**
19993      * Clear all selections
19994      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19995      */
19996     clearSelections : function(suppressEvent){
19997         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19998             this.cmp.elements = this.selections;
19999             this.cmp.removeClass(this.selectedClass);
20000             this.selections = [];
20001             if(!suppressEvent){
20002                 this.fireEvent("selectionchange", this, this.selections);
20003             }
20004         }
20005     },
20006
20007     /**
20008      * Returns true if the passed node is selected
20009      * @param {HTMLElement/Number} node The node or node index
20010      * @return {Boolean}
20011      */
20012     isSelected : function(node){
20013         var s = this.selections;
20014         if(s.length < 1){
20015             return false;
20016         }
20017         node = this.getNode(node);
20018         return s.indexOf(node) !== -1;
20019     },
20020
20021     /**
20022      * Selects nodes.
20023      * @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
20024      * @param {Boolean} keepExisting (optional) true to keep existing selections
20025      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20026      */
20027     select : function(nodeInfo, keepExisting, suppressEvent){
20028         if(nodeInfo instanceof Array){
20029             if(!keepExisting){
20030                 this.clearSelections(true);
20031             }
20032             for(var i = 0, len = nodeInfo.length; i < len; i++){
20033                 this.select(nodeInfo[i], true, true);
20034             }
20035             return;
20036         } 
20037         var node = this.getNode(nodeInfo);
20038         if(!node || this.isSelected(node)){
20039             return; // already selected.
20040         }
20041         if(!keepExisting){
20042             this.clearSelections(true);
20043         }
20044         
20045         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20046             Roo.fly(node).addClass(this.selectedClass);
20047             this.selections.push(node);
20048             if(!suppressEvent){
20049                 this.fireEvent("selectionchange", this, this.selections);
20050             }
20051         }
20052         
20053         
20054     },
20055       /**
20056      * Unselects nodes.
20057      * @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
20058      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20059      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20060      */
20061     unselect : function(nodeInfo, keepExisting, suppressEvent)
20062     {
20063         if(nodeInfo instanceof Array){
20064             Roo.each(this.selections, function(s) {
20065                 this.unselect(s, nodeInfo);
20066             }, this);
20067             return;
20068         }
20069         var node = this.getNode(nodeInfo);
20070         if(!node || !this.isSelected(node)){
20071             //Roo.log("not selected");
20072             return; // not selected.
20073         }
20074         // fireevent???
20075         var ns = [];
20076         Roo.each(this.selections, function(s) {
20077             if (s == node ) {
20078                 Roo.fly(node).removeClass(this.selectedClass);
20079
20080                 return;
20081             }
20082             ns.push(s);
20083         },this);
20084         
20085         this.selections= ns;
20086         this.fireEvent("selectionchange", this, this.selections);
20087     },
20088
20089     /**
20090      * Gets a template node.
20091      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20092      * @return {HTMLElement} The node or null if it wasn't found
20093      */
20094     getNode : function(nodeInfo){
20095         if(typeof nodeInfo == "string"){
20096             return document.getElementById(nodeInfo);
20097         }else if(typeof nodeInfo == "number"){
20098             return this.nodes[nodeInfo];
20099         }
20100         return nodeInfo;
20101     },
20102
20103     /**
20104      * Gets a range template nodes.
20105      * @param {Number} startIndex
20106      * @param {Number} endIndex
20107      * @return {Array} An array of nodes
20108      */
20109     getNodes : function(start, end){
20110         var ns = this.nodes;
20111         start = start || 0;
20112         end = typeof end == "undefined" ? ns.length - 1 : end;
20113         var nodes = [];
20114         if(start <= end){
20115             for(var i = start; i <= end; i++){
20116                 nodes.push(ns[i]);
20117             }
20118         } else{
20119             for(var i = start; i >= end; i--){
20120                 nodes.push(ns[i]);
20121             }
20122         }
20123         return nodes;
20124     },
20125
20126     /**
20127      * Finds the index of the passed node
20128      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20129      * @return {Number} The index of the node or -1
20130      */
20131     indexOf : function(node){
20132         node = this.getNode(node);
20133         if(typeof node.nodeIndex == "number"){
20134             return node.nodeIndex;
20135         }
20136         var ns = this.nodes;
20137         for(var i = 0, len = ns.length; i < len; i++){
20138             if(ns[i] == node){
20139                 return i;
20140             }
20141         }
20142         return -1;
20143     }
20144 });
20145 /*
20146  * - LGPL
20147  *
20148  * based on jquery fullcalendar
20149  * 
20150  */
20151
20152 Roo.bootstrap = Roo.bootstrap || {};
20153 /**
20154  * @class Roo.bootstrap.Calendar
20155  * @extends Roo.bootstrap.Component
20156  * Bootstrap Calendar class
20157  * @cfg {Boolean} loadMask (true|false) default false
20158  * @cfg {Object} header generate the user specific header of the calendar, default false
20159
20160  * @constructor
20161  * Create a new Container
20162  * @param {Object} config The config object
20163  */
20164
20165
20166
20167 Roo.bootstrap.Calendar = function(config){
20168     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20169      this.addEvents({
20170         /**
20171              * @event select
20172              * Fires when a date is selected
20173              * @param {DatePicker} this
20174              * @param {Date} date The selected date
20175              */
20176         'select': true,
20177         /**
20178              * @event monthchange
20179              * Fires when the displayed month changes 
20180              * @param {DatePicker} this
20181              * @param {Date} date The selected month
20182              */
20183         'monthchange': true,
20184         /**
20185              * @event evententer
20186              * Fires when mouse over an event
20187              * @param {Calendar} this
20188              * @param {event} Event
20189              */
20190         'evententer': true,
20191         /**
20192              * @event eventleave
20193              * Fires when the mouse leaves an
20194              * @param {Calendar} this
20195              * @param {event}
20196              */
20197         'eventleave': true,
20198         /**
20199              * @event eventclick
20200              * Fires when the mouse click an
20201              * @param {Calendar} this
20202              * @param {event}
20203              */
20204         'eventclick': true
20205         
20206     });
20207
20208 };
20209
20210 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20211     
20212           /**
20213      * @cfg {Roo.data.Store} store
20214      * The data source for the calendar
20215      */
20216         store : false,
20217      /**
20218      * @cfg {Number} startDay
20219      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20220      */
20221     startDay : 0,
20222     
20223     loadMask : false,
20224     
20225     header : false,
20226       
20227     getAutoCreate : function(){
20228         
20229         
20230         var fc_button = function(name, corner, style, content ) {
20231             return Roo.apply({},{
20232                 tag : 'span',
20233                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20234                          (corner.length ?
20235                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20236                             ''
20237                         ),
20238                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20239                 unselectable: 'on'
20240             });
20241         };
20242         
20243         var header = {};
20244         
20245         if(!this.header){
20246             header = {
20247                 tag : 'table',
20248                 cls : 'fc-header',
20249                 style : 'width:100%',
20250                 cn : [
20251                     {
20252                         tag: 'tr',
20253                         cn : [
20254                             {
20255                                 tag : 'td',
20256                                 cls : 'fc-header-left',
20257                                 cn : [
20258                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20259                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20260                                     { tag: 'span', cls: 'fc-header-space' },
20261                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20262
20263
20264                                 ]
20265                             },
20266
20267                             {
20268                                 tag : 'td',
20269                                 cls : 'fc-header-center',
20270                                 cn : [
20271                                     {
20272                                         tag: 'span',
20273                                         cls: 'fc-header-title',
20274                                         cn : {
20275                                             tag: 'H2',
20276                                             html : 'month / year'
20277                                         }
20278                                     }
20279
20280                                 ]
20281                             },
20282                             {
20283                                 tag : 'td',
20284                                 cls : 'fc-header-right',
20285                                 cn : [
20286                               /*      fc_button('month', 'left', '', 'month' ),
20287                                     fc_button('week', '', '', 'week' ),
20288                                     fc_button('day', 'right', '', 'day' )
20289                                 */    
20290
20291                                 ]
20292                             }
20293
20294                         ]
20295                     }
20296                 ]
20297             };
20298         }
20299         
20300         header = this.header;
20301         
20302        
20303         var cal_heads = function() {
20304             var ret = [];
20305             // fixme - handle this.
20306             
20307             for (var i =0; i < Date.dayNames.length; i++) {
20308                 var d = Date.dayNames[i];
20309                 ret.push({
20310                     tag: 'th',
20311                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20312                     html : d.substring(0,3)
20313                 });
20314                 
20315             }
20316             ret[0].cls += ' fc-first';
20317             ret[6].cls += ' fc-last';
20318             return ret;
20319         };
20320         var cal_cell = function(n) {
20321             return  {
20322                 tag: 'td',
20323                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20324                 cn : [
20325                     {
20326                         cn : [
20327                             {
20328                                 cls: 'fc-day-number',
20329                                 html: 'D'
20330                             },
20331                             {
20332                                 cls: 'fc-day-content',
20333                              
20334                                 cn : [
20335                                      {
20336                                         style: 'position: relative;' // height: 17px;
20337                                     }
20338                                 ]
20339                             }
20340                             
20341                             
20342                         ]
20343                     }
20344                 ]
20345                 
20346             }
20347         };
20348         var cal_rows = function() {
20349             
20350             var ret = [];
20351             for (var r = 0; r < 6; r++) {
20352                 var row= {
20353                     tag : 'tr',
20354                     cls : 'fc-week',
20355                     cn : []
20356                 };
20357                 
20358                 for (var i =0; i < Date.dayNames.length; i++) {
20359                     var d = Date.dayNames[i];
20360                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20361
20362                 }
20363                 row.cn[0].cls+=' fc-first';
20364                 row.cn[0].cn[0].style = 'min-height:90px';
20365                 row.cn[6].cls+=' fc-last';
20366                 ret.push(row);
20367                 
20368             }
20369             ret[0].cls += ' fc-first';
20370             ret[4].cls += ' fc-prev-last';
20371             ret[5].cls += ' fc-last';
20372             return ret;
20373             
20374         };
20375         
20376         var cal_table = {
20377             tag: 'table',
20378             cls: 'fc-border-separate',
20379             style : 'width:100%',
20380             cellspacing  : 0,
20381             cn : [
20382                 { 
20383                     tag: 'thead',
20384                     cn : [
20385                         { 
20386                             tag: 'tr',
20387                             cls : 'fc-first fc-last',
20388                             cn : cal_heads()
20389                         }
20390                     ]
20391                 },
20392                 { 
20393                     tag: 'tbody',
20394                     cn : cal_rows()
20395                 }
20396                   
20397             ]
20398         };
20399          
20400          var cfg = {
20401             cls : 'fc fc-ltr',
20402             cn : [
20403                 header,
20404                 {
20405                     cls : 'fc-content',
20406                     style : "position: relative;",
20407                     cn : [
20408                         {
20409                             cls : 'fc-view fc-view-month fc-grid',
20410                             style : 'position: relative',
20411                             unselectable : 'on',
20412                             cn : [
20413                                 {
20414                                     cls : 'fc-event-container',
20415                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20416                                 },
20417                                 cal_table
20418                             ]
20419                         }
20420                     ]
20421     
20422                 }
20423            ] 
20424             
20425         };
20426         
20427          
20428         
20429         return cfg;
20430     },
20431     
20432     
20433     initEvents : function()
20434     {
20435         if(!this.store){
20436             throw "can not find store for calendar";
20437         }
20438         
20439         var mark = {
20440             tag: "div",
20441             cls:"x-dlg-mask",
20442             style: "text-align:center",
20443             cn: [
20444                 {
20445                     tag: "div",
20446                     style: "background-color:white;width:50%;margin:250 auto",
20447                     cn: [
20448                         {
20449                             tag: "img",
20450                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20451                         },
20452                         {
20453                             tag: "span",
20454                             html: "Loading"
20455                         }
20456                         
20457                     ]
20458                 }
20459             ]
20460         };
20461         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20462         
20463         var size = this.el.select('.fc-content', true).first().getSize();
20464         this.maskEl.setSize(size.width, size.height);
20465         this.maskEl.enableDisplayMode("block");
20466         if(!this.loadMask){
20467             this.maskEl.hide();
20468         }
20469         
20470         this.store = Roo.factory(this.store, Roo.data);
20471         this.store.on('load', this.onLoad, this);
20472         this.store.on('beforeload', this.onBeforeLoad, this);
20473         
20474         this.resize();
20475         
20476         this.cells = this.el.select('.fc-day',true);
20477         //Roo.log(this.cells);
20478         this.textNodes = this.el.query('.fc-day-number');
20479         this.cells.addClassOnOver('fc-state-hover');
20480         
20481         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20482         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20483         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20484         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20485         
20486         this.on('monthchange', this.onMonthChange, this);
20487         
20488         this.update(new Date().clearTime());
20489     },
20490     
20491     resize : function() {
20492         var sz  = this.el.getSize();
20493         
20494         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20495         this.el.select('.fc-day-content div',true).setHeight(34);
20496     },
20497     
20498     
20499     // private
20500     showPrevMonth : function(e){
20501         this.update(this.activeDate.add("mo", -1));
20502     },
20503     showToday : function(e){
20504         this.update(new Date().clearTime());
20505     },
20506     // private
20507     showNextMonth : function(e){
20508         this.update(this.activeDate.add("mo", 1));
20509     },
20510
20511     // private
20512     showPrevYear : function(){
20513         this.update(this.activeDate.add("y", -1));
20514     },
20515
20516     // private
20517     showNextYear : function(){
20518         this.update(this.activeDate.add("y", 1));
20519     },
20520
20521     
20522    // private
20523     update : function(date)
20524     {
20525         var vd = this.activeDate;
20526         this.activeDate = date;
20527 //        if(vd && this.el){
20528 //            var t = date.getTime();
20529 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20530 //                Roo.log('using add remove');
20531 //                
20532 //                this.fireEvent('monthchange', this, date);
20533 //                
20534 //                this.cells.removeClass("fc-state-highlight");
20535 //                this.cells.each(function(c){
20536 //                   if(c.dateValue == t){
20537 //                       c.addClass("fc-state-highlight");
20538 //                       setTimeout(function(){
20539 //                            try{c.dom.firstChild.focus();}catch(e){}
20540 //                       }, 50);
20541 //                       return false;
20542 //                   }
20543 //                   return true;
20544 //                });
20545 //                return;
20546 //            }
20547 //        }
20548         
20549         var days = date.getDaysInMonth();
20550         
20551         var firstOfMonth = date.getFirstDateOfMonth();
20552         var startingPos = firstOfMonth.getDay()-this.startDay;
20553         
20554         if(startingPos < this.startDay){
20555             startingPos += 7;
20556         }
20557         
20558         var pm = date.add(Date.MONTH, -1);
20559         var prevStart = pm.getDaysInMonth()-startingPos;
20560 //        
20561         this.cells = this.el.select('.fc-day',true);
20562         this.textNodes = this.el.query('.fc-day-number');
20563         this.cells.addClassOnOver('fc-state-hover');
20564         
20565         var cells = this.cells.elements;
20566         var textEls = this.textNodes;
20567         
20568         Roo.each(cells, function(cell){
20569             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20570         });
20571         
20572         days += startingPos;
20573
20574         // convert everything to numbers so it's fast
20575         var day = 86400000;
20576         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20577         //Roo.log(d);
20578         //Roo.log(pm);
20579         //Roo.log(prevStart);
20580         
20581         var today = new Date().clearTime().getTime();
20582         var sel = date.clearTime().getTime();
20583         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20584         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20585         var ddMatch = this.disabledDatesRE;
20586         var ddText = this.disabledDatesText;
20587         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20588         var ddaysText = this.disabledDaysText;
20589         var format = this.format;
20590         
20591         var setCellClass = function(cal, cell){
20592             cell.row = 0;
20593             cell.events = [];
20594             cell.more = [];
20595             //Roo.log('set Cell Class');
20596             cell.title = "";
20597             var t = d.getTime();
20598             
20599             //Roo.log(d);
20600             
20601             cell.dateValue = t;
20602             if(t == today){
20603                 cell.className += " fc-today";
20604                 cell.className += " fc-state-highlight";
20605                 cell.title = cal.todayText;
20606             }
20607             if(t == sel){
20608                 // disable highlight in other month..
20609                 //cell.className += " fc-state-highlight";
20610                 
20611             }
20612             // disabling
20613             if(t < min) {
20614                 cell.className = " fc-state-disabled";
20615                 cell.title = cal.minText;
20616                 return;
20617             }
20618             if(t > max) {
20619                 cell.className = " fc-state-disabled";
20620                 cell.title = cal.maxText;
20621                 return;
20622             }
20623             if(ddays){
20624                 if(ddays.indexOf(d.getDay()) != -1){
20625                     cell.title = ddaysText;
20626                     cell.className = " fc-state-disabled";
20627                 }
20628             }
20629             if(ddMatch && format){
20630                 var fvalue = d.dateFormat(format);
20631                 if(ddMatch.test(fvalue)){
20632                     cell.title = ddText.replace("%0", fvalue);
20633                     cell.className = " fc-state-disabled";
20634                 }
20635             }
20636             
20637             if (!cell.initialClassName) {
20638                 cell.initialClassName = cell.dom.className;
20639             }
20640             
20641             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20642         };
20643
20644         var i = 0;
20645         
20646         for(; i < startingPos; i++) {
20647             textEls[i].innerHTML = (++prevStart);
20648             d.setDate(d.getDate()+1);
20649             
20650             cells[i].className = "fc-past fc-other-month";
20651             setCellClass(this, cells[i]);
20652         }
20653         
20654         var intDay = 0;
20655         
20656         for(; i < days; i++){
20657             intDay = i - startingPos + 1;
20658             textEls[i].innerHTML = (intDay);
20659             d.setDate(d.getDate()+1);
20660             
20661             cells[i].className = ''; // "x-date-active";
20662             setCellClass(this, cells[i]);
20663         }
20664         var extraDays = 0;
20665         
20666         for(; i < 42; i++) {
20667             textEls[i].innerHTML = (++extraDays);
20668             d.setDate(d.getDate()+1);
20669             
20670             cells[i].className = "fc-future fc-other-month";
20671             setCellClass(this, cells[i]);
20672         }
20673         
20674         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20675         
20676         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20677         
20678         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20679         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20680         
20681         if(totalRows != 6){
20682             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20683             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20684         }
20685         
20686         this.fireEvent('monthchange', this, date);
20687         
20688         
20689         /*
20690         if(!this.internalRender){
20691             var main = this.el.dom.firstChild;
20692             var w = main.offsetWidth;
20693             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20694             Roo.fly(main).setWidth(w);
20695             this.internalRender = true;
20696             // opera does not respect the auto grow header center column
20697             // then, after it gets a width opera refuses to recalculate
20698             // without a second pass
20699             if(Roo.isOpera && !this.secondPass){
20700                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20701                 this.secondPass = true;
20702                 this.update.defer(10, this, [date]);
20703             }
20704         }
20705         */
20706         
20707     },
20708     
20709     findCell : function(dt) {
20710         dt = dt.clearTime().getTime();
20711         var ret = false;
20712         this.cells.each(function(c){
20713             //Roo.log("check " +c.dateValue + '?=' + dt);
20714             if(c.dateValue == dt){
20715                 ret = c;
20716                 return false;
20717             }
20718             return true;
20719         });
20720         
20721         return ret;
20722     },
20723     
20724     findCells : function(ev) {
20725         var s = ev.start.clone().clearTime().getTime();
20726        // Roo.log(s);
20727         var e= ev.end.clone().clearTime().getTime();
20728        // Roo.log(e);
20729         var ret = [];
20730         this.cells.each(function(c){
20731              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20732             
20733             if(c.dateValue > e){
20734                 return ;
20735             }
20736             if(c.dateValue < s){
20737                 return ;
20738             }
20739             ret.push(c);
20740         });
20741         
20742         return ret;    
20743     },
20744     
20745 //    findBestRow: function(cells)
20746 //    {
20747 //        var ret = 0;
20748 //        
20749 //        for (var i =0 ; i < cells.length;i++) {
20750 //            ret  = Math.max(cells[i].rows || 0,ret);
20751 //        }
20752 //        return ret;
20753 //        
20754 //    },
20755     
20756     
20757     addItem : function(ev)
20758     {
20759         // look for vertical location slot in
20760         var cells = this.findCells(ev);
20761         
20762 //        ev.row = this.findBestRow(cells);
20763         
20764         // work out the location.
20765         
20766         var crow = false;
20767         var rows = [];
20768         for(var i =0; i < cells.length; i++) {
20769             
20770             cells[i].row = cells[0].row;
20771             
20772             if(i == 0){
20773                 cells[i].row = cells[i].row + 1;
20774             }
20775             
20776             if (!crow) {
20777                 crow = {
20778                     start : cells[i],
20779                     end :  cells[i]
20780                 };
20781                 continue;
20782             }
20783             if (crow.start.getY() == cells[i].getY()) {
20784                 // on same row.
20785                 crow.end = cells[i];
20786                 continue;
20787             }
20788             // different row.
20789             rows.push(crow);
20790             crow = {
20791                 start: cells[i],
20792                 end : cells[i]
20793             };
20794             
20795         }
20796         
20797         rows.push(crow);
20798         ev.els = [];
20799         ev.rows = rows;
20800         ev.cells = cells;
20801         
20802         cells[0].events.push(ev);
20803         
20804         this.calevents.push(ev);
20805     },
20806     
20807     clearEvents: function() {
20808         
20809         if(!this.calevents){
20810             return;
20811         }
20812         
20813         Roo.each(this.cells.elements, function(c){
20814             c.row = 0;
20815             c.events = [];
20816             c.more = [];
20817         });
20818         
20819         Roo.each(this.calevents, function(e) {
20820             Roo.each(e.els, function(el) {
20821                 el.un('mouseenter' ,this.onEventEnter, this);
20822                 el.un('mouseleave' ,this.onEventLeave, this);
20823                 el.remove();
20824             },this);
20825         },this);
20826         
20827         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20828             e.remove();
20829         });
20830         
20831     },
20832     
20833     renderEvents: function()
20834     {   
20835         var _this = this;
20836         
20837         this.cells.each(function(c) {
20838             
20839             if(c.row < 5){
20840                 return;
20841             }
20842             
20843             var ev = c.events;
20844             
20845             var r = 4;
20846             if(c.row != c.events.length){
20847                 r = 4 - (4 - (c.row - c.events.length));
20848             }
20849             
20850             c.events = ev.slice(0, r);
20851             c.more = ev.slice(r);
20852             
20853             if(c.more.length && c.more.length == 1){
20854                 c.events.push(c.more.pop());
20855             }
20856             
20857             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20858             
20859         });
20860             
20861         this.cells.each(function(c) {
20862             
20863             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20864             
20865             
20866             for (var e = 0; e < c.events.length; e++){
20867                 var ev = c.events[e];
20868                 var rows = ev.rows;
20869                 
20870                 for(var i = 0; i < rows.length; i++) {
20871                 
20872                     // how many rows should it span..
20873
20874                     var  cfg = {
20875                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20876                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20877
20878                         unselectable : "on",
20879                         cn : [
20880                             {
20881                                 cls: 'fc-event-inner',
20882                                 cn : [
20883     //                                {
20884     //                                  tag:'span',
20885     //                                  cls: 'fc-event-time',
20886     //                                  html : cells.length > 1 ? '' : ev.time
20887     //                                },
20888                                     {
20889                                       tag:'span',
20890                                       cls: 'fc-event-title',
20891                                       html : String.format('{0}', ev.title)
20892                                     }
20893
20894
20895                                 ]
20896                             },
20897                             {
20898                                 cls: 'ui-resizable-handle ui-resizable-e',
20899                                 html : '&nbsp;&nbsp;&nbsp'
20900                             }
20901
20902                         ]
20903                     };
20904
20905                     if (i == 0) {
20906                         cfg.cls += ' fc-event-start';
20907                     }
20908                     if ((i+1) == rows.length) {
20909                         cfg.cls += ' fc-event-end';
20910                     }
20911
20912                     var ctr = _this.el.select('.fc-event-container',true).first();
20913                     var cg = ctr.createChild(cfg);
20914
20915                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20916                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20917
20918                     var r = (c.more.length) ? 1 : 0;
20919                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20920                     cg.setWidth(ebox.right - sbox.x -2);
20921
20922                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20923                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20924                     cg.on('click', _this.onEventClick, _this, ev);
20925
20926                     ev.els.push(cg);
20927                     
20928                 }
20929                 
20930             }
20931             
20932             
20933             if(c.more.length){
20934                 var  cfg = {
20935                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20936                     style : 'position: absolute',
20937                     unselectable : "on",
20938                     cn : [
20939                         {
20940                             cls: 'fc-event-inner',
20941                             cn : [
20942                                 {
20943                                   tag:'span',
20944                                   cls: 'fc-event-title',
20945                                   html : 'More'
20946                                 }
20947
20948
20949                             ]
20950                         },
20951                         {
20952                             cls: 'ui-resizable-handle ui-resizable-e',
20953                             html : '&nbsp;&nbsp;&nbsp'
20954                         }
20955
20956                     ]
20957                 };
20958
20959                 var ctr = _this.el.select('.fc-event-container',true).first();
20960                 var cg = ctr.createChild(cfg);
20961
20962                 var sbox = c.select('.fc-day-content',true).first().getBox();
20963                 var ebox = c.select('.fc-day-content',true).first().getBox();
20964                 //Roo.log(cg);
20965                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20966                 cg.setWidth(ebox.right - sbox.x -2);
20967
20968                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20969                 
20970             }
20971             
20972         });
20973         
20974         
20975         
20976     },
20977     
20978     onEventEnter: function (e, el,event,d) {
20979         this.fireEvent('evententer', this, el, event);
20980     },
20981     
20982     onEventLeave: function (e, el,event,d) {
20983         this.fireEvent('eventleave', this, el, event);
20984     },
20985     
20986     onEventClick: function (e, el,event,d) {
20987         this.fireEvent('eventclick', this, el, event);
20988     },
20989     
20990     onMonthChange: function () {
20991         this.store.load();
20992     },
20993     
20994     onMoreEventClick: function(e, el, more)
20995     {
20996         var _this = this;
20997         
20998         this.calpopover.placement = 'right';
20999         this.calpopover.setTitle('More');
21000         
21001         this.calpopover.setContent('');
21002         
21003         var ctr = this.calpopover.el.select('.popover-content', true).first();
21004         
21005         Roo.each(more, function(m){
21006             var cfg = {
21007                 cls : 'fc-event-hori fc-event-draggable',
21008                 html : m.title
21009             };
21010             var cg = ctr.createChild(cfg);
21011             
21012             cg.on('click', _this.onEventClick, _this, m);
21013         });
21014         
21015         this.calpopover.show(el);
21016         
21017         
21018     },
21019     
21020     onLoad: function () 
21021     {   
21022         this.calevents = [];
21023         var cal = this;
21024         
21025         if(this.store.getCount() > 0){
21026             this.store.data.each(function(d){
21027                cal.addItem({
21028                     id : d.data.id,
21029                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21030                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21031                     time : d.data.start_time,
21032                     title : d.data.title,
21033                     description : d.data.description,
21034                     venue : d.data.venue
21035                 });
21036             });
21037         }
21038         
21039         this.renderEvents();
21040         
21041         if(this.calevents.length && this.loadMask){
21042             this.maskEl.hide();
21043         }
21044     },
21045     
21046     onBeforeLoad: function()
21047     {
21048         this.clearEvents();
21049         if(this.loadMask){
21050             this.maskEl.show();
21051         }
21052     }
21053 });
21054
21055  
21056  /*
21057  * - LGPL
21058  *
21059  * element
21060  * 
21061  */
21062
21063 /**
21064  * @class Roo.bootstrap.Popover
21065  * @extends Roo.bootstrap.Component
21066  * @builder-top
21067  * Bootstrap Popover class
21068  * @cfg {String} html contents of the popover   (or false to use children..)
21069  * @cfg {String} title of popover (or false to hide)
21070  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21071  * @cfg {String} trigger click || hover (or false to trigger manually)
21072  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21073  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21074  *      - if false and it has a 'parent' then it will be automatically added to that element
21075  *      - if string - Roo.get  will be called 
21076  * @cfg {Number} delay - delay before showing
21077  
21078  * @constructor
21079  * Create a new Popover
21080  * @param {Object} config The config object
21081  */
21082
21083 Roo.bootstrap.Popover = function(config){
21084     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21085     
21086     this.addEvents({
21087         // raw events
21088          /**
21089          * @event show
21090          * After the popover show
21091          * 
21092          * @param {Roo.bootstrap.Popover} this
21093          */
21094         "show" : true,
21095         /**
21096          * @event hide
21097          * After the popover hide
21098          * 
21099          * @param {Roo.bootstrap.Popover} this
21100          */
21101         "hide" : true
21102     });
21103 };
21104
21105 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21106     
21107     title: false,
21108     html: false,
21109     
21110     placement : 'right',
21111     trigger : 'hover', // hover
21112     modal : false,
21113     delay : 0,
21114     
21115     over: false,
21116     
21117     can_build_overlaid : false,
21118     
21119     maskEl : false, // the mask element
21120     headerEl : false,
21121     contentEl : false,
21122     alignEl : false, // when show is called with an element - this get's stored.
21123     
21124     getChildContainer : function()
21125     {
21126         return this.contentEl;
21127         
21128     },
21129     getPopoverHeader : function()
21130     {
21131         this.title = true; // flag not to hide it..
21132         this.headerEl.addClass('p-0');
21133         return this.headerEl
21134     },
21135     
21136     
21137     getAutoCreate : function(){
21138          
21139         var cfg = {
21140            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21141            style: 'display:block',
21142            cn : [
21143                 {
21144                     cls : 'arrow'
21145                 },
21146                 {
21147                     cls : 'popover-inner ',
21148                     cn : [
21149                         {
21150                             tag: 'h3',
21151                             cls: 'popover-title popover-header',
21152                             html : this.title === false ? '' : this.title
21153                         },
21154                         {
21155                             cls : 'popover-content popover-body '  + (this.cls || ''),
21156                             html : this.html || ''
21157                         }
21158                     ]
21159                     
21160                 }
21161            ]
21162         };
21163         
21164         return cfg;
21165     },
21166     /**
21167      * @param {string} the title
21168      */
21169     setTitle: function(str)
21170     {
21171         this.title = str;
21172         if (this.el) {
21173             this.headerEl.dom.innerHTML = str;
21174         }
21175         
21176     },
21177     /**
21178      * @param {string} the body content
21179      */
21180     setContent: function(str)
21181     {
21182         this.html = str;
21183         if (this.contentEl) {
21184             this.contentEl.dom.innerHTML = str;
21185         }
21186         
21187     },
21188     // as it get's added to the bottom of the page.
21189     onRender : function(ct, position)
21190     {
21191         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21192         
21193         
21194         
21195         if(!this.el){
21196             var cfg = Roo.apply({},  this.getAutoCreate());
21197             cfg.id = Roo.id();
21198             
21199             if (this.cls) {
21200                 cfg.cls += ' ' + this.cls;
21201             }
21202             if (this.style) {
21203                 cfg.style = this.style;
21204             }
21205             //Roo.log("adding to ");
21206             this.el = Roo.get(document.body).createChild(cfg, position);
21207 //            Roo.log(this.el);
21208         }
21209         
21210         this.contentEl = this.el.select('.popover-content',true).first();
21211         this.headerEl =  this.el.select('.popover-title',true).first();
21212         
21213         var nitems = [];
21214         if(typeof(this.items) != 'undefined'){
21215             var items = this.items;
21216             delete this.items;
21217
21218             for(var i =0;i < items.length;i++) {
21219                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21220             }
21221         }
21222
21223         this.items = nitems;
21224         
21225         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21226         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21227         
21228         
21229         
21230         this.initEvents();
21231     },
21232     
21233     resizeMask : function()
21234     {
21235         this.maskEl.setSize(
21236             Roo.lib.Dom.getViewWidth(true),
21237             Roo.lib.Dom.getViewHeight(true)
21238         );
21239     },
21240     
21241     initEvents : function()
21242     {
21243         
21244         if (!this.modal) { 
21245             Roo.bootstrap.Popover.register(this);
21246         }
21247          
21248         this.arrowEl = this.el.select('.arrow',true).first();
21249         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21250         this.el.enableDisplayMode('block');
21251         this.el.hide();
21252  
21253         
21254         if (this.over === false && !this.parent()) {
21255             return; 
21256         }
21257         if (this.triggers === false) {
21258             return;
21259         }
21260          
21261         // support parent
21262         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21263         var triggers = this.trigger ? this.trigger.split(' ') : [];
21264         Roo.each(triggers, function(trigger) {
21265         
21266             if (trigger == 'click') {
21267                 on_el.on('click', this.toggle, this);
21268             } else if (trigger != 'manual') {
21269                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21270                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21271       
21272                 on_el.on(eventIn  ,this.enter, this);
21273                 on_el.on(eventOut, this.leave, this);
21274             }
21275         }, this);
21276     },
21277     
21278     
21279     // private
21280     timeout : null,
21281     hoverState : null,
21282     
21283     toggle : function () {
21284         this.hoverState == 'in' ? this.leave() : this.enter();
21285     },
21286     
21287     enter : function () {
21288         
21289         clearTimeout(this.timeout);
21290     
21291         this.hoverState = 'in';
21292     
21293         if (!this.delay || !this.delay.show) {
21294             this.show();
21295             return;
21296         }
21297         var _t = this;
21298         this.timeout = setTimeout(function () {
21299             if (_t.hoverState == 'in') {
21300                 _t.show();
21301             }
21302         }, this.delay.show)
21303     },
21304     
21305     leave : function() {
21306         clearTimeout(this.timeout);
21307     
21308         this.hoverState = 'out';
21309     
21310         if (!this.delay || !this.delay.hide) {
21311             this.hide();
21312             return;
21313         }
21314         var _t = this;
21315         this.timeout = setTimeout(function () {
21316             if (_t.hoverState == 'out') {
21317                 _t.hide();
21318             }
21319         }, this.delay.hide)
21320     },
21321     /**
21322      * Show the popover
21323      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21324      * @param {string} (left|right|top|bottom) position
21325      */
21326     show : function (on_el, placement)
21327     {
21328         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21329         on_el = on_el || false; // default to false
21330          
21331         if (!on_el) {
21332             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21333                 on_el = this.parent().el;
21334             } else if (this.over) {
21335                 on_el = Roo.get(this.over);
21336             }
21337             
21338         }
21339         
21340         this.alignEl = Roo.get( on_el );
21341
21342         if (!this.el) {
21343             this.render(document.body);
21344         }
21345         
21346         
21347          
21348         
21349         if (this.title === false) {
21350             this.headerEl.hide();
21351         }
21352         
21353        
21354         this.el.show();
21355         this.el.dom.style.display = 'block';
21356          
21357  
21358         if (this.alignEl) {
21359             this.updatePosition(this.placement, true);
21360              
21361         } else {
21362             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21363             var es = this.el.getSize();
21364             var x = Roo.lib.Dom.getViewWidth()/2;
21365             var y = Roo.lib.Dom.getViewHeight()/2;
21366             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21367             
21368         }
21369
21370         
21371         //var arrow = this.el.select('.arrow',true).first();
21372         //arrow.set(align[2], 
21373         
21374         this.el.addClass('in');
21375         
21376          
21377         
21378         this.hoverState = 'in';
21379         
21380         if (this.modal) {
21381             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21382             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21383             this.maskEl.dom.style.display = 'block';
21384             this.maskEl.addClass('show');
21385         }
21386         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21387  
21388         this.fireEvent('show', this);
21389         
21390     },
21391     /**
21392      * fire this manually after loading a grid in the table for example
21393      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21394      * @param {Boolean} try and move it if we cant get right position.
21395      */
21396     updatePosition : function(placement, try_move)
21397     {
21398         // allow for calling with no parameters
21399         placement = placement   ? placement :  this.placement;
21400         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21401         
21402         this.el.removeClass([
21403             'fade','top','bottom', 'left', 'right','in',
21404             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21405         ]);
21406         this.el.addClass(placement + ' bs-popover-' + placement);
21407         
21408         if (!this.alignEl ) {
21409             return false;
21410         }
21411         
21412         switch (placement) {
21413             case 'right':
21414                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21415                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21416                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21417                     //normal display... or moved up/down.
21418                     this.el.setXY(offset);
21419                     var xy = this.alignEl.getAnchorXY('tr', false);
21420                     xy[0]+=2;xy[1]+=5;
21421                     this.arrowEl.setXY(xy);
21422                     return true;
21423                 }
21424                 // continue through...
21425                 return this.updatePosition('left', false);
21426                 
21427             
21428             case 'left':
21429                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21430                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21431                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21432                     //normal display... or moved up/down.
21433                     this.el.setXY(offset);
21434                     var xy = this.alignEl.getAnchorXY('tl', false);
21435                     xy[0]-=10;xy[1]+=5; // << fix me
21436                     this.arrowEl.setXY(xy);
21437                     return true;
21438                 }
21439                 // call self...
21440                 return this.updatePosition('right', false);
21441             
21442             case 'top':
21443                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21444                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21445                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21446                     //normal display... or moved up/down.
21447                     this.el.setXY(offset);
21448                     var xy = this.alignEl.getAnchorXY('t', false);
21449                     xy[1]-=10; // << fix me
21450                     this.arrowEl.setXY(xy);
21451                     return true;
21452                 }
21453                 // fall through
21454                return this.updatePosition('bottom', false);
21455             
21456             case 'bottom':
21457                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21458                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21459                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21460                     //normal display... or moved up/down.
21461                     this.el.setXY(offset);
21462                     var xy = this.alignEl.getAnchorXY('b', false);
21463                      xy[1]+=2; // << fix me
21464                     this.arrowEl.setXY(xy);
21465                     return true;
21466                 }
21467                 // fall through
21468                 return this.updatePosition('top', false);
21469                 
21470             
21471         }
21472         
21473         
21474         return false;
21475     },
21476     
21477     hide : function()
21478     {
21479         this.el.setXY([0,0]);
21480         this.el.removeClass('in');
21481         this.el.hide();
21482         this.hoverState = null;
21483         this.maskEl.hide(); // always..
21484         this.fireEvent('hide', this);
21485     }
21486     
21487 });
21488
21489
21490 Roo.apply(Roo.bootstrap.Popover, {
21491
21492     alignment : {
21493         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21494         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21495         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21496         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21497     },
21498     
21499     zIndex : 20001,
21500
21501     clickHander : false,
21502     
21503     
21504
21505     onMouseDown : function(e)
21506     {
21507         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21508             /// what is nothing is showing..
21509             this.hideAll();
21510         }
21511          
21512     },
21513     
21514     
21515     popups : [],
21516     
21517     register : function(popup)
21518     {
21519         if (!Roo.bootstrap.Popover.clickHandler) {
21520             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21521         }
21522         // hide other popups.
21523         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21524         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21525         this.hideAll(); //<< why?
21526         //this.popups.push(popup);
21527     },
21528     hideAll : function()
21529     {
21530         this.popups.forEach(function(p) {
21531             p.hide();
21532         });
21533     },
21534     onShow : function() {
21535         Roo.bootstrap.Popover.popups.push(this);
21536     },
21537     onHide : function() {
21538         Roo.bootstrap.Popover.popups.remove(this);
21539     } 
21540
21541 });/*
21542  * - LGPL
21543  *
21544  * Card header - holder for the card header elements.
21545  * 
21546  */
21547
21548 /**
21549  * @class Roo.bootstrap.PopoverNav
21550  * @extends Roo.bootstrap.NavGroup
21551  * Bootstrap Popover header navigation class
21552  * @constructor
21553  * Create a new Popover Header Navigation 
21554  * @param {Object} config The config object
21555  */
21556
21557 Roo.bootstrap.PopoverNav = function(config){
21558     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21559 };
21560
21561 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21562     
21563     
21564     container_method : 'getPopoverHeader' 
21565     
21566      
21567     
21568     
21569    
21570 });
21571
21572  
21573
21574  /*
21575  * - LGPL
21576  *
21577  * Progress
21578  * 
21579  */
21580
21581 /**
21582  * @class Roo.bootstrap.Progress
21583  * @extends Roo.bootstrap.Component
21584  * Bootstrap Progress class
21585  * @cfg {Boolean} striped striped of the progress bar
21586  * @cfg {Boolean} active animated of the progress bar
21587  * 
21588  * 
21589  * @constructor
21590  * Create a new Progress
21591  * @param {Object} config The config object
21592  */
21593
21594 Roo.bootstrap.Progress = function(config){
21595     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21596 };
21597
21598 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21599     
21600     striped : false,
21601     active: false,
21602     
21603     getAutoCreate : function(){
21604         var cfg = {
21605             tag: 'div',
21606             cls: 'progress'
21607         };
21608         
21609         
21610         if(this.striped){
21611             cfg.cls += ' progress-striped';
21612         }
21613       
21614         if(this.active){
21615             cfg.cls += ' active';
21616         }
21617         
21618         
21619         return cfg;
21620     }
21621    
21622 });
21623
21624  
21625
21626  /*
21627  * - LGPL
21628  *
21629  * ProgressBar
21630  * 
21631  */
21632
21633 /**
21634  * @class Roo.bootstrap.ProgressBar
21635  * @extends Roo.bootstrap.Component
21636  * Bootstrap ProgressBar class
21637  * @cfg {Number} aria_valuenow aria-value now
21638  * @cfg {Number} aria_valuemin aria-value min
21639  * @cfg {Number} aria_valuemax aria-value max
21640  * @cfg {String} label label for the progress bar
21641  * @cfg {String} panel (success | info | warning | danger )
21642  * @cfg {String} role role of the progress bar
21643  * @cfg {String} sr_only text
21644  * 
21645  * 
21646  * @constructor
21647  * Create a new ProgressBar
21648  * @param {Object} config The config object
21649  */
21650
21651 Roo.bootstrap.ProgressBar = function(config){
21652     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21653 };
21654
21655 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21656     
21657     aria_valuenow : 0,
21658     aria_valuemin : 0,
21659     aria_valuemax : 100,
21660     label : false,
21661     panel : false,
21662     role : false,
21663     sr_only: false,
21664     
21665     getAutoCreate : function()
21666     {
21667         
21668         var cfg = {
21669             tag: 'div',
21670             cls: 'progress-bar',
21671             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21672         };
21673         
21674         if(this.sr_only){
21675             cfg.cn = {
21676                 tag: 'span',
21677                 cls: 'sr-only',
21678                 html: this.sr_only
21679             }
21680         }
21681         
21682         if(this.role){
21683             cfg.role = this.role;
21684         }
21685         
21686         if(this.aria_valuenow){
21687             cfg['aria-valuenow'] = this.aria_valuenow;
21688         }
21689         
21690         if(this.aria_valuemin){
21691             cfg['aria-valuemin'] = this.aria_valuemin;
21692         }
21693         
21694         if(this.aria_valuemax){
21695             cfg['aria-valuemax'] = this.aria_valuemax;
21696         }
21697         
21698         if(this.label && !this.sr_only){
21699             cfg.html = this.label;
21700         }
21701         
21702         if(this.panel){
21703             cfg.cls += ' progress-bar-' + this.panel;
21704         }
21705         
21706         return cfg;
21707     },
21708     
21709     update : function(aria_valuenow)
21710     {
21711         this.aria_valuenow = aria_valuenow;
21712         
21713         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21714     }
21715    
21716 });
21717
21718  
21719
21720  /*
21721  * - LGPL
21722  *
21723  * column
21724  * 
21725  */
21726
21727 /**
21728  * @class Roo.bootstrap.TabGroup
21729  * @extends Roo.bootstrap.Column
21730  * Bootstrap Column class
21731  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21732  * @cfg {Boolean} carousel true to make the group behave like a carousel
21733  * @cfg {Boolean} bullets show bullets for the panels
21734  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21735  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21736  * @cfg {Boolean} showarrow (true|false) show arrow default true
21737  * 
21738  * @constructor
21739  * Create a new TabGroup
21740  * @param {Object} config The config object
21741  */
21742
21743 Roo.bootstrap.TabGroup = function(config){
21744     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21745     if (!this.navId) {
21746         this.navId = Roo.id();
21747     }
21748     this.tabs = [];
21749     Roo.bootstrap.TabGroup.register(this);
21750     
21751 };
21752
21753 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21754     
21755     carousel : false,
21756     transition : false,
21757     bullets : 0,
21758     timer : 0,
21759     autoslide : false,
21760     slideFn : false,
21761     slideOnTouch : false,
21762     showarrow : true,
21763     
21764     getAutoCreate : function()
21765     {
21766         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21767         
21768         cfg.cls += ' tab-content';
21769         
21770         if (this.carousel) {
21771             cfg.cls += ' carousel slide';
21772             
21773             cfg.cn = [{
21774                cls : 'carousel-inner',
21775                cn : []
21776             }];
21777         
21778             if(this.bullets  && !Roo.isTouch){
21779                 
21780                 var bullets = {
21781                     cls : 'carousel-bullets',
21782                     cn : []
21783                 };
21784                
21785                 if(this.bullets_cls){
21786                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21787                 }
21788                 
21789                 bullets.cn.push({
21790                     cls : 'clear'
21791                 });
21792                 
21793                 cfg.cn[0].cn.push(bullets);
21794             }
21795             
21796             if(this.showarrow){
21797                 cfg.cn[0].cn.push({
21798                     tag : 'div',
21799                     class : 'carousel-arrow',
21800                     cn : [
21801                         {
21802                             tag : 'div',
21803                             class : 'carousel-prev',
21804                             cn : [
21805                                 {
21806                                     tag : 'i',
21807                                     class : 'fa fa-chevron-left'
21808                                 }
21809                             ]
21810                         },
21811                         {
21812                             tag : 'div',
21813                             class : 'carousel-next',
21814                             cn : [
21815                                 {
21816                                     tag : 'i',
21817                                     class : 'fa fa-chevron-right'
21818                                 }
21819                             ]
21820                         }
21821                     ]
21822                 });
21823             }
21824             
21825         }
21826         
21827         return cfg;
21828     },
21829     
21830     initEvents:  function()
21831     {
21832 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21833 //            this.el.on("touchstart", this.onTouchStart, this);
21834 //        }
21835         
21836         if(this.autoslide){
21837             var _this = this;
21838             
21839             this.slideFn = window.setInterval(function() {
21840                 _this.showPanelNext();
21841             }, this.timer);
21842         }
21843         
21844         if(this.showarrow){
21845             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21846             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21847         }
21848         
21849         
21850     },
21851     
21852 //    onTouchStart : function(e, el, o)
21853 //    {
21854 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21855 //            return;
21856 //        }
21857 //        
21858 //        this.showPanelNext();
21859 //    },
21860     
21861     
21862     getChildContainer : function()
21863     {
21864         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21865     },
21866     
21867     /**
21868     * register a Navigation item
21869     * @param {Roo.bootstrap.NavItem} the navitem to add
21870     */
21871     register : function(item)
21872     {
21873         this.tabs.push( item);
21874         item.navId = this.navId; // not really needed..
21875         this.addBullet();
21876     
21877     },
21878     
21879     getActivePanel : function()
21880     {
21881         var r = false;
21882         Roo.each(this.tabs, function(t) {
21883             if (t.active) {
21884                 r = t;
21885                 return false;
21886             }
21887             return null;
21888         });
21889         return r;
21890         
21891     },
21892     getPanelByName : function(n)
21893     {
21894         var r = false;
21895         Roo.each(this.tabs, function(t) {
21896             if (t.tabId == n) {
21897                 r = t;
21898                 return false;
21899             }
21900             return null;
21901         });
21902         return r;
21903     },
21904     indexOfPanel : function(p)
21905     {
21906         var r = false;
21907         Roo.each(this.tabs, function(t,i) {
21908             if (t.tabId == p.tabId) {
21909                 r = i;
21910                 return false;
21911             }
21912             return null;
21913         });
21914         return r;
21915     },
21916     /**
21917      * show a specific panel
21918      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21919      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21920      */
21921     showPanel : function (pan)
21922     {
21923         if(this.transition || typeof(pan) == 'undefined'){
21924             Roo.log("waiting for the transitionend");
21925             return false;
21926         }
21927         
21928         if (typeof(pan) == 'number') {
21929             pan = this.tabs[pan];
21930         }
21931         
21932         if (typeof(pan) == 'string') {
21933             pan = this.getPanelByName(pan);
21934         }
21935         
21936         var cur = this.getActivePanel();
21937         
21938         if(!pan || !cur){
21939             Roo.log('pan or acitve pan is undefined');
21940             return false;
21941         }
21942         
21943         if (pan.tabId == this.getActivePanel().tabId) {
21944             return true;
21945         }
21946         
21947         if (false === cur.fireEvent('beforedeactivate')) {
21948             return false;
21949         }
21950         
21951         if(this.bullets > 0 && !Roo.isTouch){
21952             this.setActiveBullet(this.indexOfPanel(pan));
21953         }
21954         
21955         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21956             
21957             //class="carousel-item carousel-item-next carousel-item-left"
21958             
21959             this.transition = true;
21960             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21961             var lr = dir == 'next' ? 'left' : 'right';
21962             pan.el.addClass(dir); // or prev
21963             pan.el.addClass('carousel-item-' + dir); // or prev
21964             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21965             cur.el.addClass(lr); // or right
21966             pan.el.addClass(lr);
21967             cur.el.addClass('carousel-item-' +lr); // or right
21968             pan.el.addClass('carousel-item-' +lr);
21969             
21970             
21971             var _this = this;
21972             cur.el.on('transitionend', function() {
21973                 Roo.log("trans end?");
21974                 
21975                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21976                 pan.setActive(true);
21977                 
21978                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21979                 cur.setActive(false);
21980                 
21981                 _this.transition = false;
21982                 
21983             }, this, { single:  true } );
21984             
21985             return true;
21986         }
21987         
21988         cur.setActive(false);
21989         pan.setActive(true);
21990         
21991         return true;
21992         
21993     },
21994     showPanelNext : function()
21995     {
21996         var i = this.indexOfPanel(this.getActivePanel());
21997         
21998         if (i >= this.tabs.length - 1 && !this.autoslide) {
21999             return;
22000         }
22001         
22002         if (i >= this.tabs.length - 1 && this.autoslide) {
22003             i = -1;
22004         }
22005         
22006         this.showPanel(this.tabs[i+1]);
22007     },
22008     
22009     showPanelPrev : function()
22010     {
22011         var i = this.indexOfPanel(this.getActivePanel());
22012         
22013         if (i  < 1 && !this.autoslide) {
22014             return;
22015         }
22016         
22017         if (i < 1 && this.autoslide) {
22018             i = this.tabs.length;
22019         }
22020         
22021         this.showPanel(this.tabs[i-1]);
22022     },
22023     
22024     
22025     addBullet: function()
22026     {
22027         if(!this.bullets || Roo.isTouch){
22028             return;
22029         }
22030         var ctr = this.el.select('.carousel-bullets',true).first();
22031         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22032         var bullet = ctr.createChild({
22033             cls : 'bullet bullet-' + i
22034         },ctr.dom.lastChild);
22035         
22036         
22037         var _this = this;
22038         
22039         bullet.on('click', (function(e, el, o, ii, t){
22040
22041             e.preventDefault();
22042
22043             this.showPanel(ii);
22044
22045             if(this.autoslide && this.slideFn){
22046                 clearInterval(this.slideFn);
22047                 this.slideFn = window.setInterval(function() {
22048                     _this.showPanelNext();
22049                 }, this.timer);
22050             }
22051
22052         }).createDelegate(this, [i, bullet], true));
22053                 
22054         
22055     },
22056      
22057     setActiveBullet : function(i)
22058     {
22059         if(Roo.isTouch){
22060             return;
22061         }
22062         
22063         Roo.each(this.el.select('.bullet', true).elements, function(el){
22064             el.removeClass('selected');
22065         });
22066
22067         var bullet = this.el.select('.bullet-' + i, true).first();
22068         
22069         if(!bullet){
22070             return;
22071         }
22072         
22073         bullet.addClass('selected');
22074     }
22075     
22076     
22077   
22078 });
22079
22080  
22081
22082  
22083  
22084 Roo.apply(Roo.bootstrap.TabGroup, {
22085     
22086     groups: {},
22087      /**
22088     * register a Navigation Group
22089     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22090     */
22091     register : function(navgrp)
22092     {
22093         this.groups[navgrp.navId] = navgrp;
22094         
22095     },
22096     /**
22097     * fetch a Navigation Group based on the navigation ID
22098     * if one does not exist , it will get created.
22099     * @param {string} the navgroup to add
22100     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22101     */
22102     get: function(navId) {
22103         if (typeof(this.groups[navId]) == 'undefined') {
22104             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22105         }
22106         return this.groups[navId] ;
22107     }
22108     
22109     
22110     
22111 });
22112
22113  /*
22114  * - LGPL
22115  *
22116  * TabPanel
22117  * 
22118  */
22119
22120 /**
22121  * @class Roo.bootstrap.TabPanel
22122  * @extends Roo.bootstrap.Component
22123  * Bootstrap TabPanel class
22124  * @cfg {Boolean} active panel active
22125  * @cfg {String} html panel content
22126  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22127  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22128  * @cfg {String} href click to link..
22129  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22130  * 
22131  * 
22132  * @constructor
22133  * Create a new TabPanel
22134  * @param {Object} config The config object
22135  */
22136
22137 Roo.bootstrap.TabPanel = function(config){
22138     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22139     this.addEvents({
22140         /**
22141              * @event changed
22142              * Fires when the active status changes
22143              * @param {Roo.bootstrap.TabPanel} this
22144              * @param {Boolean} state the new state
22145             
22146          */
22147         'changed': true,
22148         /**
22149              * @event beforedeactivate
22150              * Fires before a tab is de-activated - can be used to do validation on a form.
22151              * @param {Roo.bootstrap.TabPanel} this
22152              * @return {Boolean} false if there is an error
22153             
22154          */
22155         'beforedeactivate': true
22156      });
22157     
22158     this.tabId = this.tabId || Roo.id();
22159   
22160 };
22161
22162 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22163     
22164     active: false,
22165     html: false,
22166     tabId: false,
22167     navId : false,
22168     href : '',
22169     touchSlide : false,
22170     getAutoCreate : function(){
22171         
22172         
22173         var cfg = {
22174             tag: 'div',
22175             // item is needed for carousel - not sure if it has any effect otherwise
22176             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22177             html: this.html || ''
22178         };
22179         
22180         if(this.active){
22181             cfg.cls += ' active';
22182         }
22183         
22184         if(this.tabId){
22185             cfg.tabId = this.tabId;
22186         }
22187         
22188         
22189         
22190         return cfg;
22191     },
22192     
22193     initEvents:  function()
22194     {
22195         var p = this.parent();
22196         
22197         this.navId = this.navId || p.navId;
22198         
22199         if (typeof(this.navId) != 'undefined') {
22200             // not really needed.. but just in case.. parent should be a NavGroup.
22201             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22202             
22203             tg.register(this);
22204             
22205             var i = tg.tabs.length - 1;
22206             
22207             if(this.active && tg.bullets > 0 && i < tg.bullets){
22208                 tg.setActiveBullet(i);
22209             }
22210         }
22211         
22212         this.el.on('click', this.onClick, this);
22213         
22214         if(Roo.isTouch && this.touchSlide){
22215             this.el.on("touchstart", this.onTouchStart, this);
22216             this.el.on("touchmove", this.onTouchMove, this);
22217             this.el.on("touchend", this.onTouchEnd, this);
22218         }
22219         
22220     },
22221     
22222     onRender : function(ct, position)
22223     {
22224         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22225     },
22226     
22227     setActive : function(state)
22228     {
22229         Roo.log("panel - set active " + this.tabId + "=" + state);
22230         
22231         this.active = state;
22232         if (!state) {
22233             this.el.removeClass('active');
22234             
22235         } else  if (!this.el.hasClass('active')) {
22236             this.el.addClass('active');
22237         }
22238         
22239         this.fireEvent('changed', this, state);
22240     },
22241     
22242     onClick : function(e)
22243     {
22244         e.preventDefault();
22245         
22246         if(!this.href.length){
22247             return;
22248         }
22249         
22250         window.location.href = this.href;
22251     },
22252     
22253     startX : 0,
22254     startY : 0,
22255     endX : 0,
22256     endY : 0,
22257     swiping : false,
22258     
22259     onTouchStart : function(e)
22260     {
22261         this.swiping = false;
22262         
22263         this.startX = e.browserEvent.touches[0].clientX;
22264         this.startY = e.browserEvent.touches[0].clientY;
22265     },
22266     
22267     onTouchMove : function(e)
22268     {
22269         this.swiping = true;
22270         
22271         this.endX = e.browserEvent.touches[0].clientX;
22272         this.endY = e.browserEvent.touches[0].clientY;
22273     },
22274     
22275     onTouchEnd : function(e)
22276     {
22277         if(!this.swiping){
22278             this.onClick(e);
22279             return;
22280         }
22281         
22282         var tabGroup = this.parent();
22283         
22284         if(this.endX > this.startX){ // swiping right
22285             tabGroup.showPanelPrev();
22286             return;
22287         }
22288         
22289         if(this.startX > this.endX){ // swiping left
22290             tabGroup.showPanelNext();
22291             return;
22292         }
22293     }
22294     
22295     
22296 });
22297  
22298
22299  
22300
22301  /*
22302  * - LGPL
22303  *
22304  * DateField
22305  * 
22306  */
22307
22308 /**
22309  * @class Roo.bootstrap.DateField
22310  * @extends Roo.bootstrap.Input
22311  * Bootstrap DateField class
22312  * @cfg {Number} weekStart default 0
22313  * @cfg {String} viewMode default empty, (months|years)
22314  * @cfg {String} minViewMode default empty, (months|years)
22315  * @cfg {Number} startDate default -Infinity
22316  * @cfg {Number} endDate default Infinity
22317  * @cfg {Boolean} todayHighlight default false
22318  * @cfg {Boolean} todayBtn default false
22319  * @cfg {Boolean} calendarWeeks default false
22320  * @cfg {Object} daysOfWeekDisabled default empty
22321  * @cfg {Boolean} singleMode default false (true | false)
22322  * 
22323  * @cfg {Boolean} keyboardNavigation default true
22324  * @cfg {String} language default en
22325  * 
22326  * @constructor
22327  * Create a new DateField
22328  * @param {Object} config The config object
22329  */
22330
22331 Roo.bootstrap.DateField = function(config){
22332     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22333      this.addEvents({
22334             /**
22335              * @event show
22336              * Fires when this field show.
22337              * @param {Roo.bootstrap.DateField} this
22338              * @param {Mixed} date The date value
22339              */
22340             show : true,
22341             /**
22342              * @event show
22343              * Fires when this field hide.
22344              * @param {Roo.bootstrap.DateField} this
22345              * @param {Mixed} date The date value
22346              */
22347             hide : true,
22348             /**
22349              * @event select
22350              * Fires when select a date.
22351              * @param {Roo.bootstrap.DateField} this
22352              * @param {Mixed} date The date value
22353              */
22354             select : true,
22355             /**
22356              * @event beforeselect
22357              * Fires when before select a date.
22358              * @param {Roo.bootstrap.DateField} this
22359              * @param {Mixed} date The date value
22360              */
22361             beforeselect : true
22362         });
22363 };
22364
22365 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22366     
22367     /**
22368      * @cfg {String} format
22369      * The default date format string which can be overriden for localization support.  The format must be
22370      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22371      */
22372     format : "m/d/y",
22373     /**
22374      * @cfg {String} altFormats
22375      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22376      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22377      */
22378     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22379     
22380     weekStart : 0,
22381     
22382     viewMode : '',
22383     
22384     minViewMode : '',
22385     
22386     todayHighlight : false,
22387     
22388     todayBtn: false,
22389     
22390     language: 'en',
22391     
22392     keyboardNavigation: true,
22393     
22394     calendarWeeks: false,
22395     
22396     startDate: -Infinity,
22397     
22398     endDate: Infinity,
22399     
22400     daysOfWeekDisabled: [],
22401     
22402     _events: [],
22403     
22404     singleMode : false,
22405     
22406     UTCDate: function()
22407     {
22408         return new Date(Date.UTC.apply(Date, arguments));
22409     },
22410     
22411     UTCToday: function()
22412     {
22413         var today = new Date();
22414         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22415     },
22416     
22417     getDate: function() {
22418             var d = this.getUTCDate();
22419             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22420     },
22421     
22422     getUTCDate: function() {
22423             return this.date;
22424     },
22425     
22426     setDate: function(d) {
22427             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22428     },
22429     
22430     setUTCDate: function(d) {
22431             this.date = d;
22432             this.setValue(this.formatDate(this.date));
22433     },
22434         
22435     onRender: function(ct, position)
22436     {
22437         
22438         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22439         
22440         this.language = this.language || 'en';
22441         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22442         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22443         
22444         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22445         this.format = this.format || 'm/d/y';
22446         this.isInline = false;
22447         this.isInput = true;
22448         this.component = this.el.select('.add-on', true).first() || false;
22449         this.component = (this.component && this.component.length === 0) ? false : this.component;
22450         this.hasInput = this.component && this.inputEl().length;
22451         
22452         if (typeof(this.minViewMode === 'string')) {
22453             switch (this.minViewMode) {
22454                 case 'months':
22455                     this.minViewMode = 1;
22456                     break;
22457                 case 'years':
22458                     this.minViewMode = 2;
22459                     break;
22460                 default:
22461                     this.minViewMode = 0;
22462                     break;
22463             }
22464         }
22465         
22466         if (typeof(this.viewMode === 'string')) {
22467             switch (this.viewMode) {
22468                 case 'months':
22469                     this.viewMode = 1;
22470                     break;
22471                 case 'years':
22472                     this.viewMode = 2;
22473                     break;
22474                 default:
22475                     this.viewMode = 0;
22476                     break;
22477             }
22478         }
22479                 
22480         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22481         
22482 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22483         
22484         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22485         
22486         this.picker().on('mousedown', this.onMousedown, this);
22487         this.picker().on('click', this.onClick, this);
22488         
22489         this.picker().addClass('datepicker-dropdown');
22490         
22491         this.startViewMode = this.viewMode;
22492         
22493         if(this.singleMode){
22494             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22495                 v.setVisibilityMode(Roo.Element.DISPLAY);
22496                 v.hide();
22497             });
22498             
22499             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22500                 v.setStyle('width', '189px');
22501             });
22502         }
22503         
22504         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22505             if(!this.calendarWeeks){
22506                 v.remove();
22507                 return;
22508             }
22509             
22510             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22511             v.attr('colspan', function(i, val){
22512                 return parseInt(val) + 1;
22513             });
22514         });
22515                         
22516         
22517         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22518         
22519         this.setStartDate(this.startDate);
22520         this.setEndDate(this.endDate);
22521         
22522         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22523         
22524         this.fillDow();
22525         this.fillMonths();
22526         this.update();
22527         this.showMode();
22528         
22529         if(this.isInline) {
22530             this.showPopup();
22531         }
22532     },
22533     
22534     picker : function()
22535     {
22536         return this.pickerEl;
22537 //        return this.el.select('.datepicker', true).first();
22538     },
22539     
22540     fillDow: function()
22541     {
22542         var dowCnt = this.weekStart;
22543         
22544         var dow = {
22545             tag: 'tr',
22546             cn: [
22547                 
22548             ]
22549         };
22550         
22551         if(this.calendarWeeks){
22552             dow.cn.push({
22553                 tag: 'th',
22554                 cls: 'cw',
22555                 html: '&nbsp;'
22556             })
22557         }
22558         
22559         while (dowCnt < this.weekStart + 7) {
22560             dow.cn.push({
22561                 tag: 'th',
22562                 cls: 'dow',
22563                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22564             });
22565         }
22566         
22567         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22568     },
22569     
22570     fillMonths: function()
22571     {    
22572         var i = 0;
22573         var months = this.picker().select('>.datepicker-months td', true).first();
22574         
22575         months.dom.innerHTML = '';
22576         
22577         while (i < 12) {
22578             var month = {
22579                 tag: 'span',
22580                 cls: 'month',
22581                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22582             };
22583             
22584             months.createChild(month);
22585         }
22586         
22587     },
22588     
22589     update: function()
22590     {
22591         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;
22592         
22593         if (this.date < this.startDate) {
22594             this.viewDate = new Date(this.startDate);
22595         } else if (this.date > this.endDate) {
22596             this.viewDate = new Date(this.endDate);
22597         } else {
22598             this.viewDate = new Date(this.date);
22599         }
22600         
22601         this.fill();
22602     },
22603     
22604     fill: function() 
22605     {
22606         var d = new Date(this.viewDate),
22607                 year = d.getUTCFullYear(),
22608                 month = d.getUTCMonth(),
22609                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22610                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22611                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22612                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22613                 currentDate = this.date && this.date.valueOf(),
22614                 today = this.UTCToday();
22615         
22616         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22617         
22618 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22619         
22620 //        this.picker.select('>tfoot th.today').
22621 //                                              .text(dates[this.language].today)
22622 //                                              .toggle(this.todayBtn !== false);
22623     
22624         this.updateNavArrows();
22625         this.fillMonths();
22626                                                 
22627         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22628         
22629         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22630          
22631         prevMonth.setUTCDate(day);
22632         
22633         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22634         
22635         var nextMonth = new Date(prevMonth);
22636         
22637         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22638         
22639         nextMonth = nextMonth.valueOf();
22640         
22641         var fillMonths = false;
22642         
22643         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22644         
22645         while(prevMonth.valueOf() <= nextMonth) {
22646             var clsName = '';
22647             
22648             if (prevMonth.getUTCDay() === this.weekStart) {
22649                 if(fillMonths){
22650                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22651                 }
22652                     
22653                 fillMonths = {
22654                     tag: 'tr',
22655                     cn: []
22656                 };
22657                 
22658                 if(this.calendarWeeks){
22659                     // ISO 8601: First week contains first thursday.
22660                     // ISO also states week starts on Monday, but we can be more abstract here.
22661                     var
22662                     // Start of current week: based on weekstart/current date
22663                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22664                     // Thursday of this week
22665                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22666                     // First Thursday of year, year from thursday
22667                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22668                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22669                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22670                     
22671                     fillMonths.cn.push({
22672                         tag: 'td',
22673                         cls: 'cw',
22674                         html: calWeek
22675                     });
22676                 }
22677             }
22678             
22679             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22680                 clsName += ' old';
22681             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22682                 clsName += ' new';
22683             }
22684             if (this.todayHighlight &&
22685                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22686                 prevMonth.getUTCMonth() == today.getMonth() &&
22687                 prevMonth.getUTCDate() == today.getDate()) {
22688                 clsName += ' today';
22689             }
22690             
22691             if (currentDate && prevMonth.valueOf() === currentDate) {
22692                 clsName += ' active';
22693             }
22694             
22695             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22696                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22697                     clsName += ' disabled';
22698             }
22699             
22700             fillMonths.cn.push({
22701                 tag: 'td',
22702                 cls: 'day ' + clsName,
22703                 html: prevMonth.getDate()
22704             });
22705             
22706             prevMonth.setDate(prevMonth.getDate()+1);
22707         }
22708           
22709         var currentYear = this.date && this.date.getUTCFullYear();
22710         var currentMonth = this.date && this.date.getUTCMonth();
22711         
22712         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22713         
22714         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22715             v.removeClass('active');
22716             
22717             if(currentYear === year && k === currentMonth){
22718                 v.addClass('active');
22719             }
22720             
22721             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22722                 v.addClass('disabled');
22723             }
22724             
22725         });
22726         
22727         
22728         year = parseInt(year/10, 10) * 10;
22729         
22730         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22731         
22732         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22733         
22734         year -= 1;
22735         for (var i = -1; i < 11; i++) {
22736             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22737                 tag: 'span',
22738                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22739                 html: year
22740             });
22741             
22742             year += 1;
22743         }
22744     },
22745     
22746     showMode: function(dir) 
22747     {
22748         if (dir) {
22749             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22750         }
22751         
22752         Roo.each(this.picker().select('>div',true).elements, function(v){
22753             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22754             v.hide();
22755         });
22756         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22757     },
22758     
22759     place: function()
22760     {
22761         if(this.isInline) {
22762             return;
22763         }
22764         
22765         this.picker().removeClass(['bottom', 'top']);
22766         
22767         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22768             /*
22769              * place to the top of element!
22770              *
22771              */
22772             
22773             this.picker().addClass('top');
22774             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22775             
22776             return;
22777         }
22778         
22779         this.picker().addClass('bottom');
22780         
22781         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22782     },
22783     
22784     parseDate : function(value)
22785     {
22786         if(!value || value instanceof Date){
22787             return value;
22788         }
22789         var v = Date.parseDate(value, this.format);
22790         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22791             v = Date.parseDate(value, 'Y-m-d');
22792         }
22793         if(!v && this.altFormats){
22794             if(!this.altFormatsArray){
22795                 this.altFormatsArray = this.altFormats.split("|");
22796             }
22797             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22798                 v = Date.parseDate(value, this.altFormatsArray[i]);
22799             }
22800         }
22801         return v;
22802     },
22803     
22804     formatDate : function(date, fmt)
22805     {   
22806         return (!date || !(date instanceof Date)) ?
22807         date : date.dateFormat(fmt || this.format);
22808     },
22809     
22810     onFocus : function()
22811     {
22812         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22813         this.showPopup();
22814     },
22815     
22816     onBlur : function()
22817     {
22818         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22819         
22820         var d = this.inputEl().getValue();
22821         
22822         this.setValue(d);
22823                 
22824         this.hidePopup();
22825     },
22826     
22827     showPopup : function()
22828     {
22829         this.picker().show();
22830         this.update();
22831         this.place();
22832         
22833         this.fireEvent('showpopup', this, this.date);
22834     },
22835     
22836     hidePopup : function()
22837     {
22838         if(this.isInline) {
22839             return;
22840         }
22841         this.picker().hide();
22842         this.viewMode = this.startViewMode;
22843         this.showMode();
22844         
22845         this.fireEvent('hidepopup', this, this.date);
22846         
22847     },
22848     
22849     onMousedown: function(e)
22850     {
22851         e.stopPropagation();
22852         e.preventDefault();
22853     },
22854     
22855     keyup: function(e)
22856     {
22857         Roo.bootstrap.DateField.superclass.keyup.call(this);
22858         this.update();
22859     },
22860
22861     setValue: function(v)
22862     {
22863         if(this.fireEvent('beforeselect', this, v) !== false){
22864             var d = new Date(this.parseDate(v) ).clearTime();
22865         
22866             if(isNaN(d.getTime())){
22867                 this.date = this.viewDate = '';
22868                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22869                 return;
22870             }
22871
22872             v = this.formatDate(d);
22873
22874             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22875
22876             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22877
22878             this.update();
22879
22880             this.fireEvent('select', this, this.date);
22881         }
22882     },
22883     
22884     getValue: function()
22885     {
22886         return this.formatDate(this.date);
22887     },
22888     
22889     fireKey: function(e)
22890     {
22891         if (!this.picker().isVisible()){
22892             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22893                 this.showPopup();
22894             }
22895             return;
22896         }
22897         
22898         var dateChanged = false,
22899         dir, day, month,
22900         newDate, newViewDate;
22901         
22902         switch(e.keyCode){
22903             case 27: // escape
22904                 this.hidePopup();
22905                 e.preventDefault();
22906                 break;
22907             case 37: // left
22908             case 39: // right
22909                 if (!this.keyboardNavigation) {
22910                     break;
22911                 }
22912                 dir = e.keyCode == 37 ? -1 : 1;
22913                 
22914                 if (e.ctrlKey){
22915                     newDate = this.moveYear(this.date, dir);
22916                     newViewDate = this.moveYear(this.viewDate, dir);
22917                 } else if (e.shiftKey){
22918                     newDate = this.moveMonth(this.date, dir);
22919                     newViewDate = this.moveMonth(this.viewDate, dir);
22920                 } else {
22921                     newDate = new Date(this.date);
22922                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22923                     newViewDate = new Date(this.viewDate);
22924                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22925                 }
22926                 if (this.dateWithinRange(newDate)){
22927                     this.date = newDate;
22928                     this.viewDate = newViewDate;
22929                     this.setValue(this.formatDate(this.date));
22930 //                    this.update();
22931                     e.preventDefault();
22932                     dateChanged = true;
22933                 }
22934                 break;
22935             case 38: // up
22936             case 40: // down
22937                 if (!this.keyboardNavigation) {
22938                     break;
22939                 }
22940                 dir = e.keyCode == 38 ? -1 : 1;
22941                 if (e.ctrlKey){
22942                     newDate = this.moveYear(this.date, dir);
22943                     newViewDate = this.moveYear(this.viewDate, dir);
22944                 } else if (e.shiftKey){
22945                     newDate = this.moveMonth(this.date, dir);
22946                     newViewDate = this.moveMonth(this.viewDate, dir);
22947                 } else {
22948                     newDate = new Date(this.date);
22949                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22950                     newViewDate = new Date(this.viewDate);
22951                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22952                 }
22953                 if (this.dateWithinRange(newDate)){
22954                     this.date = newDate;
22955                     this.viewDate = newViewDate;
22956                     this.setValue(this.formatDate(this.date));
22957 //                    this.update();
22958                     e.preventDefault();
22959                     dateChanged = true;
22960                 }
22961                 break;
22962             case 13: // enter
22963                 this.setValue(this.formatDate(this.date));
22964                 this.hidePopup();
22965                 e.preventDefault();
22966                 break;
22967             case 9: // tab
22968                 this.setValue(this.formatDate(this.date));
22969                 this.hidePopup();
22970                 break;
22971             case 16: // shift
22972             case 17: // ctrl
22973             case 18: // alt
22974                 break;
22975             default :
22976                 this.hidePopup();
22977                 
22978         }
22979     },
22980     
22981     
22982     onClick: function(e) 
22983     {
22984         e.stopPropagation();
22985         e.preventDefault();
22986         
22987         var target = e.getTarget();
22988         
22989         if(target.nodeName.toLowerCase() === 'i'){
22990             target = Roo.get(target).dom.parentNode;
22991         }
22992         
22993         var nodeName = target.nodeName;
22994         var className = target.className;
22995         var html = target.innerHTML;
22996         //Roo.log(nodeName);
22997         
22998         switch(nodeName.toLowerCase()) {
22999             case 'th':
23000                 switch(className) {
23001                     case 'switch':
23002                         this.showMode(1);
23003                         break;
23004                     case 'prev':
23005                     case 'next':
23006                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23007                         switch(this.viewMode){
23008                                 case 0:
23009                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23010                                         break;
23011                                 case 1:
23012                                 case 2:
23013                                         this.viewDate = this.moveYear(this.viewDate, dir);
23014                                         break;
23015                         }
23016                         this.fill();
23017                         break;
23018                     case 'today':
23019                         var date = new Date();
23020                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23021 //                        this.fill()
23022                         this.setValue(this.formatDate(this.date));
23023                         
23024                         this.hidePopup();
23025                         break;
23026                 }
23027                 break;
23028             case 'span':
23029                 if (className.indexOf('disabled') < 0) {
23030                 if (!this.viewDate) {
23031                     this.viewDate = new Date();
23032                 }
23033                 this.viewDate.setUTCDate(1);
23034                     if (className.indexOf('month') > -1) {
23035                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23036                     } else {
23037                         var year = parseInt(html, 10) || 0;
23038                         this.viewDate.setUTCFullYear(year);
23039                         
23040                     }
23041                     
23042                     if(this.singleMode){
23043                         this.setValue(this.formatDate(this.viewDate));
23044                         this.hidePopup();
23045                         return;
23046                     }
23047                     
23048                     this.showMode(-1);
23049                     this.fill();
23050                 }
23051                 break;
23052                 
23053             case 'td':
23054                 //Roo.log(className);
23055                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23056                     var day = parseInt(html, 10) || 1;
23057                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23058                         month = (this.viewDate || new Date()).getUTCMonth();
23059
23060                     if (className.indexOf('old') > -1) {
23061                         if(month === 0 ){
23062                             month = 11;
23063                             year -= 1;
23064                         }else{
23065                             month -= 1;
23066                         }
23067                     } else if (className.indexOf('new') > -1) {
23068                         if (month == 11) {
23069                             month = 0;
23070                             year += 1;
23071                         } else {
23072                             month += 1;
23073                         }
23074                     }
23075                     //Roo.log([year,month,day]);
23076                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23077                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23078 //                    this.fill();
23079                     //Roo.log(this.formatDate(this.date));
23080                     this.setValue(this.formatDate(this.date));
23081                     this.hidePopup();
23082                 }
23083                 break;
23084         }
23085     },
23086     
23087     setStartDate: function(startDate)
23088     {
23089         this.startDate = startDate || -Infinity;
23090         if (this.startDate !== -Infinity) {
23091             this.startDate = this.parseDate(this.startDate);
23092         }
23093         this.update();
23094         this.updateNavArrows();
23095     },
23096
23097     setEndDate: function(endDate)
23098     {
23099         this.endDate = endDate || Infinity;
23100         if (this.endDate !== Infinity) {
23101             this.endDate = this.parseDate(this.endDate);
23102         }
23103         this.update();
23104         this.updateNavArrows();
23105     },
23106     
23107     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23108     {
23109         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23110         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23111             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23112         }
23113         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23114             return parseInt(d, 10);
23115         });
23116         this.update();
23117         this.updateNavArrows();
23118     },
23119     
23120     updateNavArrows: function() 
23121     {
23122         if(this.singleMode){
23123             return;
23124         }
23125         
23126         var d = new Date(this.viewDate),
23127         year = d.getUTCFullYear(),
23128         month = d.getUTCMonth();
23129         
23130         Roo.each(this.picker().select('.prev', true).elements, function(v){
23131             v.show();
23132             switch (this.viewMode) {
23133                 case 0:
23134
23135                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23136                         v.hide();
23137                     }
23138                     break;
23139                 case 1:
23140                 case 2:
23141                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23142                         v.hide();
23143                     }
23144                     break;
23145             }
23146         });
23147         
23148         Roo.each(this.picker().select('.next', true).elements, function(v){
23149             v.show();
23150             switch (this.viewMode) {
23151                 case 0:
23152
23153                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23154                         v.hide();
23155                     }
23156                     break;
23157                 case 1:
23158                 case 2:
23159                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23160                         v.hide();
23161                     }
23162                     break;
23163             }
23164         })
23165     },
23166     
23167     moveMonth: function(date, dir)
23168     {
23169         if (!dir) {
23170             return date;
23171         }
23172         var new_date = new Date(date.valueOf()),
23173         day = new_date.getUTCDate(),
23174         month = new_date.getUTCMonth(),
23175         mag = Math.abs(dir),
23176         new_month, test;
23177         dir = dir > 0 ? 1 : -1;
23178         if (mag == 1){
23179             test = dir == -1
23180             // If going back one month, make sure month is not current month
23181             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23182             ? function(){
23183                 return new_date.getUTCMonth() == month;
23184             }
23185             // If going forward one month, make sure month is as expected
23186             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23187             : function(){
23188                 return new_date.getUTCMonth() != new_month;
23189             };
23190             new_month = month + dir;
23191             new_date.setUTCMonth(new_month);
23192             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23193             if (new_month < 0 || new_month > 11) {
23194                 new_month = (new_month + 12) % 12;
23195             }
23196         } else {
23197             // For magnitudes >1, move one month at a time...
23198             for (var i=0; i<mag; i++) {
23199                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23200                 new_date = this.moveMonth(new_date, dir);
23201             }
23202             // ...then reset the day, keeping it in the new month
23203             new_month = new_date.getUTCMonth();
23204             new_date.setUTCDate(day);
23205             test = function(){
23206                 return new_month != new_date.getUTCMonth();
23207             };
23208         }
23209         // Common date-resetting loop -- if date is beyond end of month, make it
23210         // end of month
23211         while (test()){
23212             new_date.setUTCDate(--day);
23213             new_date.setUTCMonth(new_month);
23214         }
23215         return new_date;
23216     },
23217
23218     moveYear: function(date, dir)
23219     {
23220         return this.moveMonth(date, dir*12);
23221     },
23222
23223     dateWithinRange: function(date)
23224     {
23225         return date >= this.startDate && date <= this.endDate;
23226     },
23227
23228     
23229     remove: function() 
23230     {
23231         this.picker().remove();
23232     },
23233     
23234     validateValue : function(value)
23235     {
23236         if(this.getVisibilityEl().hasClass('hidden')){
23237             return true;
23238         }
23239         
23240         if(value.length < 1)  {
23241             if(this.allowBlank){
23242                 return true;
23243             }
23244             return false;
23245         }
23246         
23247         if(value.length < this.minLength){
23248             return false;
23249         }
23250         if(value.length > this.maxLength){
23251             return false;
23252         }
23253         if(this.vtype){
23254             var vt = Roo.form.VTypes;
23255             if(!vt[this.vtype](value, this)){
23256                 return false;
23257             }
23258         }
23259         if(typeof this.validator == "function"){
23260             var msg = this.validator(value);
23261             if(msg !== true){
23262                 return false;
23263             }
23264         }
23265         
23266         if(this.regex && !this.regex.test(value)){
23267             return false;
23268         }
23269         
23270         if(typeof(this.parseDate(value)) == 'undefined'){
23271             return false;
23272         }
23273         
23274         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23275             return false;
23276         }      
23277         
23278         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23279             return false;
23280         } 
23281         
23282         
23283         return true;
23284     },
23285     
23286     reset : function()
23287     {
23288         this.date = this.viewDate = '';
23289         
23290         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23291     }
23292    
23293 });
23294
23295 Roo.apply(Roo.bootstrap.DateField,  {
23296     
23297     head : {
23298         tag: 'thead',
23299         cn: [
23300         {
23301             tag: 'tr',
23302             cn: [
23303             {
23304                 tag: 'th',
23305                 cls: 'prev',
23306                 html: '<i class="fa fa-arrow-left"/>'
23307             },
23308             {
23309                 tag: 'th',
23310                 cls: 'switch',
23311                 colspan: '5'
23312             },
23313             {
23314                 tag: 'th',
23315                 cls: 'next',
23316                 html: '<i class="fa fa-arrow-right"/>'
23317             }
23318
23319             ]
23320         }
23321         ]
23322     },
23323     
23324     content : {
23325         tag: 'tbody',
23326         cn: [
23327         {
23328             tag: 'tr',
23329             cn: [
23330             {
23331                 tag: 'td',
23332                 colspan: '7'
23333             }
23334             ]
23335         }
23336         ]
23337     },
23338     
23339     footer : {
23340         tag: 'tfoot',
23341         cn: [
23342         {
23343             tag: 'tr',
23344             cn: [
23345             {
23346                 tag: 'th',
23347                 colspan: '7',
23348                 cls: 'today'
23349             }
23350                     
23351             ]
23352         }
23353         ]
23354     },
23355     
23356     dates:{
23357         en: {
23358             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23359             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23360             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23361             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23362             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23363             today: "Today"
23364         }
23365     },
23366     
23367     modes: [
23368     {
23369         clsName: 'days',
23370         navFnc: 'Month',
23371         navStep: 1
23372     },
23373     {
23374         clsName: 'months',
23375         navFnc: 'FullYear',
23376         navStep: 1
23377     },
23378     {
23379         clsName: 'years',
23380         navFnc: 'FullYear',
23381         navStep: 10
23382     }]
23383 });
23384
23385 Roo.apply(Roo.bootstrap.DateField,  {
23386   
23387     template : {
23388         tag: 'div',
23389         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23390         cn: [
23391         {
23392             tag: 'div',
23393             cls: 'datepicker-days',
23394             cn: [
23395             {
23396                 tag: 'table',
23397                 cls: 'table-condensed',
23398                 cn:[
23399                 Roo.bootstrap.DateField.head,
23400                 {
23401                     tag: 'tbody'
23402                 },
23403                 Roo.bootstrap.DateField.footer
23404                 ]
23405             }
23406             ]
23407         },
23408         {
23409             tag: 'div',
23410             cls: 'datepicker-months',
23411             cn: [
23412             {
23413                 tag: 'table',
23414                 cls: 'table-condensed',
23415                 cn:[
23416                 Roo.bootstrap.DateField.head,
23417                 Roo.bootstrap.DateField.content,
23418                 Roo.bootstrap.DateField.footer
23419                 ]
23420             }
23421             ]
23422         },
23423         {
23424             tag: 'div',
23425             cls: 'datepicker-years',
23426             cn: [
23427             {
23428                 tag: 'table',
23429                 cls: 'table-condensed',
23430                 cn:[
23431                 Roo.bootstrap.DateField.head,
23432                 Roo.bootstrap.DateField.content,
23433                 Roo.bootstrap.DateField.footer
23434                 ]
23435             }
23436             ]
23437         }
23438         ]
23439     }
23440 });
23441
23442  
23443
23444  /*
23445  * - LGPL
23446  *
23447  * TimeField
23448  * 
23449  */
23450
23451 /**
23452  * @class Roo.bootstrap.TimeField
23453  * @extends Roo.bootstrap.Input
23454  * Bootstrap DateField class
23455  * 
23456  * 
23457  * @constructor
23458  * Create a new TimeField
23459  * @param {Object} config The config object
23460  */
23461
23462 Roo.bootstrap.TimeField = function(config){
23463     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23464     this.addEvents({
23465             /**
23466              * @event show
23467              * Fires when this field show.
23468              * @param {Roo.bootstrap.DateField} thisthis
23469              * @param {Mixed} date The date value
23470              */
23471             show : true,
23472             /**
23473              * @event show
23474              * Fires when this field hide.
23475              * @param {Roo.bootstrap.DateField} this
23476              * @param {Mixed} date The date value
23477              */
23478             hide : true,
23479             /**
23480              * @event select
23481              * Fires when select a date.
23482              * @param {Roo.bootstrap.DateField} this
23483              * @param {Mixed} date The date value
23484              */
23485             select : true
23486         });
23487 };
23488
23489 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23490     
23491     /**
23492      * @cfg {String} format
23493      * The default time format string which can be overriden for localization support.  The format must be
23494      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23495      */
23496     format : "H:i",
23497
23498     getAutoCreate : function()
23499     {
23500         this.after = '<i class="fa far fa-clock"></i>';
23501         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23502         
23503          
23504     },
23505     onRender: function(ct, position)
23506     {
23507         
23508         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23509                 
23510         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23511         
23512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23513         
23514         this.pop = this.picker().select('>.datepicker-time',true).first();
23515         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23516         
23517         this.picker().on('mousedown', this.onMousedown, this);
23518         this.picker().on('click', this.onClick, this);
23519         
23520         this.picker().addClass('datepicker-dropdown');
23521     
23522         this.fillTime();
23523         this.update();
23524             
23525         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23526         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23527         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23528         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23529         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23530         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23531
23532     },
23533     
23534     fireKey: function(e){
23535         if (!this.picker().isVisible()){
23536             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23537                 this.show();
23538             }
23539             return;
23540         }
23541
23542         e.preventDefault();
23543         
23544         switch(e.keyCode){
23545             case 27: // escape
23546                 this.hide();
23547                 break;
23548             case 37: // left
23549             case 39: // right
23550                 this.onTogglePeriod();
23551                 break;
23552             case 38: // up
23553                 this.onIncrementMinutes();
23554                 break;
23555             case 40: // down
23556                 this.onDecrementMinutes();
23557                 break;
23558             case 13: // enter
23559             case 9: // tab
23560                 this.setTime();
23561                 break;
23562         }
23563     },
23564     
23565     onClick: function(e) {
23566         e.stopPropagation();
23567         e.preventDefault();
23568     },
23569     
23570     picker : function()
23571     {
23572         return this.pickerEl;
23573     },
23574     
23575     fillTime: function()
23576     {    
23577         var time = this.pop.select('tbody', true).first();
23578         
23579         time.dom.innerHTML = '';
23580         
23581         time.createChild({
23582             tag: 'tr',
23583             cn: [
23584                 {
23585                     tag: 'td',
23586                     cn: [
23587                         {
23588                             tag: 'a',
23589                             href: '#',
23590                             cls: 'btn',
23591                             cn: [
23592                                 {
23593                                     tag: 'i',
23594                                     cls: 'hours-up fa fas fa-chevron-up'
23595                                 }
23596                             ]
23597                         } 
23598                     ]
23599                 },
23600                 {
23601                     tag: 'td',
23602                     cls: 'separator'
23603                 },
23604                 {
23605                     tag: 'td',
23606                     cn: [
23607                         {
23608                             tag: 'a',
23609                             href: '#',
23610                             cls: 'btn',
23611                             cn: [
23612                                 {
23613                                     tag: 'i',
23614                                     cls: 'minutes-up fa fas fa-chevron-up'
23615                                 }
23616                             ]
23617                         }
23618                     ]
23619                 },
23620                 {
23621                     tag: 'td',
23622                     cls: 'separator'
23623                 }
23624             ]
23625         });
23626         
23627         time.createChild({
23628             tag: 'tr',
23629             cn: [
23630                 {
23631                     tag: 'td',
23632                     cn: [
23633                         {
23634                             tag: 'span',
23635                             cls: 'timepicker-hour',
23636                             html: '00'
23637                         }  
23638                     ]
23639                 },
23640                 {
23641                     tag: 'td',
23642                     cls: 'separator',
23643                     html: ':'
23644                 },
23645                 {
23646                     tag: 'td',
23647                     cn: [
23648                         {
23649                             tag: 'span',
23650                             cls: 'timepicker-minute',
23651                             html: '00'
23652                         }  
23653                     ]
23654                 },
23655                 {
23656                     tag: 'td',
23657                     cls: 'separator'
23658                 },
23659                 {
23660                     tag: 'td',
23661                     cn: [
23662                         {
23663                             tag: 'button',
23664                             type: 'button',
23665                             cls: 'btn btn-primary period',
23666                             html: 'AM'
23667                             
23668                         }
23669                     ]
23670                 }
23671             ]
23672         });
23673         
23674         time.createChild({
23675             tag: 'tr',
23676             cn: [
23677                 {
23678                     tag: 'td',
23679                     cn: [
23680                         {
23681                             tag: 'a',
23682                             href: '#',
23683                             cls: 'btn',
23684                             cn: [
23685                                 {
23686                                     tag: 'span',
23687                                     cls: 'hours-down fa fas fa-chevron-down'
23688                                 }
23689                             ]
23690                         }
23691                     ]
23692                 },
23693                 {
23694                     tag: 'td',
23695                     cls: 'separator'
23696                 },
23697                 {
23698                     tag: 'td',
23699                     cn: [
23700                         {
23701                             tag: 'a',
23702                             href: '#',
23703                             cls: 'btn',
23704                             cn: [
23705                                 {
23706                                     tag: 'span',
23707                                     cls: 'minutes-down fa fas fa-chevron-down'
23708                                 }
23709                             ]
23710                         }
23711                     ]
23712                 },
23713                 {
23714                     tag: 'td',
23715                     cls: 'separator'
23716                 }
23717             ]
23718         });
23719         
23720     },
23721     
23722     update: function()
23723     {
23724         
23725         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23726         
23727         this.fill();
23728     },
23729     
23730     fill: function() 
23731     {
23732         var hours = this.time.getHours();
23733         var minutes = this.time.getMinutes();
23734         var period = 'AM';
23735         
23736         if(hours > 11){
23737             period = 'PM';
23738         }
23739         
23740         if(hours == 0){
23741             hours = 12;
23742         }
23743         
23744         
23745         if(hours > 12){
23746             hours = hours - 12;
23747         }
23748         
23749         if(hours < 10){
23750             hours = '0' + hours;
23751         }
23752         
23753         if(minutes < 10){
23754             minutes = '0' + minutes;
23755         }
23756         
23757         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23758         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23759         this.pop.select('button', true).first().dom.innerHTML = period;
23760         
23761     },
23762     
23763     place: function()
23764     {   
23765         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23766         
23767         var cls = ['bottom'];
23768         
23769         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23770             cls.pop();
23771             cls.push('top');
23772         }
23773         
23774         cls.push('right');
23775         
23776         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23777             cls.pop();
23778             cls.push('left');
23779         }
23780         //this.picker().setXY(20000,20000);
23781         this.picker().addClass(cls.join('-'));
23782         
23783         var _this = this;
23784         
23785         Roo.each(cls, function(c){
23786             if(c == 'bottom'){
23787                 (function() {
23788                  //  
23789                 }).defer(200);
23790                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23791                 //_this.picker().setTop(_this.inputEl().getHeight());
23792                 return;
23793             }
23794             if(c == 'top'){
23795                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23796                 
23797                 //_this.picker().setTop(0 - _this.picker().getHeight());
23798                 return;
23799             }
23800             /*
23801             if(c == 'left'){
23802                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23803                 return;
23804             }
23805             if(c == 'right'){
23806                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23807                 return;
23808             }
23809             */
23810         });
23811         
23812     },
23813   
23814     onFocus : function()
23815     {
23816         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23817         this.show();
23818     },
23819     
23820     onBlur : function()
23821     {
23822         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23823         this.hide();
23824     },
23825     
23826     show : function()
23827     {
23828         this.picker().show();
23829         this.pop.show();
23830         this.update();
23831         this.place();
23832         
23833         this.fireEvent('show', this, this.date);
23834     },
23835     
23836     hide : function()
23837     {
23838         this.picker().hide();
23839         this.pop.hide();
23840         
23841         this.fireEvent('hide', this, this.date);
23842     },
23843     
23844     setTime : function()
23845     {
23846         this.hide();
23847         this.setValue(this.time.format(this.format));
23848         
23849         this.fireEvent('select', this, this.date);
23850         
23851         
23852     },
23853     
23854     onMousedown: function(e){
23855         e.stopPropagation();
23856         e.preventDefault();
23857     },
23858     
23859     onIncrementHours: function()
23860     {
23861         Roo.log('onIncrementHours');
23862         this.time = this.time.add(Date.HOUR, 1);
23863         this.update();
23864         
23865     },
23866     
23867     onDecrementHours: function()
23868     {
23869         Roo.log('onDecrementHours');
23870         this.time = this.time.add(Date.HOUR, -1);
23871         this.update();
23872     },
23873     
23874     onIncrementMinutes: function()
23875     {
23876         Roo.log('onIncrementMinutes');
23877         this.time = this.time.add(Date.MINUTE, 1);
23878         this.update();
23879     },
23880     
23881     onDecrementMinutes: function()
23882     {
23883         Roo.log('onDecrementMinutes');
23884         this.time = this.time.add(Date.MINUTE, -1);
23885         this.update();
23886     },
23887     
23888     onTogglePeriod: function()
23889     {
23890         Roo.log('onTogglePeriod');
23891         this.time = this.time.add(Date.HOUR, 12);
23892         this.update();
23893     }
23894     
23895    
23896 });
23897  
23898
23899 Roo.apply(Roo.bootstrap.TimeField,  {
23900   
23901     template : {
23902         tag: 'div',
23903         cls: 'datepicker dropdown-menu',
23904         cn: [
23905             {
23906                 tag: 'div',
23907                 cls: 'datepicker-time',
23908                 cn: [
23909                 {
23910                     tag: 'table',
23911                     cls: 'table-condensed',
23912                     cn:[
23913                         {
23914                             tag: 'tbody',
23915                             cn: [
23916                                 {
23917                                     tag: 'tr',
23918                                     cn: [
23919                                     {
23920                                         tag: 'td',
23921                                         colspan: '7'
23922                                     }
23923                                     ]
23924                                 }
23925                             ]
23926                         },
23927                         {
23928                             tag: 'tfoot',
23929                             cn: [
23930                                 {
23931                                     tag: 'tr',
23932                                     cn: [
23933                                     {
23934                                         tag: 'th',
23935                                         colspan: '7',
23936                                         cls: '',
23937                                         cn: [
23938                                             {
23939                                                 tag: 'button',
23940                                                 cls: 'btn btn-info ok',
23941                                                 html: 'OK'
23942                                             }
23943                                         ]
23944                                     }
23945                     
23946                                     ]
23947                                 }
23948                             ]
23949                         }
23950                     ]
23951                 }
23952                 ]
23953             }
23954         ]
23955     }
23956 });
23957
23958  
23959
23960  /*
23961  * - LGPL
23962  *
23963  * MonthField
23964  * 
23965  */
23966
23967 /**
23968  * @class Roo.bootstrap.MonthField
23969  * @extends Roo.bootstrap.Input
23970  * Bootstrap MonthField class
23971  * 
23972  * @cfg {String} language default en
23973  * 
23974  * @constructor
23975  * Create a new MonthField
23976  * @param {Object} config The config object
23977  */
23978
23979 Roo.bootstrap.MonthField = function(config){
23980     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23981     
23982     this.addEvents({
23983         /**
23984          * @event show
23985          * Fires when this field show.
23986          * @param {Roo.bootstrap.MonthField} this
23987          * @param {Mixed} date The date value
23988          */
23989         show : true,
23990         /**
23991          * @event show
23992          * Fires when this field hide.
23993          * @param {Roo.bootstrap.MonthField} this
23994          * @param {Mixed} date The date value
23995          */
23996         hide : true,
23997         /**
23998          * @event select
23999          * Fires when select a date.
24000          * @param {Roo.bootstrap.MonthField} this
24001          * @param {String} oldvalue The old value
24002          * @param {String} newvalue The new value
24003          */
24004         select : true
24005     });
24006 };
24007
24008 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24009     
24010     onRender: function(ct, position)
24011     {
24012         
24013         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24014         
24015         this.language = this.language || 'en';
24016         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24017         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24018         
24019         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24020         this.isInline = false;
24021         this.isInput = true;
24022         this.component = this.el.select('.add-on', true).first() || false;
24023         this.component = (this.component && this.component.length === 0) ? false : this.component;
24024         this.hasInput = this.component && this.inputEL().length;
24025         
24026         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24027         
24028         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24029         
24030         this.picker().on('mousedown', this.onMousedown, this);
24031         this.picker().on('click', this.onClick, this);
24032         
24033         this.picker().addClass('datepicker-dropdown');
24034         
24035         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24036             v.setStyle('width', '189px');
24037         });
24038         
24039         this.fillMonths();
24040         
24041         this.update();
24042         
24043         if(this.isInline) {
24044             this.show();
24045         }
24046         
24047     },
24048     
24049     setValue: function(v, suppressEvent)
24050     {   
24051         var o = this.getValue();
24052         
24053         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24054         
24055         this.update();
24056
24057         if(suppressEvent !== true){
24058             this.fireEvent('select', this, o, v);
24059         }
24060         
24061     },
24062     
24063     getValue: function()
24064     {
24065         return this.value;
24066     },
24067     
24068     onClick: function(e) 
24069     {
24070         e.stopPropagation();
24071         e.preventDefault();
24072         
24073         var target = e.getTarget();
24074         
24075         if(target.nodeName.toLowerCase() === 'i'){
24076             target = Roo.get(target).dom.parentNode;
24077         }
24078         
24079         var nodeName = target.nodeName;
24080         var className = target.className;
24081         var html = target.innerHTML;
24082         
24083         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24084             return;
24085         }
24086         
24087         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24088         
24089         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24090         
24091         this.hide();
24092                         
24093     },
24094     
24095     picker : function()
24096     {
24097         return this.pickerEl;
24098     },
24099     
24100     fillMonths: function()
24101     {    
24102         var i = 0;
24103         var months = this.picker().select('>.datepicker-months td', true).first();
24104         
24105         months.dom.innerHTML = '';
24106         
24107         while (i < 12) {
24108             var month = {
24109                 tag: 'span',
24110                 cls: 'month',
24111                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24112             };
24113             
24114             months.createChild(month);
24115         }
24116         
24117     },
24118     
24119     update: function()
24120     {
24121         var _this = this;
24122         
24123         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24124             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24125         }
24126         
24127         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24128             e.removeClass('active');
24129             
24130             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24131                 e.addClass('active');
24132             }
24133         })
24134     },
24135     
24136     place: function()
24137     {
24138         if(this.isInline) {
24139             return;
24140         }
24141         
24142         this.picker().removeClass(['bottom', 'top']);
24143         
24144         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24145             /*
24146              * place to the top of element!
24147              *
24148              */
24149             
24150             this.picker().addClass('top');
24151             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24152             
24153             return;
24154         }
24155         
24156         this.picker().addClass('bottom');
24157         
24158         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24159     },
24160     
24161     onFocus : function()
24162     {
24163         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24164         this.show();
24165     },
24166     
24167     onBlur : function()
24168     {
24169         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24170         
24171         var d = this.inputEl().getValue();
24172         
24173         this.setValue(d);
24174                 
24175         this.hide();
24176     },
24177     
24178     show : function()
24179     {
24180         this.picker().show();
24181         this.picker().select('>.datepicker-months', true).first().show();
24182         this.update();
24183         this.place();
24184         
24185         this.fireEvent('show', this, this.date);
24186     },
24187     
24188     hide : function()
24189     {
24190         if(this.isInline) {
24191             return;
24192         }
24193         this.picker().hide();
24194         this.fireEvent('hide', this, this.date);
24195         
24196     },
24197     
24198     onMousedown: function(e)
24199     {
24200         e.stopPropagation();
24201         e.preventDefault();
24202     },
24203     
24204     keyup: function(e)
24205     {
24206         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24207         this.update();
24208     },
24209
24210     fireKey: function(e)
24211     {
24212         if (!this.picker().isVisible()){
24213             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24214                 this.show();
24215             }
24216             return;
24217         }
24218         
24219         var dir;
24220         
24221         switch(e.keyCode){
24222             case 27: // escape
24223                 this.hide();
24224                 e.preventDefault();
24225                 break;
24226             case 37: // left
24227             case 39: // right
24228                 dir = e.keyCode == 37 ? -1 : 1;
24229                 
24230                 this.vIndex = this.vIndex + dir;
24231                 
24232                 if(this.vIndex < 0){
24233                     this.vIndex = 0;
24234                 }
24235                 
24236                 if(this.vIndex > 11){
24237                     this.vIndex = 11;
24238                 }
24239                 
24240                 if(isNaN(this.vIndex)){
24241                     this.vIndex = 0;
24242                 }
24243                 
24244                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24245                 
24246                 break;
24247             case 38: // up
24248             case 40: // down
24249                 
24250                 dir = e.keyCode == 38 ? -1 : 1;
24251                 
24252                 this.vIndex = this.vIndex + dir * 4;
24253                 
24254                 if(this.vIndex < 0){
24255                     this.vIndex = 0;
24256                 }
24257                 
24258                 if(this.vIndex > 11){
24259                     this.vIndex = 11;
24260                 }
24261                 
24262                 if(isNaN(this.vIndex)){
24263                     this.vIndex = 0;
24264                 }
24265                 
24266                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24267                 break;
24268                 
24269             case 13: // enter
24270                 
24271                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24272                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24273                 }
24274                 
24275                 this.hide();
24276                 e.preventDefault();
24277                 break;
24278             case 9: // tab
24279                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24280                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24281                 }
24282                 this.hide();
24283                 break;
24284             case 16: // shift
24285             case 17: // ctrl
24286             case 18: // alt
24287                 break;
24288             default :
24289                 this.hide();
24290                 
24291         }
24292     },
24293     
24294     remove: function() 
24295     {
24296         this.picker().remove();
24297     }
24298    
24299 });
24300
24301 Roo.apply(Roo.bootstrap.MonthField,  {
24302     
24303     content : {
24304         tag: 'tbody',
24305         cn: [
24306         {
24307             tag: 'tr',
24308             cn: [
24309             {
24310                 tag: 'td',
24311                 colspan: '7'
24312             }
24313             ]
24314         }
24315         ]
24316     },
24317     
24318     dates:{
24319         en: {
24320             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24321             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24322         }
24323     }
24324 });
24325
24326 Roo.apply(Roo.bootstrap.MonthField,  {
24327   
24328     template : {
24329         tag: 'div',
24330         cls: 'datepicker dropdown-menu roo-dynamic',
24331         cn: [
24332             {
24333                 tag: 'div',
24334                 cls: 'datepicker-months',
24335                 cn: [
24336                 {
24337                     tag: 'table',
24338                     cls: 'table-condensed',
24339                     cn:[
24340                         Roo.bootstrap.DateField.content
24341                     ]
24342                 }
24343                 ]
24344             }
24345         ]
24346     }
24347 });
24348
24349  
24350
24351  
24352  /*
24353  * - LGPL
24354  *
24355  * CheckBox
24356  * 
24357  */
24358
24359 /**
24360  * @class Roo.bootstrap.CheckBox
24361  * @extends Roo.bootstrap.Input
24362  * Bootstrap CheckBox class
24363  * 
24364  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24365  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24366  * @cfg {String} boxLabel The text that appears beside the checkbox
24367  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24368  * @cfg {Boolean} checked initnal the element
24369  * @cfg {Boolean} inline inline the element (default false)
24370  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24371  * @cfg {String} tooltip label tooltip
24372  * 
24373  * @constructor
24374  * Create a new CheckBox
24375  * @param {Object} config The config object
24376  */
24377
24378 Roo.bootstrap.CheckBox = function(config){
24379     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24380    
24381     this.addEvents({
24382         /**
24383         * @event check
24384         * Fires when the element is checked or unchecked.
24385         * @param {Roo.bootstrap.CheckBox} this This input
24386         * @param {Boolean} checked The new checked value
24387         */
24388        check : true,
24389        /**
24390         * @event click
24391         * Fires when the element is click.
24392         * @param {Roo.bootstrap.CheckBox} this This input
24393         */
24394        click : true
24395     });
24396     
24397 };
24398
24399 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24400   
24401     inputType: 'checkbox',
24402     inputValue: 1,
24403     valueOff: 0,
24404     boxLabel: false,
24405     checked: false,
24406     weight : false,
24407     inline: false,
24408     tooltip : '',
24409     
24410     // checkbox success does not make any sense really.. 
24411     invalidClass : "",
24412     validClass : "",
24413     
24414     
24415     getAutoCreate : function()
24416     {
24417         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24418         
24419         var id = Roo.id();
24420         
24421         var cfg = {};
24422         
24423         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24424         
24425         if(this.inline){
24426             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24427         }
24428         
24429         var input =  {
24430             tag: 'input',
24431             id : id,
24432             type : this.inputType,
24433             value : this.inputValue,
24434             cls : 'roo-' + this.inputType, //'form-box',
24435             placeholder : this.placeholder || ''
24436             
24437         };
24438         
24439         if(this.inputType != 'radio'){
24440             var hidden =  {
24441                 tag: 'input',
24442                 type : 'hidden',
24443                 cls : 'roo-hidden-value',
24444                 value : this.checked ? this.inputValue : this.valueOff
24445             };
24446         }
24447         
24448             
24449         if (this.weight) { // Validity check?
24450             cfg.cls += " " + this.inputType + "-" + this.weight;
24451         }
24452         
24453         if (this.disabled) {
24454             input.disabled=true;
24455         }
24456         
24457         if(this.checked){
24458             input.checked = this.checked;
24459         }
24460         
24461         if (this.name) {
24462             
24463             input.name = this.name;
24464             
24465             if(this.inputType != 'radio'){
24466                 hidden.name = this.name;
24467                 input.name = '_hidden_' + this.name;
24468             }
24469         }
24470         
24471         if (this.size) {
24472             input.cls += ' input-' + this.size;
24473         }
24474         
24475         var settings=this;
24476         
24477         ['xs','sm','md','lg'].map(function(size){
24478             if (settings[size]) {
24479                 cfg.cls += ' col-' + size + '-' + settings[size];
24480             }
24481         });
24482         
24483         var inputblock = input;
24484          
24485         if (this.before || this.after) {
24486             
24487             inputblock = {
24488                 cls : 'input-group',
24489                 cn :  [] 
24490             };
24491             
24492             if (this.before) {
24493                 inputblock.cn.push({
24494                     tag :'span',
24495                     cls : 'input-group-addon',
24496                     html : this.before
24497                 });
24498             }
24499             
24500             inputblock.cn.push(input);
24501             
24502             if(this.inputType != 'radio'){
24503                 inputblock.cn.push(hidden);
24504             }
24505             
24506             if (this.after) {
24507                 inputblock.cn.push({
24508                     tag :'span',
24509                     cls : 'input-group-addon',
24510                     html : this.after
24511                 });
24512             }
24513             
24514         }
24515         var boxLabelCfg = false;
24516         
24517         if(this.boxLabel){
24518            
24519             boxLabelCfg = {
24520                 tag: 'label',
24521                 //'for': id, // box label is handled by onclick - so no for...
24522                 cls: 'box-label',
24523                 html: this.boxLabel
24524             };
24525             if(this.tooltip){
24526                 boxLabelCfg.tooltip = this.tooltip;
24527             }
24528              
24529         }
24530         
24531         
24532         if (align ==='left' && this.fieldLabel.length) {
24533 //                Roo.log("left and has label");
24534             cfg.cn = [
24535                 {
24536                     tag: 'label',
24537                     'for' :  id,
24538                     cls : 'control-label',
24539                     html : this.fieldLabel
24540                 },
24541                 {
24542                     cls : "", 
24543                     cn: [
24544                         inputblock
24545                     ]
24546                 }
24547             ];
24548             
24549             if (boxLabelCfg) {
24550                 cfg.cn[1].cn.push(boxLabelCfg);
24551             }
24552             
24553             if(this.labelWidth > 12){
24554                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24555             }
24556             
24557             if(this.labelWidth < 13 && this.labelmd == 0){
24558                 this.labelmd = this.labelWidth;
24559             }
24560             
24561             if(this.labellg > 0){
24562                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24563                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24564             }
24565             
24566             if(this.labelmd > 0){
24567                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24568                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24569             }
24570             
24571             if(this.labelsm > 0){
24572                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24573                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24574             }
24575             
24576             if(this.labelxs > 0){
24577                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24578                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24579             }
24580             
24581         } else if ( this.fieldLabel.length) {
24582 //                Roo.log(" label");
24583                 cfg.cn = [
24584                    
24585                     {
24586                         tag: this.boxLabel ? 'span' : 'label',
24587                         'for': id,
24588                         cls: 'control-label box-input-label',
24589                         //cls : 'input-group-addon',
24590                         html : this.fieldLabel
24591                     },
24592                     
24593                     inputblock
24594                     
24595                 ];
24596                 if (boxLabelCfg) {
24597                     cfg.cn.push(boxLabelCfg);
24598                 }
24599
24600         } else {
24601             
24602 //                Roo.log(" no label && no align");
24603                 cfg.cn = [  inputblock ] ;
24604                 if (boxLabelCfg) {
24605                     cfg.cn.push(boxLabelCfg);
24606                 }
24607
24608                 
24609         }
24610         
24611        
24612         
24613         if(this.inputType != 'radio'){
24614             cfg.cn.push(hidden);
24615         }
24616         
24617         return cfg;
24618         
24619     },
24620     
24621     /**
24622      * return the real input element.
24623      */
24624     inputEl: function ()
24625     {
24626         return this.el.select('input.roo-' + this.inputType,true).first();
24627     },
24628     hiddenEl: function ()
24629     {
24630         return this.el.select('input.roo-hidden-value',true).first();
24631     },
24632     
24633     labelEl: function()
24634     {
24635         return this.el.select('label.control-label',true).first();
24636     },
24637     /* depricated... */
24638     
24639     label: function()
24640     {
24641         return this.labelEl();
24642     },
24643     
24644     boxLabelEl: function()
24645     {
24646         return this.el.select('label.box-label',true).first();
24647     },
24648     
24649     initEvents : function()
24650     {
24651 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24652         
24653         this.inputEl().on('click', this.onClick,  this);
24654         
24655         if (this.boxLabel) { 
24656             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24657         }
24658         
24659         this.startValue = this.getValue();
24660         
24661         if(this.groupId){
24662             Roo.bootstrap.CheckBox.register(this);
24663         }
24664     },
24665     
24666     onClick : function(e)
24667     {   
24668         if(this.fireEvent('click', this, e) !== false){
24669             this.setChecked(!this.checked);
24670         }
24671         
24672     },
24673     
24674     setChecked : function(state,suppressEvent)
24675     {
24676         this.startValue = this.getValue();
24677
24678         if(this.inputType == 'radio'){
24679             
24680             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24681                 e.dom.checked = false;
24682             });
24683             
24684             this.inputEl().dom.checked = true;
24685             
24686             this.inputEl().dom.value = this.inputValue;
24687             
24688             if(suppressEvent !== true){
24689                 this.fireEvent('check', this, true);
24690             }
24691             
24692             this.validate();
24693             
24694             return;
24695         }
24696         
24697         this.checked = state;
24698         
24699         this.inputEl().dom.checked = state;
24700         
24701         
24702         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24703         
24704         if(suppressEvent !== true){
24705             this.fireEvent('check', this, state);
24706         }
24707         
24708         this.validate();
24709     },
24710     
24711     getValue : function()
24712     {
24713         if(this.inputType == 'radio'){
24714             return this.getGroupValue();
24715         }
24716         
24717         return this.hiddenEl().dom.value;
24718         
24719     },
24720     
24721     getGroupValue : function()
24722     {
24723         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24724             return '';
24725         }
24726         
24727         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24728     },
24729     
24730     setValue : function(v,suppressEvent)
24731     {
24732         if(this.inputType == 'radio'){
24733             this.setGroupValue(v, suppressEvent);
24734             return;
24735         }
24736         
24737         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24738         
24739         this.validate();
24740     },
24741     
24742     setGroupValue : function(v, suppressEvent)
24743     {
24744         this.startValue = this.getValue();
24745         
24746         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24747             e.dom.checked = false;
24748             
24749             if(e.dom.value == v){
24750                 e.dom.checked = true;
24751             }
24752         });
24753         
24754         if(suppressEvent !== true){
24755             this.fireEvent('check', this, true);
24756         }
24757
24758         this.validate();
24759         
24760         return;
24761     },
24762     
24763     validate : function()
24764     {
24765         if(this.getVisibilityEl().hasClass('hidden')){
24766             return true;
24767         }
24768         
24769         if(
24770                 this.disabled || 
24771                 (this.inputType == 'radio' && this.validateRadio()) ||
24772                 (this.inputType == 'checkbox' && this.validateCheckbox())
24773         ){
24774             this.markValid();
24775             return true;
24776         }
24777         
24778         this.markInvalid();
24779         return false;
24780     },
24781     
24782     validateRadio : function()
24783     {
24784         if(this.getVisibilityEl().hasClass('hidden')){
24785             return true;
24786         }
24787         
24788         if(this.allowBlank){
24789             return true;
24790         }
24791         
24792         var valid = false;
24793         
24794         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24795             if(!e.dom.checked){
24796                 return;
24797             }
24798             
24799             valid = true;
24800             
24801             return false;
24802         });
24803         
24804         return valid;
24805     },
24806     
24807     validateCheckbox : function()
24808     {
24809         if(!this.groupId){
24810             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24811             //return (this.getValue() == this.inputValue) ? true : false;
24812         }
24813         
24814         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24815         
24816         if(!group){
24817             return false;
24818         }
24819         
24820         var r = false;
24821         
24822         for(var i in group){
24823             if(group[i].el.isVisible(true)){
24824                 r = false;
24825                 break;
24826             }
24827             
24828             r = true;
24829         }
24830         
24831         for(var i in group){
24832             if(r){
24833                 break;
24834             }
24835             
24836             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24837         }
24838         
24839         return r;
24840     },
24841     
24842     /**
24843      * Mark this field as valid
24844      */
24845     markValid : function()
24846     {
24847         var _this = this;
24848         
24849         this.fireEvent('valid', this);
24850         
24851         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24852         
24853         if(this.groupId){
24854             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24855         }
24856         
24857         if(label){
24858             label.markValid();
24859         }
24860
24861         if(this.inputType == 'radio'){
24862             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24863                 var fg = e.findParent('.form-group', false, true);
24864                 if (Roo.bootstrap.version == 3) {
24865                     fg.removeClass([_this.invalidClass, _this.validClass]);
24866                     fg.addClass(_this.validClass);
24867                 } else {
24868                     fg.removeClass(['is-valid', 'is-invalid']);
24869                     fg.addClass('is-valid');
24870                 }
24871             });
24872             
24873             return;
24874         }
24875
24876         if(!this.groupId){
24877             var fg = this.el.findParent('.form-group', false, true);
24878             if (Roo.bootstrap.version == 3) {
24879                 fg.removeClass([this.invalidClass, this.validClass]);
24880                 fg.addClass(this.validClass);
24881             } else {
24882                 fg.removeClass(['is-valid', 'is-invalid']);
24883                 fg.addClass('is-valid');
24884             }
24885             return;
24886         }
24887         
24888         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24889         
24890         if(!group){
24891             return;
24892         }
24893         
24894         for(var i in group){
24895             var fg = group[i].el.findParent('.form-group', false, true);
24896             if (Roo.bootstrap.version == 3) {
24897                 fg.removeClass([this.invalidClass, this.validClass]);
24898                 fg.addClass(this.validClass);
24899             } else {
24900                 fg.removeClass(['is-valid', 'is-invalid']);
24901                 fg.addClass('is-valid');
24902             }
24903         }
24904     },
24905     
24906      /**
24907      * Mark this field as invalid
24908      * @param {String} msg The validation message
24909      */
24910     markInvalid : function(msg)
24911     {
24912         if(this.allowBlank){
24913             return;
24914         }
24915         
24916         var _this = this;
24917         
24918         this.fireEvent('invalid', this, msg);
24919         
24920         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24921         
24922         if(this.groupId){
24923             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24924         }
24925         
24926         if(label){
24927             label.markInvalid();
24928         }
24929             
24930         if(this.inputType == 'radio'){
24931             
24932             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24933                 var fg = e.findParent('.form-group', false, true);
24934                 if (Roo.bootstrap.version == 3) {
24935                     fg.removeClass([_this.invalidClass, _this.validClass]);
24936                     fg.addClass(_this.invalidClass);
24937                 } else {
24938                     fg.removeClass(['is-invalid', 'is-valid']);
24939                     fg.addClass('is-invalid');
24940                 }
24941             });
24942             
24943             return;
24944         }
24945         
24946         if(!this.groupId){
24947             var fg = this.el.findParent('.form-group', false, true);
24948             if (Roo.bootstrap.version == 3) {
24949                 fg.removeClass([_this.invalidClass, _this.validClass]);
24950                 fg.addClass(_this.invalidClass);
24951             } else {
24952                 fg.removeClass(['is-invalid', 'is-valid']);
24953                 fg.addClass('is-invalid');
24954             }
24955             return;
24956         }
24957         
24958         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24959         
24960         if(!group){
24961             return;
24962         }
24963         
24964         for(var i in group){
24965             var fg = group[i].el.findParent('.form-group', false, true);
24966             if (Roo.bootstrap.version == 3) {
24967                 fg.removeClass([_this.invalidClass, _this.validClass]);
24968                 fg.addClass(_this.invalidClass);
24969             } else {
24970                 fg.removeClass(['is-invalid', 'is-valid']);
24971                 fg.addClass('is-invalid');
24972             }
24973         }
24974         
24975     },
24976     
24977     clearInvalid : function()
24978     {
24979         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24980         
24981         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24982         
24983         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24984         
24985         if (label && label.iconEl) {
24986             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24987             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24988         }
24989     },
24990     
24991     disable : function()
24992     {
24993         if(this.inputType != 'radio'){
24994             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24995             return;
24996         }
24997         
24998         var _this = this;
24999         
25000         if(this.rendered){
25001             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25002                 _this.getActionEl().addClass(this.disabledClass);
25003                 e.dom.disabled = true;
25004             });
25005         }
25006         
25007         this.disabled = true;
25008         this.fireEvent("disable", this);
25009         return this;
25010     },
25011
25012     enable : function()
25013     {
25014         if(this.inputType != 'radio'){
25015             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25016             return;
25017         }
25018         
25019         var _this = this;
25020         
25021         if(this.rendered){
25022             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25023                 _this.getActionEl().removeClass(this.disabledClass);
25024                 e.dom.disabled = false;
25025             });
25026         }
25027         
25028         this.disabled = false;
25029         this.fireEvent("enable", this);
25030         return this;
25031     },
25032     
25033     setBoxLabel : function(v)
25034     {
25035         this.boxLabel = v;
25036         
25037         if(this.rendered){
25038             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25039         }
25040     }
25041
25042 });
25043
25044 Roo.apply(Roo.bootstrap.CheckBox, {
25045     
25046     groups: {},
25047     
25048      /**
25049     * register a CheckBox Group
25050     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25051     */
25052     register : function(checkbox)
25053     {
25054         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25055             this.groups[checkbox.groupId] = {};
25056         }
25057         
25058         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25059             return;
25060         }
25061         
25062         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25063         
25064     },
25065     /**
25066     * fetch a CheckBox Group based on the group ID
25067     * @param {string} the group ID
25068     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25069     */
25070     get: function(groupId) {
25071         if (typeof(this.groups[groupId]) == 'undefined') {
25072             return false;
25073         }
25074         
25075         return this.groups[groupId] ;
25076     }
25077     
25078     
25079 });
25080 /*
25081  * - LGPL
25082  *
25083  * RadioItem
25084  * 
25085  */
25086
25087 /**
25088  * @class Roo.bootstrap.Radio
25089  * @extends Roo.bootstrap.Component
25090  * Bootstrap Radio class
25091  * @cfg {String} boxLabel - the label associated
25092  * @cfg {String} value - the value of radio
25093  * 
25094  * @constructor
25095  * Create a new Radio
25096  * @param {Object} config The config object
25097  */
25098 Roo.bootstrap.Radio = function(config){
25099     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25100     
25101 };
25102
25103 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25104     
25105     boxLabel : '',
25106     
25107     value : '',
25108     
25109     getAutoCreate : function()
25110     {
25111         var cfg = {
25112             tag : 'div',
25113             cls : 'form-group radio',
25114             cn : [
25115                 {
25116                     tag : 'label',
25117                     cls : 'box-label',
25118                     html : this.boxLabel
25119                 }
25120             ]
25121         };
25122         
25123         return cfg;
25124     },
25125     
25126     initEvents : function() 
25127     {
25128         this.parent().register(this);
25129         
25130         this.el.on('click', this.onClick, this);
25131         
25132     },
25133     
25134     onClick : function(e)
25135     {
25136         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25137             this.setChecked(true);
25138         }
25139     },
25140     
25141     setChecked : function(state, suppressEvent)
25142     {
25143         this.parent().setValue(this.value, suppressEvent);
25144         
25145     },
25146     
25147     setBoxLabel : function(v)
25148     {
25149         this.boxLabel = v;
25150         
25151         if(this.rendered){
25152             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25153         }
25154     }
25155     
25156 });
25157  
25158
25159  /*
25160  * - LGPL
25161  *
25162  * Input
25163  * 
25164  */
25165
25166 /**
25167  * @class Roo.bootstrap.SecurePass
25168  * @extends Roo.bootstrap.Input
25169  * Bootstrap SecurePass class
25170  *
25171  * 
25172  * @constructor
25173  * Create a new SecurePass
25174  * @param {Object} config The config object
25175  */
25176  
25177 Roo.bootstrap.SecurePass = function (config) {
25178     // these go here, so the translation tool can replace them..
25179     this.errors = {
25180         PwdEmpty: "Please type a password, and then retype it to confirm.",
25181         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25182         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25183         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25184         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25185         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25186         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25187         TooWeak: "Your password is Too Weak."
25188     },
25189     this.meterLabel = "Password strength:";
25190     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25191     this.meterClass = [
25192         "roo-password-meter-tooweak", 
25193         "roo-password-meter-weak", 
25194         "roo-password-meter-medium", 
25195         "roo-password-meter-strong", 
25196         "roo-password-meter-grey"
25197     ];
25198     
25199     this.errors = {};
25200     
25201     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25202 }
25203
25204 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25205     /**
25206      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25207      * {
25208      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25209      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25210      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25211      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25212      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25213      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25214      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25215      * })
25216      */
25217     // private
25218     
25219     meterWidth: 300,
25220     errorMsg :'',    
25221     errors: false,
25222     imageRoot: '/',
25223     /**
25224      * @cfg {String/Object} Label for the strength meter (defaults to
25225      * 'Password strength:')
25226      */
25227     // private
25228     meterLabel: '',
25229     /**
25230      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25231      * ['Weak', 'Medium', 'Strong'])
25232      */
25233     // private    
25234     pwdStrengths: false,    
25235     // private
25236     strength: 0,
25237     // private
25238     _lastPwd: null,
25239     // private
25240     kCapitalLetter: 0,
25241     kSmallLetter: 1,
25242     kDigit: 2,
25243     kPunctuation: 3,
25244     
25245     insecure: false,
25246     // private
25247     initEvents: function ()
25248     {
25249         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25250
25251         if (this.el.is('input[type=password]') && Roo.isSafari) {
25252             this.el.on('keydown', this.SafariOnKeyDown, this);
25253         }
25254
25255         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25256     },
25257     // private
25258     onRender: function (ct, position)
25259     {
25260         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25261         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25262         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25263
25264         this.trigger.createChild({
25265                    cn: [
25266                     {
25267                     //id: 'PwdMeter',
25268                     tag: 'div',
25269                     cls: 'roo-password-meter-grey col-xs-12',
25270                     style: {
25271                         //width: 0,
25272                         //width: this.meterWidth + 'px'                                                
25273                         }
25274                     },
25275                     {                            
25276                          cls: 'roo-password-meter-text'                          
25277                     }
25278                 ]            
25279         });
25280
25281          
25282         if (this.hideTrigger) {
25283             this.trigger.setDisplayed(false);
25284         }
25285         this.setSize(this.width || '', this.height || '');
25286     },
25287     // private
25288     onDestroy: function ()
25289     {
25290         if (this.trigger) {
25291             this.trigger.removeAllListeners();
25292             this.trigger.remove();
25293         }
25294         if (this.wrap) {
25295             this.wrap.remove();
25296         }
25297         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25298     },
25299     // private
25300     checkStrength: function ()
25301     {
25302         var pwd = this.inputEl().getValue();
25303         if (pwd == this._lastPwd) {
25304             return;
25305         }
25306
25307         var strength;
25308         if (this.ClientSideStrongPassword(pwd)) {
25309             strength = 3;
25310         } else if (this.ClientSideMediumPassword(pwd)) {
25311             strength = 2;
25312         } else if (this.ClientSideWeakPassword(pwd)) {
25313             strength = 1;
25314         } else {
25315             strength = 0;
25316         }
25317         
25318         Roo.log('strength1: ' + strength);
25319         
25320         //var pm = this.trigger.child('div/div/div').dom;
25321         var pm = this.trigger.child('div/div');
25322         pm.removeClass(this.meterClass);
25323         pm.addClass(this.meterClass[strength]);
25324                 
25325         
25326         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25327                 
25328         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25329         
25330         this._lastPwd = pwd;
25331     },
25332     reset: function ()
25333     {
25334         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25335         
25336         this._lastPwd = '';
25337         
25338         var pm = this.trigger.child('div/div');
25339         pm.removeClass(this.meterClass);
25340         pm.addClass('roo-password-meter-grey');        
25341         
25342         
25343         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25344         
25345         pt.innerHTML = '';
25346         this.inputEl().dom.type='password';
25347     },
25348     // private
25349     validateValue: function (value)
25350     {
25351         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25352             return false;
25353         }
25354         if (value.length == 0) {
25355             if (this.allowBlank) {
25356                 this.clearInvalid();
25357                 return true;
25358             }
25359
25360             this.markInvalid(this.errors.PwdEmpty);
25361             this.errorMsg = this.errors.PwdEmpty;
25362             return false;
25363         }
25364         
25365         if(this.insecure){
25366             return true;
25367         }
25368         
25369         if (!value.match(/[\x21-\x7e]+/)) {
25370             this.markInvalid(this.errors.PwdBadChar);
25371             this.errorMsg = this.errors.PwdBadChar;
25372             return false;
25373         }
25374         if (value.length < 6) {
25375             this.markInvalid(this.errors.PwdShort);
25376             this.errorMsg = this.errors.PwdShort;
25377             return false;
25378         }
25379         if (value.length > 16) {
25380             this.markInvalid(this.errors.PwdLong);
25381             this.errorMsg = this.errors.PwdLong;
25382             return false;
25383         }
25384         var strength;
25385         if (this.ClientSideStrongPassword(value)) {
25386             strength = 3;
25387         } else if (this.ClientSideMediumPassword(value)) {
25388             strength = 2;
25389         } else if (this.ClientSideWeakPassword(value)) {
25390             strength = 1;
25391         } else {
25392             strength = 0;
25393         }
25394
25395         
25396         if (strength < 2) {
25397             //this.markInvalid(this.errors.TooWeak);
25398             this.errorMsg = this.errors.TooWeak;
25399             //return false;
25400         }
25401         
25402         
25403         console.log('strength2: ' + strength);
25404         
25405         //var pm = this.trigger.child('div/div/div').dom;
25406         
25407         var pm = this.trigger.child('div/div');
25408         pm.removeClass(this.meterClass);
25409         pm.addClass(this.meterClass[strength]);
25410                 
25411         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25412                 
25413         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25414         
25415         this.errorMsg = ''; 
25416         return true;
25417     },
25418     // private
25419     CharacterSetChecks: function (type)
25420     {
25421         this.type = type;
25422         this.fResult = false;
25423     },
25424     // private
25425     isctype: function (character, type)
25426     {
25427         switch (type) {  
25428             case this.kCapitalLetter:
25429                 if (character >= 'A' && character <= 'Z') {
25430                     return true;
25431                 }
25432                 break;
25433             
25434             case this.kSmallLetter:
25435                 if (character >= 'a' && character <= 'z') {
25436                     return true;
25437                 }
25438                 break;
25439             
25440             case this.kDigit:
25441                 if (character >= '0' && character <= '9') {
25442                     return true;
25443                 }
25444                 break;
25445             
25446             case this.kPunctuation:
25447                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25448                     return true;
25449                 }
25450                 break;
25451             
25452             default:
25453                 return false;
25454         }
25455
25456     },
25457     // private
25458     IsLongEnough: function (pwd, size)
25459     {
25460         return !(pwd == null || isNaN(size) || pwd.length < size);
25461     },
25462     // private
25463     SpansEnoughCharacterSets: function (word, nb)
25464     {
25465         if (!this.IsLongEnough(word, nb))
25466         {
25467             return false;
25468         }
25469
25470         var characterSetChecks = new Array(
25471             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25472             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25473         );
25474         
25475         for (var index = 0; index < word.length; ++index) {
25476             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25477                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25478                     characterSetChecks[nCharSet].fResult = true;
25479                     break;
25480                 }
25481             }
25482         }
25483
25484         var nCharSets = 0;
25485         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25486             if (characterSetChecks[nCharSet].fResult) {
25487                 ++nCharSets;
25488             }
25489         }
25490
25491         if (nCharSets < nb) {
25492             return false;
25493         }
25494         return true;
25495     },
25496     // private
25497     ClientSideStrongPassword: function (pwd)
25498     {
25499         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25500     },
25501     // private
25502     ClientSideMediumPassword: function (pwd)
25503     {
25504         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25505     },
25506     // private
25507     ClientSideWeakPassword: function (pwd)
25508     {
25509         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25510     }
25511           
25512 })//<script type="text/javascript">
25513
25514 /*
25515  * Based  Ext JS Library 1.1.1
25516  * Copyright(c) 2006-2007, Ext JS, LLC.
25517  * LGPL
25518  *
25519  */
25520  
25521 /**
25522  * @class Roo.HtmlEditorCore
25523  * @extends Roo.Component
25524  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25525  *
25526  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25527  */
25528
25529 Roo.HtmlEditorCore = function(config){
25530     
25531     
25532     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25533     
25534     
25535     this.addEvents({
25536         /**
25537          * @event initialize
25538          * Fires when the editor is fully initialized (including the iframe)
25539          * @param {Roo.HtmlEditorCore} this
25540          */
25541         initialize: true,
25542         /**
25543          * @event activate
25544          * Fires when the editor is first receives the focus. Any insertion must wait
25545          * until after this event.
25546          * @param {Roo.HtmlEditorCore} this
25547          */
25548         activate: true,
25549          /**
25550          * @event beforesync
25551          * Fires before the textarea is updated with content from the editor iframe. Return false
25552          * to cancel the sync.
25553          * @param {Roo.HtmlEditorCore} this
25554          * @param {String} html
25555          */
25556         beforesync: true,
25557          /**
25558          * @event beforepush
25559          * Fires before the iframe editor is updated with content from the textarea. Return false
25560          * to cancel the push.
25561          * @param {Roo.HtmlEditorCore} this
25562          * @param {String} html
25563          */
25564         beforepush: true,
25565          /**
25566          * @event sync
25567          * Fires when the textarea is updated with content from the editor iframe.
25568          * @param {Roo.HtmlEditorCore} this
25569          * @param {String} html
25570          */
25571         sync: true,
25572          /**
25573          * @event push
25574          * Fires when the iframe editor is updated with content from the textarea.
25575          * @param {Roo.HtmlEditorCore} this
25576          * @param {String} html
25577          */
25578         push: true,
25579         
25580         /**
25581          * @event editorevent
25582          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25583          * @param {Roo.HtmlEditorCore} this
25584          */
25585         editorevent: true
25586         
25587     });
25588     
25589     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25590     
25591     // defaults : white / black...
25592     this.applyBlacklists();
25593     
25594     
25595     
25596 };
25597
25598
25599 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25600
25601
25602      /**
25603      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25604      */
25605     
25606     owner : false,
25607     
25608      /**
25609      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25610      *                        Roo.resizable.
25611      */
25612     resizable : false,
25613      /**
25614      * @cfg {Number} height (in pixels)
25615      */   
25616     height: 300,
25617    /**
25618      * @cfg {Number} width (in pixels)
25619      */   
25620     width: 500,
25621     
25622     /**
25623      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25624      * 
25625      */
25626     stylesheets: false,
25627     
25628     /**
25629      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25630      */
25631     allowComments: false,
25632     // id of frame..
25633     frameId: false,
25634     
25635     // private properties
25636     validationEvent : false,
25637     deferHeight: true,
25638     initialized : false,
25639     activated : false,
25640     sourceEditMode : false,
25641     onFocus : Roo.emptyFn,
25642     iframePad:3,
25643     hideMode:'offsets',
25644     
25645     clearUp: true,
25646     
25647     // blacklist + whitelisted elements..
25648     black: false,
25649     white: false,
25650      
25651     bodyCls : '',
25652
25653     /**
25654      * Protected method that will not generally be called directly. It
25655      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25656      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25657      */
25658     getDocMarkup : function(){
25659         // body styles..
25660         var st = '';
25661         
25662         // inherit styels from page...?? 
25663         if (this.stylesheets === false) {
25664             
25665             Roo.get(document.head).select('style').each(function(node) {
25666                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25667             });
25668             
25669             Roo.get(document.head).select('link').each(function(node) { 
25670                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25671             });
25672             
25673         } else if (!this.stylesheets.length) {
25674                 // simple..
25675                 st = '<style type="text/css">' +
25676                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25677                    '</style>';
25678         } else {
25679             for (var i in this.stylesheets) { 
25680                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25681             }
25682             
25683         }
25684         
25685         st +=  '<style type="text/css">' +
25686             'IMG { cursor: pointer } ' +
25687         '</style>';
25688
25689         var cls = 'roo-htmleditor-body';
25690         
25691         if(this.bodyCls.length){
25692             cls += ' ' + this.bodyCls;
25693         }
25694         
25695         return '<html><head>' + st  +
25696             //<style type="text/css">' +
25697             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25698             //'</style>' +
25699             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25700     },
25701
25702     // private
25703     onRender : function(ct, position)
25704     {
25705         var _t = this;
25706         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25707         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25708         
25709         
25710         this.el.dom.style.border = '0 none';
25711         this.el.dom.setAttribute('tabIndex', -1);
25712         this.el.addClass('x-hidden hide');
25713         
25714         
25715         
25716         if(Roo.isIE){ // fix IE 1px bogus margin
25717             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25718         }
25719        
25720         
25721         this.frameId = Roo.id();
25722         
25723          
25724         
25725         var iframe = this.owner.wrap.createChild({
25726             tag: 'iframe',
25727             cls: 'form-control', // bootstrap..
25728             id: this.frameId,
25729             name: this.frameId,
25730             frameBorder : 'no',
25731             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25732         }, this.el
25733         );
25734         
25735         
25736         this.iframe = iframe.dom;
25737
25738          this.assignDocWin();
25739         
25740         this.doc.designMode = 'on';
25741        
25742         this.doc.open();
25743         this.doc.write(this.getDocMarkup());
25744         this.doc.close();
25745
25746         
25747         var task = { // must defer to wait for browser to be ready
25748             run : function(){
25749                 //console.log("run task?" + this.doc.readyState);
25750                 this.assignDocWin();
25751                 if(this.doc.body || this.doc.readyState == 'complete'){
25752                     try {
25753                         this.doc.designMode="on";
25754                     } catch (e) {
25755                         return;
25756                     }
25757                     Roo.TaskMgr.stop(task);
25758                     this.initEditor.defer(10, this);
25759                 }
25760             },
25761             interval : 10,
25762             duration: 10000,
25763             scope: this
25764         };
25765         Roo.TaskMgr.start(task);
25766
25767     },
25768
25769     // private
25770     onResize : function(w, h)
25771     {
25772          Roo.log('resize: ' +w + ',' + h );
25773         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25774         if(!this.iframe){
25775             return;
25776         }
25777         if(typeof w == 'number'){
25778             
25779             this.iframe.style.width = w + 'px';
25780         }
25781         if(typeof h == 'number'){
25782             
25783             this.iframe.style.height = h + 'px';
25784             if(this.doc){
25785                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25786             }
25787         }
25788         
25789     },
25790
25791     /**
25792      * Toggles the editor between standard and source edit mode.
25793      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25794      */
25795     toggleSourceEdit : function(sourceEditMode){
25796         
25797         this.sourceEditMode = sourceEditMode === true;
25798         
25799         if(this.sourceEditMode){
25800  
25801             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25802             
25803         }else{
25804             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25805             //this.iframe.className = '';
25806             this.deferFocus();
25807         }
25808         //this.setSize(this.owner.wrap.getSize());
25809         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25810     },
25811
25812     
25813   
25814
25815     /**
25816      * Protected method that will not generally be called directly. If you need/want
25817      * custom HTML cleanup, this is the method you should override.
25818      * @param {String} html The HTML to be cleaned
25819      * return {String} The cleaned HTML
25820      */
25821     cleanHtml : function(html){
25822         html = String(html);
25823         if(html.length > 5){
25824             if(Roo.isSafari){ // strip safari nonsense
25825                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25826             }
25827         }
25828         if(html == '&nbsp;'){
25829             html = '';
25830         }
25831         return html;
25832     },
25833
25834     /**
25835      * HTML Editor -> Textarea
25836      * Protected method that will not generally be called directly. Syncs the contents
25837      * of the editor iframe with the textarea.
25838      */
25839     syncValue : function(){
25840         if(this.initialized){
25841             var bd = (this.doc.body || this.doc.documentElement);
25842             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25843             var html = bd.innerHTML;
25844             if(Roo.isSafari){
25845                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25846                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25847                 if(m && m[1]){
25848                     html = '<div style="'+m[0]+'">' + html + '</div>';
25849                 }
25850             }
25851             html = this.cleanHtml(html);
25852             // fix up the special chars.. normaly like back quotes in word...
25853             // however we do not want to do this with chinese..
25854             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25855                 
25856                 var cc = match.charCodeAt();
25857
25858                 // Get the character value, handling surrogate pairs
25859                 if (match.length == 2) {
25860                     // It's a surrogate pair, calculate the Unicode code point
25861                     var high = match.charCodeAt(0) - 0xD800;
25862                     var low  = match.charCodeAt(1) - 0xDC00;
25863                     cc = (high * 0x400) + low + 0x10000;
25864                 }  else if (
25865                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25866                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25867                     (cc >= 0xf900 && cc < 0xfb00 )
25868                 ) {
25869                         return match;
25870                 }  
25871          
25872                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25873                 return "&#" + cc + ";";
25874                 
25875                 
25876             });
25877             
25878             
25879              
25880             if(this.owner.fireEvent('beforesync', this, html) !== false){
25881                 this.el.dom.value = html;
25882                 this.owner.fireEvent('sync', this, html);
25883             }
25884         }
25885     },
25886
25887     /**
25888      * Protected method that will not generally be called directly. Pushes the value of the textarea
25889      * into the iframe editor.
25890      */
25891     pushValue : function(){
25892         if(this.initialized){
25893             var v = this.el.dom.value.trim();
25894             
25895 //            if(v.length < 1){
25896 //                v = '&#160;';
25897 //            }
25898             
25899             if(this.owner.fireEvent('beforepush', this, v) !== false){
25900                 var d = (this.doc.body || this.doc.documentElement);
25901                 d.innerHTML = v;
25902                 this.cleanUpPaste();
25903                 this.el.dom.value = d.innerHTML;
25904                 this.owner.fireEvent('push', this, v);
25905             }
25906         }
25907     },
25908
25909     // private
25910     deferFocus : function(){
25911         this.focus.defer(10, this);
25912     },
25913
25914     // doc'ed in Field
25915     focus : function(){
25916         if(this.win && !this.sourceEditMode){
25917             this.win.focus();
25918         }else{
25919             this.el.focus();
25920         }
25921     },
25922     
25923     assignDocWin: function()
25924     {
25925         var iframe = this.iframe;
25926         
25927          if(Roo.isIE){
25928             this.doc = iframe.contentWindow.document;
25929             this.win = iframe.contentWindow;
25930         } else {
25931 //            if (!Roo.get(this.frameId)) {
25932 //                return;
25933 //            }
25934 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25935 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25936             
25937             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25938                 return;
25939             }
25940             
25941             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25942             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25943         }
25944     },
25945     
25946     // private
25947     initEditor : function(){
25948         //console.log("INIT EDITOR");
25949         this.assignDocWin();
25950         
25951         
25952         
25953         this.doc.designMode="on";
25954         this.doc.open();
25955         this.doc.write(this.getDocMarkup());
25956         this.doc.close();
25957         
25958         var dbody = (this.doc.body || this.doc.documentElement);
25959         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25960         // this copies styles from the containing element into thsi one..
25961         // not sure why we need all of this..
25962         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25963         
25964         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25965         //ss['background-attachment'] = 'fixed'; // w3c
25966         dbody.bgProperties = 'fixed'; // ie
25967         //Roo.DomHelper.applyStyles(dbody, ss);
25968         Roo.EventManager.on(this.doc, {
25969             //'mousedown': this.onEditorEvent,
25970             'mouseup': this.onEditorEvent,
25971             'dblclick': this.onEditorEvent,
25972             'click': this.onEditorEvent,
25973             'keyup': this.onEditorEvent,
25974             buffer:100,
25975             scope: this
25976         });
25977         if(Roo.isGecko){
25978             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25979         }
25980         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25981             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25982         }
25983         this.initialized = true;
25984
25985         this.owner.fireEvent('initialize', this);
25986         this.pushValue();
25987     },
25988
25989     // private
25990     onDestroy : function(){
25991         
25992         
25993         
25994         if(this.rendered){
25995             
25996             //for (var i =0; i < this.toolbars.length;i++) {
25997             //    // fixme - ask toolbars for heights?
25998             //    this.toolbars[i].onDestroy();
25999            // }
26000             
26001             //this.wrap.dom.innerHTML = '';
26002             //this.wrap.remove();
26003         }
26004     },
26005
26006     // private
26007     onFirstFocus : function(){
26008         
26009         this.assignDocWin();
26010         
26011         
26012         this.activated = true;
26013          
26014     
26015         if(Roo.isGecko){ // prevent silly gecko errors
26016             this.win.focus();
26017             var s = this.win.getSelection();
26018             if(!s.focusNode || s.focusNode.nodeType != 3){
26019                 var r = s.getRangeAt(0);
26020                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26021                 r.collapse(true);
26022                 this.deferFocus();
26023             }
26024             try{
26025                 this.execCmd('useCSS', true);
26026                 this.execCmd('styleWithCSS', false);
26027             }catch(e){}
26028         }
26029         this.owner.fireEvent('activate', this);
26030     },
26031
26032     // private
26033     adjustFont: function(btn){
26034         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26035         //if(Roo.isSafari){ // safari
26036         //    adjust *= 2;
26037        // }
26038         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26039         if(Roo.isSafari){ // safari
26040             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26041             v =  (v < 10) ? 10 : v;
26042             v =  (v > 48) ? 48 : v;
26043             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26044             
26045         }
26046         
26047         
26048         v = Math.max(1, v+adjust);
26049         
26050         this.execCmd('FontSize', v  );
26051     },
26052
26053     onEditorEvent : function(e)
26054     {
26055         this.owner.fireEvent('editorevent', this, e);
26056       //  this.updateToolbar();
26057         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26058     },
26059
26060     insertTag : function(tg)
26061     {
26062         // could be a bit smarter... -> wrap the current selected tRoo..
26063         if (tg.toLowerCase() == 'span' ||
26064             tg.toLowerCase() == 'code' ||
26065             tg.toLowerCase() == 'sup' ||
26066             tg.toLowerCase() == 'sub' 
26067             ) {
26068             
26069             range = this.createRange(this.getSelection());
26070             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26071             wrappingNode.appendChild(range.extractContents());
26072             range.insertNode(wrappingNode);
26073
26074             return;
26075             
26076             
26077             
26078         }
26079         this.execCmd("formatblock",   tg);
26080         
26081     },
26082     
26083     insertText : function(txt)
26084     {
26085         
26086         
26087         var range = this.createRange();
26088         range.deleteContents();
26089                //alert(Sender.getAttribute('label'));
26090                
26091         range.insertNode(this.doc.createTextNode(txt));
26092     } ,
26093     
26094      
26095
26096     /**
26097      * Executes a Midas editor command on the editor document and performs necessary focus and
26098      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26099      * @param {String} cmd The Midas command
26100      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26101      */
26102     relayCmd : function(cmd, value){
26103         this.win.focus();
26104         this.execCmd(cmd, value);
26105         this.owner.fireEvent('editorevent', this);
26106         //this.updateToolbar();
26107         this.owner.deferFocus();
26108     },
26109
26110     /**
26111      * Executes a Midas editor command directly on the editor document.
26112      * For visual commands, you should use {@link #relayCmd} instead.
26113      * <b>This should only be called after the editor is initialized.</b>
26114      * @param {String} cmd The Midas command
26115      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26116      */
26117     execCmd : function(cmd, value){
26118         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26119         this.syncValue();
26120     },
26121  
26122  
26123    
26124     /**
26125      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26126      * to insert tRoo.
26127      * @param {String} text | dom node.. 
26128      */
26129     insertAtCursor : function(text)
26130     {
26131         
26132         if(!this.activated){
26133             return;
26134         }
26135         /*
26136         if(Roo.isIE){
26137             this.win.focus();
26138             var r = this.doc.selection.createRange();
26139             if(r){
26140                 r.collapse(true);
26141                 r.pasteHTML(text);
26142                 this.syncValue();
26143                 this.deferFocus();
26144             
26145             }
26146             return;
26147         }
26148         */
26149         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26150             this.win.focus();
26151             
26152             
26153             // from jquery ui (MIT licenced)
26154             var range, node;
26155             var win = this.win;
26156             
26157             if (win.getSelection && win.getSelection().getRangeAt) {
26158                 range = win.getSelection().getRangeAt(0);
26159                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26160                 range.insertNode(node);
26161             } else if (win.document.selection && win.document.selection.createRange) {
26162                 // no firefox support
26163                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26164                 win.document.selection.createRange().pasteHTML(txt);
26165             } else {
26166                 // no firefox support
26167                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26168                 this.execCmd('InsertHTML', txt);
26169             } 
26170             
26171             this.syncValue();
26172             
26173             this.deferFocus();
26174         }
26175     },
26176  // private
26177     mozKeyPress : function(e){
26178         if(e.ctrlKey){
26179             var c = e.getCharCode(), cmd;
26180           
26181             if(c > 0){
26182                 c = String.fromCharCode(c).toLowerCase();
26183                 switch(c){
26184                     case 'b':
26185                         cmd = 'bold';
26186                         break;
26187                     case 'i':
26188                         cmd = 'italic';
26189                         break;
26190                     
26191                     case 'u':
26192                         cmd = 'underline';
26193                         break;
26194                     
26195                     case 'v':
26196                         this.cleanUpPaste.defer(100, this);
26197                         return;
26198                         
26199                 }
26200                 if(cmd){
26201                     this.win.focus();
26202                     this.execCmd(cmd);
26203                     this.deferFocus();
26204                     e.preventDefault();
26205                 }
26206                 
26207             }
26208         }
26209     },
26210
26211     // private
26212     fixKeys : function(){ // load time branching for fastest keydown performance
26213         if(Roo.isIE){
26214             return function(e){
26215                 var k = e.getKey(), r;
26216                 if(k == e.TAB){
26217                     e.stopEvent();
26218                     r = this.doc.selection.createRange();
26219                     if(r){
26220                         r.collapse(true);
26221                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26222                         this.deferFocus();
26223                     }
26224                     return;
26225                 }
26226                 
26227                 if(k == e.ENTER){
26228                     r = this.doc.selection.createRange();
26229                     if(r){
26230                         var target = r.parentElement();
26231                         if(!target || target.tagName.toLowerCase() != 'li'){
26232                             e.stopEvent();
26233                             r.pasteHTML('<br />');
26234                             r.collapse(false);
26235                             r.select();
26236                         }
26237                     }
26238                 }
26239                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26240                     this.cleanUpPaste.defer(100, this);
26241                     return;
26242                 }
26243                 
26244                 
26245             };
26246         }else if(Roo.isOpera){
26247             return function(e){
26248                 var k = e.getKey();
26249                 if(k == e.TAB){
26250                     e.stopEvent();
26251                     this.win.focus();
26252                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26253                     this.deferFocus();
26254                 }
26255                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26256                     this.cleanUpPaste.defer(100, this);
26257                     return;
26258                 }
26259                 
26260             };
26261         }else if(Roo.isSafari){
26262             return function(e){
26263                 var k = e.getKey();
26264                 
26265                 if(k == e.TAB){
26266                     e.stopEvent();
26267                     this.execCmd('InsertText','\t');
26268                     this.deferFocus();
26269                     return;
26270                 }
26271                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26272                     this.cleanUpPaste.defer(100, this);
26273                     return;
26274                 }
26275                 
26276              };
26277         }
26278     }(),
26279     
26280     getAllAncestors: function()
26281     {
26282         var p = this.getSelectedNode();
26283         var a = [];
26284         if (!p) {
26285             a.push(p); // push blank onto stack..
26286             p = this.getParentElement();
26287         }
26288         
26289         
26290         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26291             a.push(p);
26292             p = p.parentNode;
26293         }
26294         a.push(this.doc.body);
26295         return a;
26296     },
26297     lastSel : false,
26298     lastSelNode : false,
26299     
26300     
26301     getSelection : function() 
26302     {
26303         this.assignDocWin();
26304         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26305     },
26306     
26307     getSelectedNode: function() 
26308     {
26309         // this may only work on Gecko!!!
26310         
26311         // should we cache this!!!!
26312         
26313         
26314         
26315          
26316         var range = this.createRange(this.getSelection()).cloneRange();
26317         
26318         if (Roo.isIE) {
26319             var parent = range.parentElement();
26320             while (true) {
26321                 var testRange = range.duplicate();
26322                 testRange.moveToElementText(parent);
26323                 if (testRange.inRange(range)) {
26324                     break;
26325                 }
26326                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26327                     break;
26328                 }
26329                 parent = parent.parentElement;
26330             }
26331             return parent;
26332         }
26333         
26334         // is ancestor a text element.
26335         var ac =  range.commonAncestorContainer;
26336         if (ac.nodeType == 3) {
26337             ac = ac.parentNode;
26338         }
26339         
26340         var ar = ac.childNodes;
26341          
26342         var nodes = [];
26343         var other_nodes = [];
26344         var has_other_nodes = false;
26345         for (var i=0;i<ar.length;i++) {
26346             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26347                 continue;
26348             }
26349             // fullly contained node.
26350             
26351             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26352                 nodes.push(ar[i]);
26353                 continue;
26354             }
26355             
26356             // probably selected..
26357             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26358                 other_nodes.push(ar[i]);
26359                 continue;
26360             }
26361             // outer..
26362             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26363                 continue;
26364             }
26365             
26366             
26367             has_other_nodes = true;
26368         }
26369         if (!nodes.length && other_nodes.length) {
26370             nodes= other_nodes;
26371         }
26372         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26373             return false;
26374         }
26375         
26376         return nodes[0];
26377     },
26378     createRange: function(sel)
26379     {
26380         // this has strange effects when using with 
26381         // top toolbar - not sure if it's a great idea.
26382         //this.editor.contentWindow.focus();
26383         if (typeof sel != "undefined") {
26384             try {
26385                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26386             } catch(e) {
26387                 return this.doc.createRange();
26388             }
26389         } else {
26390             return this.doc.createRange();
26391         }
26392     },
26393     getParentElement: function()
26394     {
26395         
26396         this.assignDocWin();
26397         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26398         
26399         var range = this.createRange(sel);
26400          
26401         try {
26402             var p = range.commonAncestorContainer;
26403             while (p.nodeType == 3) { // text node
26404                 p = p.parentNode;
26405             }
26406             return p;
26407         } catch (e) {
26408             return null;
26409         }
26410     
26411     },
26412     /***
26413      *
26414      * Range intersection.. the hard stuff...
26415      *  '-1' = before
26416      *  '0' = hits..
26417      *  '1' = after.
26418      *         [ -- selected range --- ]
26419      *   [fail]                        [fail]
26420      *
26421      *    basically..
26422      *      if end is before start or  hits it. fail.
26423      *      if start is after end or hits it fail.
26424      *
26425      *   if either hits (but other is outside. - then it's not 
26426      *   
26427      *    
26428      **/
26429     
26430     
26431     // @see http://www.thismuchiknow.co.uk/?p=64.
26432     rangeIntersectsNode : function(range, node)
26433     {
26434         var nodeRange = node.ownerDocument.createRange();
26435         try {
26436             nodeRange.selectNode(node);
26437         } catch (e) {
26438             nodeRange.selectNodeContents(node);
26439         }
26440     
26441         var rangeStartRange = range.cloneRange();
26442         rangeStartRange.collapse(true);
26443     
26444         var rangeEndRange = range.cloneRange();
26445         rangeEndRange.collapse(false);
26446     
26447         var nodeStartRange = nodeRange.cloneRange();
26448         nodeStartRange.collapse(true);
26449     
26450         var nodeEndRange = nodeRange.cloneRange();
26451         nodeEndRange.collapse(false);
26452     
26453         return rangeStartRange.compareBoundaryPoints(
26454                  Range.START_TO_START, nodeEndRange) == -1 &&
26455                rangeEndRange.compareBoundaryPoints(
26456                  Range.START_TO_START, nodeStartRange) == 1;
26457         
26458          
26459     },
26460     rangeCompareNode : function(range, node)
26461     {
26462         var nodeRange = node.ownerDocument.createRange();
26463         try {
26464             nodeRange.selectNode(node);
26465         } catch (e) {
26466             nodeRange.selectNodeContents(node);
26467         }
26468         
26469         
26470         range.collapse(true);
26471     
26472         nodeRange.collapse(true);
26473      
26474         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26475         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26476          
26477         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26478         
26479         var nodeIsBefore   =  ss == 1;
26480         var nodeIsAfter    = ee == -1;
26481         
26482         if (nodeIsBefore && nodeIsAfter) {
26483             return 0; // outer
26484         }
26485         if (!nodeIsBefore && nodeIsAfter) {
26486             return 1; //right trailed.
26487         }
26488         
26489         if (nodeIsBefore && !nodeIsAfter) {
26490             return 2;  // left trailed.
26491         }
26492         // fully contined.
26493         return 3;
26494     },
26495
26496     // private? - in a new class?
26497     cleanUpPaste :  function()
26498     {
26499         // cleans up the whole document..
26500         Roo.log('cleanuppaste');
26501         
26502         this.cleanUpChildren(this.doc.body);
26503         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26504         if (clean != this.doc.body.innerHTML) {
26505             this.doc.body.innerHTML = clean;
26506         }
26507         
26508     },
26509     
26510     cleanWordChars : function(input) {// change the chars to hex code
26511         var he = Roo.HtmlEditorCore;
26512         
26513         var output = input;
26514         Roo.each(he.swapCodes, function(sw) { 
26515             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26516             
26517             output = output.replace(swapper, sw[1]);
26518         });
26519         
26520         return output;
26521     },
26522     
26523     
26524     cleanUpChildren : function (n)
26525     {
26526         if (!n.childNodes.length) {
26527             return;
26528         }
26529         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26530            this.cleanUpChild(n.childNodes[i]);
26531         }
26532     },
26533     
26534     
26535         
26536     
26537     cleanUpChild : function (node)
26538     {
26539         var ed = this;
26540         //console.log(node);
26541         if (node.nodeName == "#text") {
26542             // clean up silly Windows -- stuff?
26543             return; 
26544         }
26545         if (node.nodeName == "#comment") {
26546             if (!this.allowComments) {
26547                 node.parentNode.removeChild(node);
26548             }
26549             // clean up silly Windows -- stuff?
26550             return; 
26551         }
26552         var lcname = node.tagName.toLowerCase();
26553         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26554         // whitelist of tags..
26555         
26556         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26557             // remove node.
26558             node.parentNode.removeChild(node);
26559             return;
26560             
26561         }
26562         
26563         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26564         
26565         // spans with no attributes - just remove them..
26566         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26567             remove_keep_children = true;
26568         }
26569         
26570         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26571         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26572         
26573         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26574         //    remove_keep_children = true;
26575         //}
26576         
26577         if (remove_keep_children) {
26578             this.cleanUpChildren(node);
26579             // inserts everything just before this node...
26580             while (node.childNodes.length) {
26581                 var cn = node.childNodes[0];
26582                 node.removeChild(cn);
26583                 node.parentNode.insertBefore(cn, node);
26584             }
26585             node.parentNode.removeChild(node);
26586             return;
26587         }
26588         
26589         if (!node.attributes || !node.attributes.length) {
26590             
26591           
26592             
26593             
26594             this.cleanUpChildren(node);
26595             return;
26596         }
26597         
26598         function cleanAttr(n,v)
26599         {
26600             
26601             if (v.match(/^\./) || v.match(/^\//)) {
26602                 return;
26603             }
26604             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26605                 return;
26606             }
26607             if (v.match(/^#/)) {
26608                 return;
26609             }
26610             if (v.match(/^\{/)) { // allow template editing.
26611                 return;
26612             }
26613 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26614             node.removeAttribute(n);
26615             
26616         }
26617         
26618         var cwhite = this.cwhite;
26619         var cblack = this.cblack;
26620             
26621         function cleanStyle(n,v)
26622         {
26623             if (v.match(/expression/)) { //XSS?? should we even bother..
26624                 node.removeAttribute(n);
26625                 return;
26626             }
26627             
26628             var parts = v.split(/;/);
26629             var clean = [];
26630             
26631             Roo.each(parts, function(p) {
26632                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26633                 if (!p.length) {
26634                     return true;
26635                 }
26636                 var l = p.split(':').shift().replace(/\s+/g,'');
26637                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26638                 
26639                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26640 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26641                     //node.removeAttribute(n);
26642                     return true;
26643                 }
26644                 //Roo.log()
26645                 // only allow 'c whitelisted system attributes'
26646                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26647 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26648                     //node.removeAttribute(n);
26649                     return true;
26650                 }
26651                 
26652                 
26653                  
26654                 
26655                 clean.push(p);
26656                 return true;
26657             });
26658             if (clean.length) { 
26659                 node.setAttribute(n, clean.join(';'));
26660             } else {
26661                 node.removeAttribute(n);
26662             }
26663             
26664         }
26665         
26666         
26667         for (var i = node.attributes.length-1; i > -1 ; i--) {
26668             var a = node.attributes[i];
26669             //console.log(a);
26670             
26671             if (a.name.toLowerCase().substr(0,2)=='on')  {
26672                 node.removeAttribute(a.name);
26673                 continue;
26674             }
26675             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26676                 node.removeAttribute(a.name);
26677                 continue;
26678             }
26679             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26680                 cleanAttr(a.name,a.value); // fixme..
26681                 continue;
26682             }
26683             if (a.name == 'style') {
26684                 cleanStyle(a.name,a.value);
26685                 continue;
26686             }
26687             /// clean up MS crap..
26688             // tecnically this should be a list of valid class'es..
26689             
26690             
26691             if (a.name == 'class') {
26692                 if (a.value.match(/^Mso/)) {
26693                     node.removeAttribute('class');
26694                 }
26695                 
26696                 if (a.value.match(/^body$/)) {
26697                     node.removeAttribute('class');
26698                 }
26699                 continue;
26700             }
26701             
26702             // style cleanup!?
26703             // class cleanup?
26704             
26705         }
26706         
26707         
26708         this.cleanUpChildren(node);
26709         
26710         
26711     },
26712     
26713     /**
26714      * Clean up MS wordisms...
26715      */
26716     cleanWord : function(node)
26717     {
26718         if (!node) {
26719             this.cleanWord(this.doc.body);
26720             return;
26721         }
26722         
26723         if(
26724                 node.nodeName == 'SPAN' &&
26725                 !node.hasAttributes() &&
26726                 node.childNodes.length == 1 &&
26727                 node.firstChild.nodeName == "#text"  
26728         ) {
26729             var textNode = node.firstChild;
26730             node.removeChild(textNode);
26731             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26732                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26733             }
26734             node.parentNode.insertBefore(textNode, node);
26735             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26736                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26737             }
26738             node.parentNode.removeChild(node);
26739         }
26740         
26741         if (node.nodeName == "#text") {
26742             // clean up silly Windows -- stuff?
26743             return; 
26744         }
26745         if (node.nodeName == "#comment") {
26746             node.parentNode.removeChild(node);
26747             // clean up silly Windows -- stuff?
26748             return; 
26749         }
26750         
26751         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26752             node.parentNode.removeChild(node);
26753             return;
26754         }
26755         //Roo.log(node.tagName);
26756         // remove - but keep children..
26757         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26758             //Roo.log('-- removed');
26759             while (node.childNodes.length) {
26760                 var cn = node.childNodes[0];
26761                 node.removeChild(cn);
26762                 node.parentNode.insertBefore(cn, node);
26763                 // move node to parent - and clean it..
26764                 this.cleanWord(cn);
26765             }
26766             node.parentNode.removeChild(node);
26767             /// no need to iterate chidlren = it's got none..
26768             //this.iterateChildren(node, this.cleanWord);
26769             return;
26770         }
26771         // clean styles
26772         if (node.className.length) {
26773             
26774             var cn = node.className.split(/\W+/);
26775             var cna = [];
26776             Roo.each(cn, function(cls) {
26777                 if (cls.match(/Mso[a-zA-Z]+/)) {
26778                     return;
26779                 }
26780                 cna.push(cls);
26781             });
26782             node.className = cna.length ? cna.join(' ') : '';
26783             if (!cna.length) {
26784                 node.removeAttribute("class");
26785             }
26786         }
26787         
26788         if (node.hasAttribute("lang")) {
26789             node.removeAttribute("lang");
26790         }
26791         
26792         if (node.hasAttribute("style")) {
26793             
26794             var styles = node.getAttribute("style").split(";");
26795             var nstyle = [];
26796             Roo.each(styles, function(s) {
26797                 if (!s.match(/:/)) {
26798                     return;
26799                 }
26800                 var kv = s.split(":");
26801                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26802                     return;
26803                 }
26804                 // what ever is left... we allow.
26805                 nstyle.push(s);
26806             });
26807             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26808             if (!nstyle.length) {
26809                 node.removeAttribute('style');
26810             }
26811         }
26812         this.iterateChildren(node, this.cleanWord);
26813         
26814         
26815         
26816     },
26817     /**
26818      * iterateChildren of a Node, calling fn each time, using this as the scole..
26819      * @param {DomNode} node node to iterate children of.
26820      * @param {Function} fn method of this class to call on each item.
26821      */
26822     iterateChildren : function(node, fn)
26823     {
26824         if (!node.childNodes.length) {
26825                 return;
26826         }
26827         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26828            fn.call(this, node.childNodes[i])
26829         }
26830     },
26831     
26832     
26833     /**
26834      * cleanTableWidths.
26835      *
26836      * Quite often pasting from word etc.. results in tables with column and widths.
26837      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26838      *
26839      */
26840     cleanTableWidths : function(node)
26841     {
26842          
26843          
26844         if (!node) {
26845             this.cleanTableWidths(this.doc.body);
26846             return;
26847         }
26848         
26849         // ignore list...
26850         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26851             return; 
26852         }
26853         Roo.log(node.tagName);
26854         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26855             this.iterateChildren(node, this.cleanTableWidths);
26856             return;
26857         }
26858         if (node.hasAttribute('width')) {
26859             node.removeAttribute('width');
26860         }
26861         
26862          
26863         if (node.hasAttribute("style")) {
26864             // pretty basic...
26865             
26866             var styles = node.getAttribute("style").split(";");
26867             var nstyle = [];
26868             Roo.each(styles, function(s) {
26869                 if (!s.match(/:/)) {
26870                     return;
26871                 }
26872                 var kv = s.split(":");
26873                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26874                     return;
26875                 }
26876                 // what ever is left... we allow.
26877                 nstyle.push(s);
26878             });
26879             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26880             if (!nstyle.length) {
26881                 node.removeAttribute('style');
26882             }
26883         }
26884         
26885         this.iterateChildren(node, this.cleanTableWidths);
26886         
26887         
26888     },
26889     
26890     
26891     
26892     
26893     domToHTML : function(currentElement, depth, nopadtext) {
26894         
26895         depth = depth || 0;
26896         nopadtext = nopadtext || false;
26897     
26898         if (!currentElement) {
26899             return this.domToHTML(this.doc.body);
26900         }
26901         
26902         //Roo.log(currentElement);
26903         var j;
26904         var allText = false;
26905         var nodeName = currentElement.nodeName;
26906         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26907         
26908         if  (nodeName == '#text') {
26909             
26910             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26911         }
26912         
26913         
26914         var ret = '';
26915         if (nodeName != 'BODY') {
26916              
26917             var i = 0;
26918             // Prints the node tagName, such as <A>, <IMG>, etc
26919             if (tagName) {
26920                 var attr = [];
26921                 for(i = 0; i < currentElement.attributes.length;i++) {
26922                     // quoting?
26923                     var aname = currentElement.attributes.item(i).name;
26924                     if (!currentElement.attributes.item(i).value.length) {
26925                         continue;
26926                     }
26927                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26928                 }
26929                 
26930                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26931             } 
26932             else {
26933                 
26934                 // eack
26935             }
26936         } else {
26937             tagName = false;
26938         }
26939         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26940             return ret;
26941         }
26942         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26943             nopadtext = true;
26944         }
26945         
26946         
26947         // Traverse the tree
26948         i = 0;
26949         var currentElementChild = currentElement.childNodes.item(i);
26950         var allText = true;
26951         var innerHTML  = '';
26952         lastnode = '';
26953         while (currentElementChild) {
26954             // Formatting code (indent the tree so it looks nice on the screen)
26955             var nopad = nopadtext;
26956             if (lastnode == 'SPAN') {
26957                 nopad  = true;
26958             }
26959             // text
26960             if  (currentElementChild.nodeName == '#text') {
26961                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26962                 toadd = nopadtext ? toadd : toadd.trim();
26963                 if (!nopad && toadd.length > 80) {
26964                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26965                 }
26966                 innerHTML  += toadd;
26967                 
26968                 i++;
26969                 currentElementChild = currentElement.childNodes.item(i);
26970                 lastNode = '';
26971                 continue;
26972             }
26973             allText = false;
26974             
26975             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26976                 
26977             // Recursively traverse the tree structure of the child node
26978             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26979             lastnode = currentElementChild.nodeName;
26980             i++;
26981             currentElementChild=currentElement.childNodes.item(i);
26982         }
26983         
26984         ret += innerHTML;
26985         
26986         if (!allText) {
26987                 // The remaining code is mostly for formatting the tree
26988             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26989         }
26990         
26991         
26992         if (tagName) {
26993             ret+= "</"+tagName+">";
26994         }
26995         return ret;
26996         
26997     },
26998         
26999     applyBlacklists : function()
27000     {
27001         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27002         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27003         
27004         this.white = [];
27005         this.black = [];
27006         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27007             if (b.indexOf(tag) > -1) {
27008                 return;
27009             }
27010             this.white.push(tag);
27011             
27012         }, this);
27013         
27014         Roo.each(w, function(tag) {
27015             if (b.indexOf(tag) > -1) {
27016                 return;
27017             }
27018             if (this.white.indexOf(tag) > -1) {
27019                 return;
27020             }
27021             this.white.push(tag);
27022             
27023         }, this);
27024         
27025         
27026         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27027             if (w.indexOf(tag) > -1) {
27028                 return;
27029             }
27030             this.black.push(tag);
27031             
27032         }, this);
27033         
27034         Roo.each(b, function(tag) {
27035             if (w.indexOf(tag) > -1) {
27036                 return;
27037             }
27038             if (this.black.indexOf(tag) > -1) {
27039                 return;
27040             }
27041             this.black.push(tag);
27042             
27043         }, this);
27044         
27045         
27046         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27047         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27048         
27049         this.cwhite = [];
27050         this.cblack = [];
27051         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27052             if (b.indexOf(tag) > -1) {
27053                 return;
27054             }
27055             this.cwhite.push(tag);
27056             
27057         }, this);
27058         
27059         Roo.each(w, function(tag) {
27060             if (b.indexOf(tag) > -1) {
27061                 return;
27062             }
27063             if (this.cwhite.indexOf(tag) > -1) {
27064                 return;
27065             }
27066             this.cwhite.push(tag);
27067             
27068         }, this);
27069         
27070         
27071         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27072             if (w.indexOf(tag) > -1) {
27073                 return;
27074             }
27075             this.cblack.push(tag);
27076             
27077         }, this);
27078         
27079         Roo.each(b, function(tag) {
27080             if (w.indexOf(tag) > -1) {
27081                 return;
27082             }
27083             if (this.cblack.indexOf(tag) > -1) {
27084                 return;
27085             }
27086             this.cblack.push(tag);
27087             
27088         }, this);
27089     },
27090     
27091     setStylesheets : function(stylesheets)
27092     {
27093         if(typeof(stylesheets) == 'string'){
27094             Roo.get(this.iframe.contentDocument.head).createChild({
27095                 tag : 'link',
27096                 rel : 'stylesheet',
27097                 type : 'text/css',
27098                 href : stylesheets
27099             });
27100             
27101             return;
27102         }
27103         var _this = this;
27104      
27105         Roo.each(stylesheets, function(s) {
27106             if(!s.length){
27107                 return;
27108             }
27109             
27110             Roo.get(_this.iframe.contentDocument.head).createChild({
27111                 tag : 'link',
27112                 rel : 'stylesheet',
27113                 type : 'text/css',
27114                 href : s
27115             });
27116         });
27117
27118         
27119     },
27120     
27121     removeStylesheets : function()
27122     {
27123         var _this = this;
27124         
27125         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27126             s.remove();
27127         });
27128     },
27129     
27130     setStyle : function(style)
27131     {
27132         Roo.get(this.iframe.contentDocument.head).createChild({
27133             tag : 'style',
27134             type : 'text/css',
27135             html : style
27136         });
27137
27138         return;
27139     }
27140     
27141     // hide stuff that is not compatible
27142     /**
27143      * @event blur
27144      * @hide
27145      */
27146     /**
27147      * @event change
27148      * @hide
27149      */
27150     /**
27151      * @event focus
27152      * @hide
27153      */
27154     /**
27155      * @event specialkey
27156      * @hide
27157      */
27158     /**
27159      * @cfg {String} fieldClass @hide
27160      */
27161     /**
27162      * @cfg {String} focusClass @hide
27163      */
27164     /**
27165      * @cfg {String} autoCreate @hide
27166      */
27167     /**
27168      * @cfg {String} inputType @hide
27169      */
27170     /**
27171      * @cfg {String} invalidClass @hide
27172      */
27173     /**
27174      * @cfg {String} invalidText @hide
27175      */
27176     /**
27177      * @cfg {String} msgFx @hide
27178      */
27179     /**
27180      * @cfg {String} validateOnBlur @hide
27181      */
27182 });
27183
27184 Roo.HtmlEditorCore.white = [
27185         'area', 'br', 'img', 'input', 'hr', 'wbr',
27186         
27187        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27188        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27189        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27190        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27191        'table',   'ul',         'xmp', 
27192        
27193        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27194       'thead',   'tr', 
27195      
27196       'dir', 'menu', 'ol', 'ul', 'dl',
27197        
27198       'embed',  'object'
27199 ];
27200
27201
27202 Roo.HtmlEditorCore.black = [
27203     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27204         'applet', // 
27205         'base',   'basefont', 'bgsound', 'blink',  'body', 
27206         'frame',  'frameset', 'head',    'html',   'ilayer', 
27207         'iframe', 'layer',  'link',     'meta',    'object',   
27208         'script', 'style' ,'title',  'xml' // clean later..
27209 ];
27210 Roo.HtmlEditorCore.clean = [
27211     'script', 'style', 'title', 'xml'
27212 ];
27213 Roo.HtmlEditorCore.remove = [
27214     'font'
27215 ];
27216 // attributes..
27217
27218 Roo.HtmlEditorCore.ablack = [
27219     'on'
27220 ];
27221     
27222 Roo.HtmlEditorCore.aclean = [ 
27223     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27224 ];
27225
27226 // protocols..
27227 Roo.HtmlEditorCore.pwhite= [
27228         'http',  'https',  'mailto'
27229 ];
27230
27231 // white listed style attributes.
27232 Roo.HtmlEditorCore.cwhite= [
27233       //  'text-align', /// default is to allow most things..
27234       
27235          
27236 //        'font-size'//??
27237 ];
27238
27239 // black listed style attributes.
27240 Roo.HtmlEditorCore.cblack= [
27241       //  'font-size' -- this can be set by the project 
27242 ];
27243
27244
27245 Roo.HtmlEditorCore.swapCodes   =[ 
27246     [    8211, "&#8211;" ], 
27247     [    8212, "&#8212;" ], 
27248     [    8216,  "'" ],  
27249     [    8217, "'" ],  
27250     [    8220, '"' ],  
27251     [    8221, '"' ],  
27252     [    8226, "*" ],  
27253     [    8230, "..." ]
27254 ]; 
27255
27256     /*
27257  * - LGPL
27258  *
27259  * HtmlEditor
27260  * 
27261  */
27262
27263 /**
27264  * @class Roo.bootstrap.HtmlEditor
27265  * @extends Roo.bootstrap.TextArea
27266  * Bootstrap HtmlEditor class
27267
27268  * @constructor
27269  * Create a new HtmlEditor
27270  * @param {Object} config The config object
27271  */
27272
27273 Roo.bootstrap.HtmlEditor = function(config){
27274     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27275     if (!this.toolbars) {
27276         this.toolbars = [];
27277     }
27278     
27279     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27280     this.addEvents({
27281             /**
27282              * @event initialize
27283              * Fires when the editor is fully initialized (including the iframe)
27284              * @param {HtmlEditor} this
27285              */
27286             initialize: true,
27287             /**
27288              * @event activate
27289              * Fires when the editor is first receives the focus. Any insertion must wait
27290              * until after this event.
27291              * @param {HtmlEditor} this
27292              */
27293             activate: true,
27294              /**
27295              * @event beforesync
27296              * Fires before the textarea is updated with content from the editor iframe. Return false
27297              * to cancel the sync.
27298              * @param {HtmlEditor} this
27299              * @param {String} html
27300              */
27301             beforesync: true,
27302              /**
27303              * @event beforepush
27304              * Fires before the iframe editor is updated with content from the textarea. Return false
27305              * to cancel the push.
27306              * @param {HtmlEditor} this
27307              * @param {String} html
27308              */
27309             beforepush: true,
27310              /**
27311              * @event sync
27312              * Fires when the textarea is updated with content from the editor iframe.
27313              * @param {HtmlEditor} this
27314              * @param {String} html
27315              */
27316             sync: true,
27317              /**
27318              * @event push
27319              * Fires when the iframe editor is updated with content from the textarea.
27320              * @param {HtmlEditor} this
27321              * @param {String} html
27322              */
27323             push: true,
27324              /**
27325              * @event editmodechange
27326              * Fires when the editor switches edit modes
27327              * @param {HtmlEditor} this
27328              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27329              */
27330             editmodechange: true,
27331             /**
27332              * @event editorevent
27333              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27334              * @param {HtmlEditor} this
27335              */
27336             editorevent: true,
27337             /**
27338              * @event firstfocus
27339              * Fires when on first focus - needed by toolbars..
27340              * @param {HtmlEditor} this
27341              */
27342             firstfocus: true,
27343             /**
27344              * @event autosave
27345              * Auto save the htmlEditor value as a file into Events
27346              * @param {HtmlEditor} this
27347              */
27348             autosave: true,
27349             /**
27350              * @event savedpreview
27351              * preview the saved version of htmlEditor
27352              * @param {HtmlEditor} this
27353              */
27354             savedpreview: true
27355         });
27356 };
27357
27358
27359 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27360     
27361     
27362       /**
27363      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27364      */
27365     toolbars : false,
27366     
27367      /**
27368     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27369     */
27370     btns : [],
27371    
27372      /**
27373      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27374      *                        Roo.resizable.
27375      */
27376     resizable : false,
27377      /**
27378      * @cfg {Number} height (in pixels)
27379      */   
27380     height: 300,
27381    /**
27382      * @cfg {Number} width (in pixels)
27383      */   
27384     width: false,
27385     
27386     /**
27387      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27388      * 
27389      */
27390     stylesheets: false,
27391     
27392     // id of frame..
27393     frameId: false,
27394     
27395     // private properties
27396     validationEvent : false,
27397     deferHeight: true,
27398     initialized : false,
27399     activated : false,
27400     
27401     onFocus : Roo.emptyFn,
27402     iframePad:3,
27403     hideMode:'offsets',
27404     
27405     tbContainer : false,
27406     
27407     bodyCls : '',
27408     
27409     toolbarContainer :function() {
27410         return this.wrap.select('.x-html-editor-tb',true).first();
27411     },
27412
27413     /**
27414      * Protected method that will not generally be called directly. It
27415      * is called when the editor creates its toolbar. Override this method if you need to
27416      * add custom toolbar buttons.
27417      * @param {HtmlEditor} editor
27418      */
27419     createToolbar : function(){
27420         Roo.log('renewing');
27421         Roo.log("create toolbars");
27422         
27423         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27424         this.toolbars[0].render(this.toolbarContainer());
27425         
27426         return;
27427         
27428 //        if (!editor.toolbars || !editor.toolbars.length) {
27429 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27430 //        }
27431 //        
27432 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27433 //            editor.toolbars[i] = Roo.factory(
27434 //                    typeof(editor.toolbars[i]) == 'string' ?
27435 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27436 //                Roo.bootstrap.HtmlEditor);
27437 //            editor.toolbars[i].init(editor);
27438 //        }
27439     },
27440
27441      
27442     // private
27443     onRender : function(ct, position)
27444     {
27445        // Roo.log("Call onRender: " + this.xtype);
27446         var _t = this;
27447         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27448       
27449         this.wrap = this.inputEl().wrap({
27450             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27451         });
27452         
27453         this.editorcore.onRender(ct, position);
27454          
27455         if (this.resizable) {
27456             this.resizeEl = new Roo.Resizable(this.wrap, {
27457                 pinned : true,
27458                 wrap: true,
27459                 dynamic : true,
27460                 minHeight : this.height,
27461                 height: this.height,
27462                 handles : this.resizable,
27463                 width: this.width,
27464                 listeners : {
27465                     resize : function(r, w, h) {
27466                         _t.onResize(w,h); // -something
27467                     }
27468                 }
27469             });
27470             
27471         }
27472         this.createToolbar(this);
27473        
27474         
27475         if(!this.width && this.resizable){
27476             this.setSize(this.wrap.getSize());
27477         }
27478         if (this.resizeEl) {
27479             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27480             // should trigger onReize..
27481         }
27482         
27483     },
27484
27485     // private
27486     onResize : function(w, h)
27487     {
27488         Roo.log('resize: ' +w + ',' + h );
27489         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27490         var ew = false;
27491         var eh = false;
27492         
27493         if(this.inputEl() ){
27494             if(typeof w == 'number'){
27495                 var aw = w - this.wrap.getFrameWidth('lr');
27496                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27497                 ew = aw;
27498             }
27499             if(typeof h == 'number'){
27500                  var tbh = -11;  // fixme it needs to tool bar size!
27501                 for (var i =0; i < this.toolbars.length;i++) {
27502                     // fixme - ask toolbars for heights?
27503                     tbh += this.toolbars[i].el.getHeight();
27504                     //if (this.toolbars[i].footer) {
27505                     //    tbh += this.toolbars[i].footer.el.getHeight();
27506                     //}
27507                 }
27508               
27509                 
27510                 
27511                 
27512                 
27513                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27514                 ah -= 5; // knock a few pixes off for look..
27515                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27516                 var eh = ah;
27517             }
27518         }
27519         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27520         this.editorcore.onResize(ew,eh);
27521         
27522     },
27523
27524     /**
27525      * Toggles the editor between standard and source edit mode.
27526      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27527      */
27528     toggleSourceEdit : function(sourceEditMode)
27529     {
27530         this.editorcore.toggleSourceEdit(sourceEditMode);
27531         
27532         if(this.editorcore.sourceEditMode){
27533             Roo.log('editor - showing textarea');
27534             
27535 //            Roo.log('in');
27536 //            Roo.log(this.syncValue());
27537             this.syncValue();
27538             this.inputEl().removeClass(['hide', 'x-hidden']);
27539             this.inputEl().dom.removeAttribute('tabIndex');
27540             this.inputEl().focus();
27541         }else{
27542             Roo.log('editor - hiding textarea');
27543 //            Roo.log('out')
27544 //            Roo.log(this.pushValue()); 
27545             this.pushValue();
27546             
27547             this.inputEl().addClass(['hide', 'x-hidden']);
27548             this.inputEl().dom.setAttribute('tabIndex', -1);
27549             //this.deferFocus();
27550         }
27551          
27552         if(this.resizable){
27553             this.setSize(this.wrap.getSize());
27554         }
27555         
27556         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27557     },
27558  
27559     // private (for BoxComponent)
27560     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27561
27562     // private (for BoxComponent)
27563     getResizeEl : function(){
27564         return this.wrap;
27565     },
27566
27567     // private (for BoxComponent)
27568     getPositionEl : function(){
27569         return this.wrap;
27570     },
27571
27572     // private
27573     initEvents : function(){
27574         this.originalValue = this.getValue();
27575     },
27576
27577 //    /**
27578 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27579 //     * @method
27580 //     */
27581 //    markInvalid : Roo.emptyFn,
27582 //    /**
27583 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27584 //     * @method
27585 //     */
27586 //    clearInvalid : Roo.emptyFn,
27587
27588     setValue : function(v){
27589         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27590         this.editorcore.pushValue();
27591     },
27592
27593      
27594     // private
27595     deferFocus : function(){
27596         this.focus.defer(10, this);
27597     },
27598
27599     // doc'ed in Field
27600     focus : function(){
27601         this.editorcore.focus();
27602         
27603     },
27604       
27605
27606     // private
27607     onDestroy : function(){
27608         
27609         
27610         
27611         if(this.rendered){
27612             
27613             for (var i =0; i < this.toolbars.length;i++) {
27614                 // fixme - ask toolbars for heights?
27615                 this.toolbars[i].onDestroy();
27616             }
27617             
27618             this.wrap.dom.innerHTML = '';
27619             this.wrap.remove();
27620         }
27621     },
27622
27623     // private
27624     onFirstFocus : function(){
27625         //Roo.log("onFirstFocus");
27626         this.editorcore.onFirstFocus();
27627          for (var i =0; i < this.toolbars.length;i++) {
27628             this.toolbars[i].onFirstFocus();
27629         }
27630         
27631     },
27632     
27633     // private
27634     syncValue : function()
27635     {   
27636         this.editorcore.syncValue();
27637     },
27638     
27639     pushValue : function()
27640     {   
27641         this.editorcore.pushValue();
27642     }
27643      
27644     
27645     // hide stuff that is not compatible
27646     /**
27647      * @event blur
27648      * @hide
27649      */
27650     /**
27651      * @event change
27652      * @hide
27653      */
27654     /**
27655      * @event focus
27656      * @hide
27657      */
27658     /**
27659      * @event specialkey
27660      * @hide
27661      */
27662     /**
27663      * @cfg {String} fieldClass @hide
27664      */
27665     /**
27666      * @cfg {String} focusClass @hide
27667      */
27668     /**
27669      * @cfg {String} autoCreate @hide
27670      */
27671     /**
27672      * @cfg {String} inputType @hide
27673      */
27674      
27675     /**
27676      * @cfg {String} invalidText @hide
27677      */
27678     /**
27679      * @cfg {String} msgFx @hide
27680      */
27681     /**
27682      * @cfg {String} validateOnBlur @hide
27683      */
27684 });
27685  
27686     
27687    
27688    
27689    
27690       
27691 Roo.namespace('Roo.bootstrap.htmleditor');
27692 /**
27693  * @class Roo.bootstrap.HtmlEditorToolbar1
27694  * Basic Toolbar
27695  * 
27696  * @example
27697  * Usage:
27698  *
27699  new Roo.bootstrap.HtmlEditor({
27700     ....
27701     toolbars : [
27702         new Roo.bootstrap.HtmlEditorToolbar1({
27703             disable : { fonts: 1 , format: 1, ..., ... , ...],
27704             btns : [ .... ]
27705         })
27706     }
27707      
27708  * 
27709  * @cfg {Object} disable List of elements to disable..
27710  * @cfg {Array} btns List of additional buttons.
27711  * 
27712  * 
27713  * NEEDS Extra CSS? 
27714  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27715  */
27716  
27717 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27718 {
27719     
27720     Roo.apply(this, config);
27721     
27722     // default disabled, based on 'good practice'..
27723     this.disable = this.disable || {};
27724     Roo.applyIf(this.disable, {
27725         fontSize : true,
27726         colors : true,
27727         specialElements : true
27728     });
27729     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27730     
27731     this.editor = config.editor;
27732     this.editorcore = config.editor.editorcore;
27733     
27734     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27735     
27736     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27737     // dont call parent... till later.
27738 }
27739 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27740      
27741     bar : true,
27742     
27743     editor : false,
27744     editorcore : false,
27745     
27746     
27747     formats : [
27748         "p" ,  
27749         "h1","h2","h3","h4","h5","h6", 
27750         "pre", "code", 
27751         "abbr", "acronym", "address", "cite", "samp", "var",
27752         'div','span'
27753     ],
27754     
27755     onRender : function(ct, position)
27756     {
27757        // Roo.log("Call onRender: " + this.xtype);
27758         
27759        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27760        Roo.log(this.el);
27761        this.el.dom.style.marginBottom = '0';
27762        var _this = this;
27763        var editorcore = this.editorcore;
27764        var editor= this.editor;
27765        
27766        var children = [];
27767        var btn = function(id,cmd , toggle, handler, html){
27768        
27769             var  event = toggle ? 'toggle' : 'click';
27770        
27771             var a = {
27772                 size : 'sm',
27773                 xtype: 'Button',
27774                 xns: Roo.bootstrap,
27775                 //glyphicon : id,
27776                 fa: id,
27777                 cmd : id || cmd,
27778                 enableToggle:toggle !== false,
27779                 html : html || '',
27780                 pressed : toggle ? false : null,
27781                 listeners : {}
27782             };
27783             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27784                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27785             };
27786             children.push(a);
27787             return a;
27788        }
27789        
27790     //    var cb_box = function...
27791         
27792         var style = {
27793                 xtype: 'Button',
27794                 size : 'sm',
27795                 xns: Roo.bootstrap,
27796                 fa : 'font',
27797                 //html : 'submit'
27798                 menu : {
27799                     xtype: 'Menu',
27800                     xns: Roo.bootstrap,
27801                     items:  []
27802                 }
27803         };
27804         Roo.each(this.formats, function(f) {
27805             style.menu.items.push({
27806                 xtype :'MenuItem',
27807                 xns: Roo.bootstrap,
27808                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27809                 tagname : f,
27810                 listeners : {
27811                     click : function()
27812                     {
27813                         editorcore.insertTag(this.tagname);
27814                         editor.focus();
27815                     }
27816                 }
27817                 
27818             });
27819         });
27820         children.push(style);   
27821         
27822         btn('bold',false,true);
27823         btn('italic',false,true);
27824         btn('align-left', 'justifyleft',true);
27825         btn('align-center', 'justifycenter',true);
27826         btn('align-right' , 'justifyright',true);
27827         btn('link', false, false, function(btn) {
27828             //Roo.log("create link?");
27829             var url = prompt(this.createLinkText, this.defaultLinkValue);
27830             if(url && url != 'http:/'+'/'){
27831                 this.editorcore.relayCmd('createlink', url);
27832             }
27833         }),
27834         btn('list','insertunorderedlist',true);
27835         btn('pencil', false,true, function(btn){
27836                 Roo.log(this);
27837                 this.toggleSourceEdit(btn.pressed);
27838         });
27839         
27840         if (this.editor.btns.length > 0) {
27841             for (var i = 0; i<this.editor.btns.length; i++) {
27842                 children.push(this.editor.btns[i]);
27843             }
27844         }
27845         
27846         /*
27847         var cog = {
27848                 xtype: 'Button',
27849                 size : 'sm',
27850                 xns: Roo.bootstrap,
27851                 glyphicon : 'cog',
27852                 //html : 'submit'
27853                 menu : {
27854                     xtype: 'Menu',
27855                     xns: Roo.bootstrap,
27856                     items:  []
27857                 }
27858         };
27859         
27860         cog.menu.items.push({
27861             xtype :'MenuItem',
27862             xns: Roo.bootstrap,
27863             html : Clean styles,
27864             tagname : f,
27865             listeners : {
27866                 click : function()
27867                 {
27868                     editorcore.insertTag(this.tagname);
27869                     editor.focus();
27870                 }
27871             }
27872             
27873         });
27874        */
27875         
27876          
27877        this.xtype = 'NavSimplebar';
27878         
27879         for(var i=0;i< children.length;i++) {
27880             
27881             this.buttons.add(this.addxtypeChild(children[i]));
27882             
27883         }
27884         
27885         editor.on('editorevent', this.updateToolbar, this);
27886     },
27887     onBtnClick : function(id)
27888     {
27889        this.editorcore.relayCmd(id);
27890        this.editorcore.focus();
27891     },
27892     
27893     /**
27894      * Protected method that will not generally be called directly. It triggers
27895      * a toolbar update by reading the markup state of the current selection in the editor.
27896      */
27897     updateToolbar: function(){
27898
27899         if(!this.editorcore.activated){
27900             this.editor.onFirstFocus(); // is this neeed?
27901             return;
27902         }
27903
27904         var btns = this.buttons; 
27905         var doc = this.editorcore.doc;
27906         btns.get('bold').setActive(doc.queryCommandState('bold'));
27907         btns.get('italic').setActive(doc.queryCommandState('italic'));
27908         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27909         
27910         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27911         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27912         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27913         
27914         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27915         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27916          /*
27917         
27918         var ans = this.editorcore.getAllAncestors();
27919         if (this.formatCombo) {
27920             
27921             
27922             var store = this.formatCombo.store;
27923             this.formatCombo.setValue("");
27924             for (var i =0; i < ans.length;i++) {
27925                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27926                     // select it..
27927                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27928                     break;
27929                 }
27930             }
27931         }
27932         
27933         
27934         
27935         // hides menus... - so this cant be on a menu...
27936         Roo.bootstrap.MenuMgr.hideAll();
27937         */
27938         Roo.bootstrap.MenuMgr.hideAll();
27939         //this.editorsyncValue();
27940     },
27941     onFirstFocus: function() {
27942         this.buttons.each(function(item){
27943            item.enable();
27944         });
27945     },
27946     toggleSourceEdit : function(sourceEditMode){
27947         
27948           
27949         if(sourceEditMode){
27950             Roo.log("disabling buttons");
27951            this.buttons.each( function(item){
27952                 if(item.cmd != 'pencil'){
27953                     item.disable();
27954                 }
27955             });
27956           
27957         }else{
27958             Roo.log("enabling buttons");
27959             if(this.editorcore.initialized){
27960                 this.buttons.each( function(item){
27961                     item.enable();
27962                 });
27963             }
27964             
27965         }
27966         Roo.log("calling toggole on editor");
27967         // tell the editor that it's been pressed..
27968         this.editor.toggleSourceEdit(sourceEditMode);
27969        
27970     }
27971 });
27972
27973
27974
27975
27976  
27977 /*
27978  * - LGPL
27979  */
27980
27981 /**
27982  * @class Roo.bootstrap.Markdown
27983  * @extends Roo.bootstrap.TextArea
27984  * Bootstrap Showdown editable area
27985  * @cfg {string} content
27986  * 
27987  * @constructor
27988  * Create a new Showdown
27989  */
27990
27991 Roo.bootstrap.Markdown = function(config){
27992     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27993    
27994 };
27995
27996 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27997     
27998     editing :false,
27999     
28000     initEvents : function()
28001     {
28002         
28003         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28004         this.markdownEl = this.el.createChild({
28005             cls : 'roo-markdown-area'
28006         });
28007         this.inputEl().addClass('d-none');
28008         if (this.getValue() == '') {
28009             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28010             
28011         } else {
28012             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28013         }
28014         this.markdownEl.on('click', this.toggleTextEdit, this);
28015         this.on('blur', this.toggleTextEdit, this);
28016         this.on('specialkey', this.resizeTextArea, this);
28017     },
28018     
28019     toggleTextEdit : function()
28020     {
28021         var sh = this.markdownEl.getHeight();
28022         this.inputEl().addClass('d-none');
28023         this.markdownEl.addClass('d-none');
28024         if (!this.editing) {
28025             // show editor?
28026             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28027             this.inputEl().removeClass('d-none');
28028             this.inputEl().focus();
28029             this.editing = true;
28030             return;
28031         }
28032         // show showdown...
28033         this.updateMarkdown();
28034         this.markdownEl.removeClass('d-none');
28035         this.editing = false;
28036         return;
28037     },
28038     updateMarkdown : function()
28039     {
28040         if (this.getValue() == '') {
28041             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28042             return;
28043         }
28044  
28045         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28046     },
28047     
28048     resizeTextArea: function () {
28049         
28050         var sh = 100;
28051         Roo.log([sh, this.getValue().split("\n").length * 30]);
28052         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28053     },
28054     setValue : function(val)
28055     {
28056         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28057         if (!this.editing) {
28058             this.updateMarkdown();
28059         }
28060         
28061     },
28062     focus : function()
28063     {
28064         if (!this.editing) {
28065             this.toggleTextEdit();
28066         }
28067         
28068     }
28069
28070
28071 });/*
28072  * Based on:
28073  * Ext JS Library 1.1.1
28074  * Copyright(c) 2006-2007, Ext JS, LLC.
28075  *
28076  * Originally Released Under LGPL - original licence link has changed is not relivant.
28077  *
28078  * Fork - LGPL
28079  * <script type="text/javascript">
28080  */
28081  
28082 /**
28083  * @class Roo.bootstrap.PagingToolbar
28084  * @extends Roo.bootstrap.NavSimplebar
28085  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28086  * @constructor
28087  * Create a new PagingToolbar
28088  * @param {Object} config The config object
28089  * @param {Roo.data.Store} store
28090  */
28091 Roo.bootstrap.PagingToolbar = function(config)
28092 {
28093     // old args format still supported... - xtype is prefered..
28094         // created from xtype...
28095     
28096     this.ds = config.dataSource;
28097     
28098     if (config.store && !this.ds) {
28099         this.store= Roo.factory(config.store, Roo.data);
28100         this.ds = this.store;
28101         this.ds.xmodule = this.xmodule || false;
28102     }
28103     
28104     this.toolbarItems = [];
28105     if (config.items) {
28106         this.toolbarItems = config.items;
28107     }
28108     
28109     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28110     
28111     this.cursor = 0;
28112     
28113     if (this.ds) { 
28114         this.bind(this.ds);
28115     }
28116     
28117     if (Roo.bootstrap.version == 4) {
28118         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28119     } else {
28120         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28121     }
28122     
28123 };
28124
28125 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28126     /**
28127      * @cfg {Roo.data.Store} dataSource
28128      * The underlying data store providing the paged data
28129      */
28130     /**
28131      * @cfg {String/HTMLElement/Element} container
28132      * container The id or element that will contain the toolbar
28133      */
28134     /**
28135      * @cfg {Boolean} displayInfo
28136      * True to display the displayMsg (defaults to false)
28137      */
28138     /**
28139      * @cfg {Number} pageSize
28140      * The number of records to display per page (defaults to 20)
28141      */
28142     pageSize: 20,
28143     /**
28144      * @cfg {String} displayMsg
28145      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28146      */
28147     displayMsg : 'Displaying {0} - {1} of {2}',
28148     /**
28149      * @cfg {String} emptyMsg
28150      * The message to display when no records are found (defaults to "No data to display")
28151      */
28152     emptyMsg : 'No data to display',
28153     /**
28154      * Customizable piece of the default paging text (defaults to "Page")
28155      * @type String
28156      */
28157     beforePageText : "Page",
28158     /**
28159      * Customizable piece of the default paging text (defaults to "of %0")
28160      * @type String
28161      */
28162     afterPageText : "of {0}",
28163     /**
28164      * Customizable piece of the default paging text (defaults to "First Page")
28165      * @type String
28166      */
28167     firstText : "First Page",
28168     /**
28169      * Customizable piece of the default paging text (defaults to "Previous Page")
28170      * @type String
28171      */
28172     prevText : "Previous Page",
28173     /**
28174      * Customizable piece of the default paging text (defaults to "Next Page")
28175      * @type String
28176      */
28177     nextText : "Next Page",
28178     /**
28179      * Customizable piece of the default paging text (defaults to "Last Page")
28180      * @type String
28181      */
28182     lastText : "Last Page",
28183     /**
28184      * Customizable piece of the default paging text (defaults to "Refresh")
28185      * @type String
28186      */
28187     refreshText : "Refresh",
28188
28189     buttons : false,
28190     // private
28191     onRender : function(ct, position) 
28192     {
28193         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28194         this.navgroup.parentId = this.id;
28195         this.navgroup.onRender(this.el, null);
28196         // add the buttons to the navgroup
28197         
28198         if(this.displayInfo){
28199             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28200             this.displayEl = this.el.select('.x-paging-info', true).first();
28201 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28202 //            this.displayEl = navel.el.select('span',true).first();
28203         }
28204         
28205         var _this = this;
28206         
28207         if(this.buttons){
28208             Roo.each(_this.buttons, function(e){ // this might need to use render????
28209                Roo.factory(e).render(_this.el);
28210             });
28211         }
28212             
28213         Roo.each(_this.toolbarItems, function(e) {
28214             _this.navgroup.addItem(e);
28215         });
28216         
28217         
28218         this.first = this.navgroup.addItem({
28219             tooltip: this.firstText,
28220             cls: "prev btn-outline-secondary",
28221             html : ' <i class="fa fa-step-backward"></i>',
28222             disabled: true,
28223             preventDefault: true,
28224             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28225         });
28226         
28227         this.prev =  this.navgroup.addItem({
28228             tooltip: this.prevText,
28229             cls: "prev btn-outline-secondary",
28230             html : ' <i class="fa fa-backward"></i>',
28231             disabled: true,
28232             preventDefault: true,
28233             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28234         });
28235     //this.addSeparator();
28236         
28237         
28238         var field = this.navgroup.addItem( {
28239             tagtype : 'span',
28240             cls : 'x-paging-position  btn-outline-secondary',
28241              disabled: true,
28242             html : this.beforePageText  +
28243                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28244                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28245          } ); //?? escaped?
28246         
28247         this.field = field.el.select('input', true).first();
28248         this.field.on("keydown", this.onPagingKeydown, this);
28249         this.field.on("focus", function(){this.dom.select();});
28250     
28251     
28252         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28253         //this.field.setHeight(18);
28254         //this.addSeparator();
28255         this.next = this.navgroup.addItem({
28256             tooltip: this.nextText,
28257             cls: "next btn-outline-secondary",
28258             html : ' <i class="fa fa-forward"></i>',
28259             disabled: true,
28260             preventDefault: true,
28261             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28262         });
28263         this.last = this.navgroup.addItem({
28264             tooltip: this.lastText,
28265             html : ' <i class="fa fa-step-forward"></i>',
28266             cls: "next btn-outline-secondary",
28267             disabled: true,
28268             preventDefault: true,
28269             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28270         });
28271     //this.addSeparator();
28272         this.loading = this.navgroup.addItem({
28273             tooltip: this.refreshText,
28274             cls: "btn-outline-secondary",
28275             html : ' <i class="fa fa-refresh"></i>',
28276             preventDefault: true,
28277             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28278         });
28279         
28280     },
28281
28282     // private
28283     updateInfo : function(){
28284         if(this.displayEl){
28285             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28286             var msg = count == 0 ?
28287                 this.emptyMsg :
28288                 String.format(
28289                     this.displayMsg,
28290                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28291                 );
28292             this.displayEl.update(msg);
28293         }
28294     },
28295
28296     // private
28297     onLoad : function(ds, r, o)
28298     {
28299         this.cursor = o.params && o.params.start ? o.params.start : 0;
28300         
28301         var d = this.getPageData(),
28302             ap = d.activePage,
28303             ps = d.pages;
28304         
28305         
28306         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28307         this.field.dom.value = ap;
28308         this.first.setDisabled(ap == 1);
28309         this.prev.setDisabled(ap == 1);
28310         this.next.setDisabled(ap == ps);
28311         this.last.setDisabled(ap == ps);
28312         this.loading.enable();
28313         this.updateInfo();
28314     },
28315
28316     // private
28317     getPageData : function(){
28318         var total = this.ds.getTotalCount();
28319         return {
28320             total : total,
28321             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28322             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28323         };
28324     },
28325
28326     // private
28327     onLoadError : function(){
28328         this.loading.enable();
28329     },
28330
28331     // private
28332     onPagingKeydown : function(e){
28333         var k = e.getKey();
28334         var d = this.getPageData();
28335         if(k == e.RETURN){
28336             var v = this.field.dom.value, pageNum;
28337             if(!v || isNaN(pageNum = parseInt(v, 10))){
28338                 this.field.dom.value = d.activePage;
28339                 return;
28340             }
28341             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28342             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28343             e.stopEvent();
28344         }
28345         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))
28346         {
28347           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28348           this.field.dom.value = pageNum;
28349           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28350           e.stopEvent();
28351         }
28352         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28353         {
28354           var v = this.field.dom.value, pageNum; 
28355           var increment = (e.shiftKey) ? 10 : 1;
28356           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28357                 increment *= -1;
28358           }
28359           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28360             this.field.dom.value = d.activePage;
28361             return;
28362           }
28363           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28364           {
28365             this.field.dom.value = parseInt(v, 10) + increment;
28366             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28367             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28368           }
28369           e.stopEvent();
28370         }
28371     },
28372
28373     // private
28374     beforeLoad : function(){
28375         if(this.loading){
28376             this.loading.disable();
28377         }
28378     },
28379
28380     // private
28381     onClick : function(which){
28382         
28383         var ds = this.ds;
28384         if (!ds) {
28385             return;
28386         }
28387         
28388         switch(which){
28389             case "first":
28390                 ds.load({params:{start: 0, limit: this.pageSize}});
28391             break;
28392             case "prev":
28393                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28394             break;
28395             case "next":
28396                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28397             break;
28398             case "last":
28399                 var total = ds.getTotalCount();
28400                 var extra = total % this.pageSize;
28401                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28402                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28403             break;
28404             case "refresh":
28405                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28406             break;
28407         }
28408     },
28409
28410     /**
28411      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28412      * @param {Roo.data.Store} store The data store to unbind
28413      */
28414     unbind : function(ds){
28415         ds.un("beforeload", this.beforeLoad, this);
28416         ds.un("load", this.onLoad, this);
28417         ds.un("loadexception", this.onLoadError, this);
28418         ds.un("remove", this.updateInfo, this);
28419         ds.un("add", this.updateInfo, this);
28420         this.ds = undefined;
28421     },
28422
28423     /**
28424      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28425      * @param {Roo.data.Store} store The data store to bind
28426      */
28427     bind : function(ds){
28428         ds.on("beforeload", this.beforeLoad, this);
28429         ds.on("load", this.onLoad, this);
28430         ds.on("loadexception", this.onLoadError, this);
28431         ds.on("remove", this.updateInfo, this);
28432         ds.on("add", this.updateInfo, this);
28433         this.ds = ds;
28434     }
28435 });/*
28436  * - LGPL
28437  *
28438  * element
28439  * 
28440  */
28441
28442 /**
28443  * @class Roo.bootstrap.MessageBar
28444  * @extends Roo.bootstrap.Component
28445  * Bootstrap MessageBar class
28446  * @cfg {String} html contents of the MessageBar
28447  * @cfg {String} weight (info | success | warning | danger) default info
28448  * @cfg {String} beforeClass insert the bar before the given class
28449  * @cfg {Boolean} closable (true | false) default false
28450  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28451  * 
28452  * @constructor
28453  * Create a new Element
28454  * @param {Object} config The config object
28455  */
28456
28457 Roo.bootstrap.MessageBar = function(config){
28458     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28459 };
28460
28461 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28462     
28463     html: '',
28464     weight: 'info',
28465     closable: false,
28466     fixed: false,
28467     beforeClass: 'bootstrap-sticky-wrap',
28468     
28469     getAutoCreate : function(){
28470         
28471         var cfg = {
28472             tag: 'div',
28473             cls: 'alert alert-dismissable alert-' + this.weight,
28474             cn: [
28475                 {
28476                     tag: 'span',
28477                     cls: 'message',
28478                     html: this.html || ''
28479                 }
28480             ]
28481         };
28482         
28483         if(this.fixed){
28484             cfg.cls += ' alert-messages-fixed';
28485         }
28486         
28487         if(this.closable){
28488             cfg.cn.push({
28489                 tag: 'button',
28490                 cls: 'close',
28491                 html: 'x'
28492             });
28493         }
28494         
28495         return cfg;
28496     },
28497     
28498     onRender : function(ct, position)
28499     {
28500         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28501         
28502         if(!this.el){
28503             var cfg = Roo.apply({},  this.getAutoCreate());
28504             cfg.id = Roo.id();
28505             
28506             if (this.cls) {
28507                 cfg.cls += ' ' + this.cls;
28508             }
28509             if (this.style) {
28510                 cfg.style = this.style;
28511             }
28512             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28513             
28514             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28515         }
28516         
28517         this.el.select('>button.close').on('click', this.hide, this);
28518         
28519     },
28520     
28521     show : function()
28522     {
28523         if (!this.rendered) {
28524             this.render();
28525         }
28526         
28527         this.el.show();
28528         
28529         this.fireEvent('show', this);
28530         
28531     },
28532     
28533     hide : function()
28534     {
28535         if (!this.rendered) {
28536             this.render();
28537         }
28538         
28539         this.el.hide();
28540         
28541         this.fireEvent('hide', this);
28542     },
28543     
28544     update : function()
28545     {
28546 //        var e = this.el.dom.firstChild;
28547 //        
28548 //        if(this.closable){
28549 //            e = e.nextSibling;
28550 //        }
28551 //        
28552 //        e.data = this.html || '';
28553
28554         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28555     }
28556    
28557 });
28558
28559  
28560
28561      /*
28562  * - LGPL
28563  *
28564  * Graph
28565  * 
28566  */
28567
28568
28569 /**
28570  * @class Roo.bootstrap.Graph
28571  * @extends Roo.bootstrap.Component
28572  * Bootstrap Graph class
28573 > Prameters
28574  -sm {number} sm 4
28575  -md {number} md 5
28576  @cfg {String} graphtype  bar | vbar | pie
28577  @cfg {number} g_x coodinator | centre x (pie)
28578  @cfg {number} g_y coodinator | centre y (pie)
28579  @cfg {number} g_r radius (pie)
28580  @cfg {number} g_height height of the chart (respected by all elements in the set)
28581  @cfg {number} g_width width of the chart (respected by all elements in the set)
28582  @cfg {Object} title The title of the chart
28583     
28584  -{Array}  values
28585  -opts (object) options for the chart 
28586      o {
28587      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28588      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28589      o vgutter (number)
28590      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.
28591      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28592      o to
28593      o stretch (boolean)
28594      o }
28595  -opts (object) options for the pie
28596      o{
28597      o cut
28598      o startAngle (number)
28599      o endAngle (number)
28600      } 
28601  *
28602  * @constructor
28603  * Create a new Input
28604  * @param {Object} config The config object
28605  */
28606
28607 Roo.bootstrap.Graph = function(config){
28608     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28609     
28610     this.addEvents({
28611         // img events
28612         /**
28613          * @event click
28614          * The img click event for the img.
28615          * @param {Roo.EventObject} e
28616          */
28617         "click" : true
28618     });
28619 };
28620
28621 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28622     
28623     sm: 4,
28624     md: 5,
28625     graphtype: 'bar',
28626     g_height: 250,
28627     g_width: 400,
28628     g_x: 50,
28629     g_y: 50,
28630     g_r: 30,
28631     opts:{
28632         //g_colors: this.colors,
28633         g_type: 'soft',
28634         g_gutter: '20%'
28635
28636     },
28637     title : false,
28638
28639     getAutoCreate : function(){
28640         
28641         var cfg = {
28642             tag: 'div',
28643             html : null
28644         };
28645         
28646         
28647         return  cfg;
28648     },
28649
28650     onRender : function(ct,position){
28651         
28652         
28653         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28654         
28655         if (typeof(Raphael) == 'undefined') {
28656             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28657             return;
28658         }
28659         
28660         this.raphael = Raphael(this.el.dom);
28661         
28662                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28663                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28664                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28665                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28666                 /*
28667                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28668                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28669                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28670                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28671                 
28672                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28673                 r.barchart(330, 10, 300, 220, data1);
28674                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28675                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28676                 */
28677                 
28678                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28679                 // r.barchart(30, 30, 560, 250,  xdata, {
28680                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28681                 //     axis : "0 0 1 1",
28682                 //     axisxlabels :  xdata
28683                 //     //yvalues : cols,
28684                    
28685                 // });
28686 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28687 //        
28688 //        this.load(null,xdata,{
28689 //                axis : "0 0 1 1",
28690 //                axisxlabels :  xdata
28691 //                });
28692
28693     },
28694
28695     load : function(graphtype,xdata,opts)
28696     {
28697         this.raphael.clear();
28698         if(!graphtype) {
28699             graphtype = this.graphtype;
28700         }
28701         if(!opts){
28702             opts = this.opts;
28703         }
28704         var r = this.raphael,
28705             fin = function () {
28706                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28707             },
28708             fout = function () {
28709                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28710             },
28711             pfin = function() {
28712                 this.sector.stop();
28713                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28714
28715                 if (this.label) {
28716                     this.label[0].stop();
28717                     this.label[0].attr({ r: 7.5 });
28718                     this.label[1].attr({ "font-weight": 800 });
28719                 }
28720             },
28721             pfout = function() {
28722                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28723
28724                 if (this.label) {
28725                     this.label[0].animate({ r: 5 }, 500, "bounce");
28726                     this.label[1].attr({ "font-weight": 400 });
28727                 }
28728             };
28729
28730         switch(graphtype){
28731             case 'bar':
28732                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28733                 break;
28734             case 'hbar':
28735                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28736                 break;
28737             case 'pie':
28738 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28739 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28740 //            
28741                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28742                 
28743                 break;
28744
28745         }
28746         
28747         if(this.title){
28748             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28749         }
28750         
28751     },
28752     
28753     setTitle: function(o)
28754     {
28755         this.title = o;
28756     },
28757     
28758     initEvents: function() {
28759         
28760         if(!this.href){
28761             this.el.on('click', this.onClick, this);
28762         }
28763     },
28764     
28765     onClick : function(e)
28766     {
28767         Roo.log('img onclick');
28768         this.fireEvent('click', this, e);
28769     }
28770    
28771 });
28772
28773  
28774 /*
28775  * - LGPL
28776  *
28777  * numberBox
28778  * 
28779  */
28780 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28781
28782 /**
28783  * @class Roo.bootstrap.dash.NumberBox
28784  * @extends Roo.bootstrap.Component
28785  * Bootstrap NumberBox class
28786  * @cfg {String} headline Box headline
28787  * @cfg {String} content Box content
28788  * @cfg {String} icon Box icon
28789  * @cfg {String} footer Footer text
28790  * @cfg {String} fhref Footer href
28791  * 
28792  * @constructor
28793  * Create a new NumberBox
28794  * @param {Object} config The config object
28795  */
28796
28797
28798 Roo.bootstrap.dash.NumberBox = function(config){
28799     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28800     
28801 };
28802
28803 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28804     
28805     headline : '',
28806     content : '',
28807     icon : '',
28808     footer : '',
28809     fhref : '',
28810     ficon : '',
28811     
28812     getAutoCreate : function(){
28813         
28814         var cfg = {
28815             tag : 'div',
28816             cls : 'small-box ',
28817             cn : [
28818                 {
28819                     tag : 'div',
28820                     cls : 'inner',
28821                     cn :[
28822                         {
28823                             tag : 'h3',
28824                             cls : 'roo-headline',
28825                             html : this.headline
28826                         },
28827                         {
28828                             tag : 'p',
28829                             cls : 'roo-content',
28830                             html : this.content
28831                         }
28832                     ]
28833                 }
28834             ]
28835         };
28836         
28837         if(this.icon){
28838             cfg.cn.push({
28839                 tag : 'div',
28840                 cls : 'icon',
28841                 cn :[
28842                     {
28843                         tag : 'i',
28844                         cls : 'ion ' + this.icon
28845                     }
28846                 ]
28847             });
28848         }
28849         
28850         if(this.footer){
28851             var footer = {
28852                 tag : 'a',
28853                 cls : 'small-box-footer',
28854                 href : this.fhref || '#',
28855                 html : this.footer
28856             };
28857             
28858             cfg.cn.push(footer);
28859             
28860         }
28861         
28862         return  cfg;
28863     },
28864
28865     onRender : function(ct,position){
28866         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28867
28868
28869        
28870                 
28871     },
28872
28873     setHeadline: function (value)
28874     {
28875         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28876     },
28877     
28878     setFooter: function (value, href)
28879     {
28880         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28881         
28882         if(href){
28883             this.el.select('a.small-box-footer',true).first().attr('href', href);
28884         }
28885         
28886     },
28887
28888     setContent: function (value)
28889     {
28890         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28891     },
28892
28893     initEvents: function() 
28894     {   
28895         
28896     }
28897     
28898 });
28899
28900  
28901 /*
28902  * - LGPL
28903  *
28904  * TabBox
28905  * 
28906  */
28907 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28908
28909 /**
28910  * @class Roo.bootstrap.dash.TabBox
28911  * @extends Roo.bootstrap.Component
28912  * Bootstrap TabBox class
28913  * @cfg {String} title Title of the TabBox
28914  * @cfg {String} icon Icon of the TabBox
28915  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28916  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28917  * 
28918  * @constructor
28919  * Create a new TabBox
28920  * @param {Object} config The config object
28921  */
28922
28923
28924 Roo.bootstrap.dash.TabBox = function(config){
28925     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28926     this.addEvents({
28927         // raw events
28928         /**
28929          * @event addpane
28930          * When a pane is added
28931          * @param {Roo.bootstrap.dash.TabPane} pane
28932          */
28933         "addpane" : true,
28934         /**
28935          * @event activatepane
28936          * When a pane is activated
28937          * @param {Roo.bootstrap.dash.TabPane} pane
28938          */
28939         "activatepane" : true
28940         
28941          
28942     });
28943     
28944     this.panes = [];
28945 };
28946
28947 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28948
28949     title : '',
28950     icon : false,
28951     showtabs : true,
28952     tabScrollable : false,
28953     
28954     getChildContainer : function()
28955     {
28956         return this.el.select('.tab-content', true).first();
28957     },
28958     
28959     getAutoCreate : function(){
28960         
28961         var header = {
28962             tag: 'li',
28963             cls: 'pull-left header',
28964             html: this.title,
28965             cn : []
28966         };
28967         
28968         if(this.icon){
28969             header.cn.push({
28970                 tag: 'i',
28971                 cls: 'fa ' + this.icon
28972             });
28973         }
28974         
28975         var h = {
28976             tag: 'ul',
28977             cls: 'nav nav-tabs pull-right',
28978             cn: [
28979                 header
28980             ]
28981         };
28982         
28983         if(this.tabScrollable){
28984             h = {
28985                 tag: 'div',
28986                 cls: 'tab-header',
28987                 cn: [
28988                     {
28989                         tag: 'ul',
28990                         cls: 'nav nav-tabs pull-right',
28991                         cn: [
28992                             header
28993                         ]
28994                     }
28995                 ]
28996             };
28997         }
28998         
28999         var cfg = {
29000             tag: 'div',
29001             cls: 'nav-tabs-custom',
29002             cn: [
29003                 h,
29004                 {
29005                     tag: 'div',
29006                     cls: 'tab-content no-padding',
29007                     cn: []
29008                 }
29009             ]
29010         };
29011
29012         return  cfg;
29013     },
29014     initEvents : function()
29015     {
29016         //Roo.log('add add pane handler');
29017         this.on('addpane', this.onAddPane, this);
29018     },
29019      /**
29020      * Updates the box title
29021      * @param {String} html to set the title to.
29022      */
29023     setTitle : function(value)
29024     {
29025         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29026     },
29027     onAddPane : function(pane)
29028     {
29029         this.panes.push(pane);
29030         //Roo.log('addpane');
29031         //Roo.log(pane);
29032         // tabs are rendere left to right..
29033         if(!this.showtabs){
29034             return;
29035         }
29036         
29037         var ctr = this.el.select('.nav-tabs', true).first();
29038          
29039          
29040         var existing = ctr.select('.nav-tab',true);
29041         var qty = existing.getCount();;
29042         
29043         
29044         var tab = ctr.createChild({
29045             tag : 'li',
29046             cls : 'nav-tab' + (qty ? '' : ' active'),
29047             cn : [
29048                 {
29049                     tag : 'a',
29050                     href:'#',
29051                     html : pane.title
29052                 }
29053             ]
29054         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29055         pane.tab = tab;
29056         
29057         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29058         if (!qty) {
29059             pane.el.addClass('active');
29060         }
29061         
29062                 
29063     },
29064     onTabClick : function(ev,un,ob,pane)
29065     {
29066         //Roo.log('tab - prev default');
29067         ev.preventDefault();
29068         
29069         
29070         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29071         pane.tab.addClass('active');
29072         //Roo.log(pane.title);
29073         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29074         // technically we should have a deactivate event.. but maybe add later.
29075         // and it should not de-activate the selected tab...
29076         this.fireEvent('activatepane', pane);
29077         pane.el.addClass('active');
29078         pane.fireEvent('activate');
29079         
29080         
29081     },
29082     
29083     getActivePane : function()
29084     {
29085         var r = false;
29086         Roo.each(this.panes, function(p) {
29087             if(p.el.hasClass('active')){
29088                 r = p;
29089                 return false;
29090             }
29091             
29092             return;
29093         });
29094         
29095         return r;
29096     }
29097     
29098     
29099 });
29100
29101  
29102 /*
29103  * - LGPL
29104  *
29105  * Tab pane
29106  * 
29107  */
29108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29109 /**
29110  * @class Roo.bootstrap.TabPane
29111  * @extends Roo.bootstrap.Component
29112  * Bootstrap TabPane class
29113  * @cfg {Boolean} active (false | true) Default false
29114  * @cfg {String} title title of panel
29115
29116  * 
29117  * @constructor
29118  * Create a new TabPane
29119  * @param {Object} config The config object
29120  */
29121
29122 Roo.bootstrap.dash.TabPane = function(config){
29123     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29124     
29125     this.addEvents({
29126         // raw events
29127         /**
29128          * @event activate
29129          * When a pane is activated
29130          * @param {Roo.bootstrap.dash.TabPane} pane
29131          */
29132         "activate" : true
29133          
29134     });
29135 };
29136
29137 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29138     
29139     active : false,
29140     title : '',
29141     
29142     // the tabBox that this is attached to.
29143     tab : false,
29144      
29145     getAutoCreate : function() 
29146     {
29147         var cfg = {
29148             tag: 'div',
29149             cls: 'tab-pane'
29150         };
29151         
29152         if(this.active){
29153             cfg.cls += ' active';
29154         }
29155         
29156         return cfg;
29157     },
29158     initEvents  : function()
29159     {
29160         //Roo.log('trigger add pane handler');
29161         this.parent().fireEvent('addpane', this)
29162     },
29163     
29164      /**
29165      * Updates the tab title 
29166      * @param {String} html to set the title to.
29167      */
29168     setTitle: function(str)
29169     {
29170         if (!this.tab) {
29171             return;
29172         }
29173         this.title = str;
29174         this.tab.select('a', true).first().dom.innerHTML = str;
29175         
29176     }
29177     
29178     
29179     
29180 });
29181
29182  
29183
29184
29185  /*
29186  * - LGPL
29187  *
29188  * menu
29189  * 
29190  */
29191 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29192
29193 /**
29194  * @class Roo.bootstrap.menu.Menu
29195  * @extends Roo.bootstrap.Component
29196  * Bootstrap Menu class - container for Menu
29197  * @cfg {String} html Text of the menu
29198  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29199  * @cfg {String} icon Font awesome icon
29200  * @cfg {String} pos Menu align to (top | bottom) default bottom
29201  * 
29202  * 
29203  * @constructor
29204  * Create a new Menu
29205  * @param {Object} config The config object
29206  */
29207
29208
29209 Roo.bootstrap.menu.Menu = function(config){
29210     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29211     
29212     this.addEvents({
29213         /**
29214          * @event beforeshow
29215          * Fires before this menu is displayed
29216          * @param {Roo.bootstrap.menu.Menu} this
29217          */
29218         beforeshow : true,
29219         /**
29220          * @event beforehide
29221          * Fires before this menu is hidden
29222          * @param {Roo.bootstrap.menu.Menu} this
29223          */
29224         beforehide : true,
29225         /**
29226          * @event show
29227          * Fires after this menu is displayed
29228          * @param {Roo.bootstrap.menu.Menu} this
29229          */
29230         show : true,
29231         /**
29232          * @event hide
29233          * Fires after this menu is hidden
29234          * @param {Roo.bootstrap.menu.Menu} this
29235          */
29236         hide : true,
29237         /**
29238          * @event click
29239          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29240          * @param {Roo.bootstrap.menu.Menu} this
29241          * @param {Roo.EventObject} e
29242          */
29243         click : true
29244     });
29245     
29246 };
29247
29248 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29249     
29250     submenu : false,
29251     html : '',
29252     weight : 'default',
29253     icon : false,
29254     pos : 'bottom',
29255     
29256     
29257     getChildContainer : function() {
29258         if(this.isSubMenu){
29259             return this.el;
29260         }
29261         
29262         return this.el.select('ul.dropdown-menu', true).first();  
29263     },
29264     
29265     getAutoCreate : function()
29266     {
29267         var text = [
29268             {
29269                 tag : 'span',
29270                 cls : 'roo-menu-text',
29271                 html : this.html
29272             }
29273         ];
29274         
29275         if(this.icon){
29276             text.unshift({
29277                 tag : 'i',
29278                 cls : 'fa ' + this.icon
29279             })
29280         }
29281         
29282         
29283         var cfg = {
29284             tag : 'div',
29285             cls : 'btn-group',
29286             cn : [
29287                 {
29288                     tag : 'button',
29289                     cls : 'dropdown-button btn btn-' + this.weight,
29290                     cn : text
29291                 },
29292                 {
29293                     tag : 'button',
29294                     cls : 'dropdown-toggle btn btn-' + this.weight,
29295                     cn : [
29296                         {
29297                             tag : 'span',
29298                             cls : 'caret'
29299                         }
29300                     ]
29301                 },
29302                 {
29303                     tag : 'ul',
29304                     cls : 'dropdown-menu'
29305                 }
29306             ]
29307             
29308         };
29309         
29310         if(this.pos == 'top'){
29311             cfg.cls += ' dropup';
29312         }
29313         
29314         if(this.isSubMenu){
29315             cfg = {
29316                 tag : 'ul',
29317                 cls : 'dropdown-menu'
29318             }
29319         }
29320         
29321         return cfg;
29322     },
29323     
29324     onRender : function(ct, position)
29325     {
29326         this.isSubMenu = ct.hasClass('dropdown-submenu');
29327         
29328         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29329     },
29330     
29331     initEvents : function() 
29332     {
29333         if(this.isSubMenu){
29334             return;
29335         }
29336         
29337         this.hidden = true;
29338         
29339         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29340         this.triggerEl.on('click', this.onTriggerPress, this);
29341         
29342         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29343         this.buttonEl.on('click', this.onClick, this);
29344         
29345     },
29346     
29347     list : function()
29348     {
29349         if(this.isSubMenu){
29350             return this.el;
29351         }
29352         
29353         return this.el.select('ul.dropdown-menu', true).first();
29354     },
29355     
29356     onClick : function(e)
29357     {
29358         this.fireEvent("click", this, e);
29359     },
29360     
29361     onTriggerPress  : function(e)
29362     {   
29363         if (this.isVisible()) {
29364             this.hide();
29365         } else {
29366             this.show();
29367         }
29368     },
29369     
29370     isVisible : function(){
29371         return !this.hidden;
29372     },
29373     
29374     show : function()
29375     {
29376         this.fireEvent("beforeshow", this);
29377         
29378         this.hidden = false;
29379         this.el.addClass('open');
29380         
29381         Roo.get(document).on("mouseup", this.onMouseUp, this);
29382         
29383         this.fireEvent("show", this);
29384         
29385         
29386     },
29387     
29388     hide : function()
29389     {
29390         this.fireEvent("beforehide", this);
29391         
29392         this.hidden = true;
29393         this.el.removeClass('open');
29394         
29395         Roo.get(document).un("mouseup", this.onMouseUp);
29396         
29397         this.fireEvent("hide", this);
29398     },
29399     
29400     onMouseUp : function()
29401     {
29402         this.hide();
29403     }
29404     
29405 });
29406
29407  
29408  /*
29409  * - LGPL
29410  *
29411  * menu item
29412  * 
29413  */
29414 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29415
29416 /**
29417  * @class Roo.bootstrap.menu.Item
29418  * @extends Roo.bootstrap.Component
29419  * Bootstrap MenuItem class
29420  * @cfg {Boolean} submenu (true | false) default false
29421  * @cfg {String} html text of the item
29422  * @cfg {String} href the link
29423  * @cfg {Boolean} disable (true | false) default false
29424  * @cfg {Boolean} preventDefault (true | false) default true
29425  * @cfg {String} icon Font awesome icon
29426  * @cfg {String} pos Submenu align to (left | right) default right 
29427  * 
29428  * 
29429  * @constructor
29430  * Create a new Item
29431  * @param {Object} config The config object
29432  */
29433
29434
29435 Roo.bootstrap.menu.Item = function(config){
29436     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29437     this.addEvents({
29438         /**
29439          * @event mouseover
29440          * Fires when the mouse is hovering over this menu
29441          * @param {Roo.bootstrap.menu.Item} this
29442          * @param {Roo.EventObject} e
29443          */
29444         mouseover : true,
29445         /**
29446          * @event mouseout
29447          * Fires when the mouse exits this menu
29448          * @param {Roo.bootstrap.menu.Item} this
29449          * @param {Roo.EventObject} e
29450          */
29451         mouseout : true,
29452         // raw events
29453         /**
29454          * @event click
29455          * The raw click event for the entire grid.
29456          * @param {Roo.EventObject} e
29457          */
29458         click : true
29459     });
29460 };
29461
29462 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29463     
29464     submenu : false,
29465     href : '',
29466     html : '',
29467     preventDefault: true,
29468     disable : false,
29469     icon : false,
29470     pos : 'right',
29471     
29472     getAutoCreate : function()
29473     {
29474         var text = [
29475             {
29476                 tag : 'span',
29477                 cls : 'roo-menu-item-text',
29478                 html : this.html
29479             }
29480         ];
29481         
29482         if(this.icon){
29483             text.unshift({
29484                 tag : 'i',
29485                 cls : 'fa ' + this.icon
29486             })
29487         }
29488         
29489         var cfg = {
29490             tag : 'li',
29491             cn : [
29492                 {
29493                     tag : 'a',
29494                     href : this.href || '#',
29495                     cn : text
29496                 }
29497             ]
29498         };
29499         
29500         if(this.disable){
29501             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29502         }
29503         
29504         if(this.submenu){
29505             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29506             
29507             if(this.pos == 'left'){
29508                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29509             }
29510         }
29511         
29512         return cfg;
29513     },
29514     
29515     initEvents : function() 
29516     {
29517         this.el.on('mouseover', this.onMouseOver, this);
29518         this.el.on('mouseout', this.onMouseOut, this);
29519         
29520         this.el.select('a', true).first().on('click', this.onClick, this);
29521         
29522     },
29523     
29524     onClick : function(e)
29525     {
29526         if(this.preventDefault){
29527             e.preventDefault();
29528         }
29529         
29530         this.fireEvent("click", this, e);
29531     },
29532     
29533     onMouseOver : function(e)
29534     {
29535         if(this.submenu && this.pos == 'left'){
29536             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29537         }
29538         
29539         this.fireEvent("mouseover", this, e);
29540     },
29541     
29542     onMouseOut : function(e)
29543     {
29544         this.fireEvent("mouseout", this, e);
29545     }
29546 });
29547
29548  
29549
29550  /*
29551  * - LGPL
29552  *
29553  * menu separator
29554  * 
29555  */
29556 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29557
29558 /**
29559  * @class Roo.bootstrap.menu.Separator
29560  * @extends Roo.bootstrap.Component
29561  * Bootstrap Separator class
29562  * 
29563  * @constructor
29564  * Create a new Separator
29565  * @param {Object} config The config object
29566  */
29567
29568
29569 Roo.bootstrap.menu.Separator = function(config){
29570     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29571 };
29572
29573 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29574     
29575     getAutoCreate : function(){
29576         var cfg = {
29577             tag : 'li',
29578             cls: 'dropdown-divider divider'
29579         };
29580         
29581         return cfg;
29582     }
29583    
29584 });
29585
29586  
29587
29588  /*
29589  * - LGPL
29590  *
29591  * Tooltip
29592  * 
29593  */
29594
29595 /**
29596  * @class Roo.bootstrap.Tooltip
29597  * Bootstrap Tooltip class
29598  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29599  * to determine which dom element triggers the tooltip.
29600  * 
29601  * It needs to add support for additional attributes like tooltip-position
29602  * 
29603  * @constructor
29604  * Create a new Toolti
29605  * @param {Object} config The config object
29606  */
29607
29608 Roo.bootstrap.Tooltip = function(config){
29609     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29610     
29611     this.alignment = Roo.bootstrap.Tooltip.alignment;
29612     
29613     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29614         this.alignment = config.alignment;
29615     }
29616     
29617 };
29618
29619 Roo.apply(Roo.bootstrap.Tooltip, {
29620     /**
29621      * @function init initialize tooltip monitoring.
29622      * @static
29623      */
29624     currentEl : false,
29625     currentTip : false,
29626     currentRegion : false,
29627     
29628     //  init : delay?
29629     
29630     init : function()
29631     {
29632         Roo.get(document).on('mouseover', this.enter ,this);
29633         Roo.get(document).on('mouseout', this.leave, this);
29634          
29635         
29636         this.currentTip = new Roo.bootstrap.Tooltip();
29637     },
29638     
29639     enter : function(ev)
29640     {
29641         var dom = ev.getTarget();
29642         
29643         //Roo.log(['enter',dom]);
29644         var el = Roo.fly(dom);
29645         if (this.currentEl) {
29646             //Roo.log(dom);
29647             //Roo.log(this.currentEl);
29648             //Roo.log(this.currentEl.contains(dom));
29649             if (this.currentEl == el) {
29650                 return;
29651             }
29652             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29653                 return;
29654             }
29655
29656         }
29657         
29658         if (this.currentTip.el) {
29659             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29660         }    
29661         //Roo.log(ev);
29662         
29663         if(!el || el.dom == document){
29664             return;
29665         }
29666         
29667         var bindEl = el; 
29668         var pel = false;
29669         if (!el.attr('tooltip')) {
29670             pel = el.findParent("[tooltip]");
29671             if (pel) {
29672                 bindEl = Roo.get(pel);
29673             }
29674         }
29675         
29676        
29677         
29678         // you can not look for children, as if el is the body.. then everythign is the child..
29679         if (!pel && !el.attr('tooltip')) { //
29680             if (!el.select("[tooltip]").elements.length) {
29681                 return;
29682             }
29683             // is the mouse over this child...?
29684             bindEl = el.select("[tooltip]").first();
29685             var xy = ev.getXY();
29686             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29687                 //Roo.log("not in region.");
29688                 return;
29689             }
29690             //Roo.log("child element over..");
29691             
29692         }
29693         this.currentEl = el;
29694         this.currentTip.bind(bindEl);
29695         this.currentRegion = Roo.lib.Region.getRegion(dom);
29696         this.currentTip.enter();
29697         
29698     },
29699     leave : function(ev)
29700     {
29701         var dom = ev.getTarget();
29702         //Roo.log(['leave',dom]);
29703         if (!this.currentEl) {
29704             return;
29705         }
29706         
29707         
29708         if (dom != this.currentEl.dom) {
29709             return;
29710         }
29711         var xy = ev.getXY();
29712         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29713             return;
29714         }
29715         // only activate leave if mouse cursor is outside... bounding box..
29716         
29717         
29718         
29719         
29720         if (this.currentTip) {
29721             this.currentTip.leave();
29722         }
29723         //Roo.log('clear currentEl');
29724         this.currentEl = false;
29725         
29726         
29727     },
29728     alignment : {
29729         'left' : ['r-l', [-2,0], 'right'],
29730         'right' : ['l-r', [2,0], 'left'],
29731         'bottom' : ['t-b', [0,2], 'top'],
29732         'top' : [ 'b-t', [0,-2], 'bottom']
29733     }
29734     
29735 });
29736
29737
29738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29739     
29740     
29741     bindEl : false,
29742     
29743     delay : null, // can be { show : 300 , hide: 500}
29744     
29745     timeout : null,
29746     
29747     hoverState : null, //???
29748     
29749     placement : 'bottom', 
29750     
29751     alignment : false,
29752     
29753     getAutoCreate : function(){
29754     
29755         var cfg = {
29756            cls : 'tooltip',   
29757            role : 'tooltip',
29758            cn : [
29759                 {
29760                     cls : 'tooltip-arrow arrow'
29761                 },
29762                 {
29763                     cls : 'tooltip-inner'
29764                 }
29765            ]
29766         };
29767         
29768         return cfg;
29769     },
29770     bind : function(el)
29771     {
29772         this.bindEl = el;
29773     },
29774     
29775     initEvents : function()
29776     {
29777         this.arrowEl = this.el.select('.arrow', true).first();
29778         this.innerEl = this.el.select('.tooltip-inner', true).first();
29779     },
29780     
29781     enter : function () {
29782        
29783         if (this.timeout != null) {
29784             clearTimeout(this.timeout);
29785         }
29786         
29787         this.hoverState = 'in';
29788          //Roo.log("enter - show");
29789         if (!this.delay || !this.delay.show) {
29790             this.show();
29791             return;
29792         }
29793         var _t = this;
29794         this.timeout = setTimeout(function () {
29795             if (_t.hoverState == 'in') {
29796                 _t.show();
29797             }
29798         }, this.delay.show);
29799     },
29800     leave : function()
29801     {
29802         clearTimeout(this.timeout);
29803     
29804         this.hoverState = 'out';
29805          if (!this.delay || !this.delay.hide) {
29806             this.hide();
29807             return;
29808         }
29809        
29810         var _t = this;
29811         this.timeout = setTimeout(function () {
29812             //Roo.log("leave - timeout");
29813             
29814             if (_t.hoverState == 'out') {
29815                 _t.hide();
29816                 Roo.bootstrap.Tooltip.currentEl = false;
29817             }
29818         }, delay);
29819     },
29820     
29821     show : function (msg)
29822     {
29823         if (!this.el) {
29824             this.render(document.body);
29825         }
29826         // set content.
29827         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29828         
29829         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29830         
29831         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29832         
29833         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29834                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29835         
29836         var placement = typeof this.placement == 'function' ?
29837             this.placement.call(this, this.el, on_el) :
29838             this.placement;
29839             
29840         var autoToken = /\s?auto?\s?/i;
29841         var autoPlace = autoToken.test(placement);
29842         if (autoPlace) {
29843             placement = placement.replace(autoToken, '') || 'top';
29844         }
29845         
29846         //this.el.detach()
29847         //this.el.setXY([0,0]);
29848         this.el.show();
29849         //this.el.dom.style.display='block';
29850         
29851         //this.el.appendTo(on_el);
29852         
29853         var p = this.getPosition();
29854         var box = this.el.getBox();
29855         
29856         if (autoPlace) {
29857             // fixme..
29858         }
29859         
29860         var align = this.alignment[placement];
29861         
29862         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29863         
29864         if(placement == 'top' || placement == 'bottom'){
29865             if(xy[0] < 0){
29866                 placement = 'right';
29867             }
29868             
29869             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29870                 placement = 'left';
29871             }
29872             
29873             var scroll = Roo.select('body', true).first().getScroll();
29874             
29875             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29876                 placement = 'top';
29877             }
29878             
29879             align = this.alignment[placement];
29880             
29881             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29882             
29883         }
29884         
29885         var elems = document.getElementsByTagName('div');
29886         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29887         for (var i = 0; i < elems.length; i++) {
29888           var zindex = Number.parseInt(
29889                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29890                 10
29891           );
29892           if (zindex > highest) {
29893             highest = zindex;
29894           }
29895         }
29896         
29897         
29898         
29899         this.el.dom.style.zIndex = highest;
29900         
29901         this.el.alignTo(this.bindEl, align[0],align[1]);
29902         //var arrow = this.el.select('.arrow',true).first();
29903         //arrow.set(align[2], 
29904         
29905         this.el.addClass(placement);
29906         this.el.addClass("bs-tooltip-"+ placement);
29907         
29908         this.el.addClass('in fade show');
29909         
29910         this.hoverState = null;
29911         
29912         if (this.el.hasClass('fade')) {
29913             // fade it?
29914         }
29915         
29916         
29917         
29918         
29919         
29920     },
29921     hide : function()
29922     {
29923          
29924         if (!this.el) {
29925             return;
29926         }
29927         //this.el.setXY([0,0]);
29928         this.el.removeClass(['show', 'in']);
29929         //this.el.hide();
29930         
29931     }
29932     
29933 });
29934  
29935
29936  /*
29937  * - LGPL
29938  *
29939  * Location Picker
29940  * 
29941  */
29942
29943 /**
29944  * @class Roo.bootstrap.LocationPicker
29945  * @extends Roo.bootstrap.Component
29946  * Bootstrap LocationPicker class
29947  * @cfg {Number} latitude Position when init default 0
29948  * @cfg {Number} longitude Position when init default 0
29949  * @cfg {Number} zoom default 15
29950  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29951  * @cfg {Boolean} mapTypeControl default false
29952  * @cfg {Boolean} disableDoubleClickZoom default false
29953  * @cfg {Boolean} scrollwheel default true
29954  * @cfg {Boolean} streetViewControl default false
29955  * @cfg {Number} radius default 0
29956  * @cfg {String} locationName
29957  * @cfg {Boolean} draggable default true
29958  * @cfg {Boolean} enableAutocomplete default false
29959  * @cfg {Boolean} enableReverseGeocode default true
29960  * @cfg {String} markerTitle
29961  * 
29962  * @constructor
29963  * Create a new LocationPicker
29964  * @param {Object} config The config object
29965  */
29966
29967
29968 Roo.bootstrap.LocationPicker = function(config){
29969     
29970     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29971     
29972     this.addEvents({
29973         /**
29974          * @event initial
29975          * Fires when the picker initialized.
29976          * @param {Roo.bootstrap.LocationPicker} this
29977          * @param {Google Location} location
29978          */
29979         initial : true,
29980         /**
29981          * @event positionchanged
29982          * Fires when the picker position changed.
29983          * @param {Roo.bootstrap.LocationPicker} this
29984          * @param {Google Location} location
29985          */
29986         positionchanged : true,
29987         /**
29988          * @event resize
29989          * Fires when the map resize.
29990          * @param {Roo.bootstrap.LocationPicker} this
29991          */
29992         resize : true,
29993         /**
29994          * @event show
29995          * Fires when the map show.
29996          * @param {Roo.bootstrap.LocationPicker} this
29997          */
29998         show : true,
29999         /**
30000          * @event hide
30001          * Fires when the map hide.
30002          * @param {Roo.bootstrap.LocationPicker} this
30003          */
30004         hide : true,
30005         /**
30006          * @event mapClick
30007          * Fires when click the map.
30008          * @param {Roo.bootstrap.LocationPicker} this
30009          * @param {Map event} e
30010          */
30011         mapClick : true,
30012         /**
30013          * @event mapRightClick
30014          * Fires when right click the map.
30015          * @param {Roo.bootstrap.LocationPicker} this
30016          * @param {Map event} e
30017          */
30018         mapRightClick : true,
30019         /**
30020          * @event markerClick
30021          * Fires when click the marker.
30022          * @param {Roo.bootstrap.LocationPicker} this
30023          * @param {Map event} e
30024          */
30025         markerClick : true,
30026         /**
30027          * @event markerRightClick
30028          * Fires when right click the marker.
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          * @param {Map event} e
30031          */
30032         markerRightClick : true,
30033         /**
30034          * @event OverlayViewDraw
30035          * Fires when OverlayView Draw
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          */
30038         OverlayViewDraw : true,
30039         /**
30040          * @event OverlayViewOnAdd
30041          * Fires when OverlayView Draw
30042          * @param {Roo.bootstrap.LocationPicker} this
30043          */
30044         OverlayViewOnAdd : true,
30045         /**
30046          * @event OverlayViewOnRemove
30047          * Fires when OverlayView Draw
30048          * @param {Roo.bootstrap.LocationPicker} this
30049          */
30050         OverlayViewOnRemove : true,
30051         /**
30052          * @event OverlayViewShow
30053          * Fires when OverlayView Draw
30054          * @param {Roo.bootstrap.LocationPicker} this
30055          * @param {Pixel} cpx
30056          */
30057         OverlayViewShow : true,
30058         /**
30059          * @event OverlayViewHide
30060          * Fires when OverlayView Draw
30061          * @param {Roo.bootstrap.LocationPicker} this
30062          */
30063         OverlayViewHide : true,
30064         /**
30065          * @event loadexception
30066          * Fires when load google lib failed.
30067          * @param {Roo.bootstrap.LocationPicker} this
30068          */
30069         loadexception : true
30070     });
30071         
30072 };
30073
30074 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30075     
30076     gMapContext: false,
30077     
30078     latitude: 0,
30079     longitude: 0,
30080     zoom: 15,
30081     mapTypeId: false,
30082     mapTypeControl: false,
30083     disableDoubleClickZoom: false,
30084     scrollwheel: true,
30085     streetViewControl: false,
30086     radius: 0,
30087     locationName: '',
30088     draggable: true,
30089     enableAutocomplete: false,
30090     enableReverseGeocode: true,
30091     markerTitle: '',
30092     
30093     getAutoCreate: function()
30094     {
30095
30096         var cfg = {
30097             tag: 'div',
30098             cls: 'roo-location-picker'
30099         };
30100         
30101         return cfg
30102     },
30103     
30104     initEvents: function(ct, position)
30105     {       
30106         if(!this.el.getWidth() || this.isApplied()){
30107             return;
30108         }
30109         
30110         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30111         
30112         this.initial();
30113     },
30114     
30115     initial: function()
30116     {
30117         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30118             this.fireEvent('loadexception', this);
30119             return;
30120         }
30121         
30122         if(!this.mapTypeId){
30123             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30124         }
30125         
30126         this.gMapContext = this.GMapContext();
30127         
30128         this.initOverlayView();
30129         
30130         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30131         
30132         var _this = this;
30133                 
30134         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30135             _this.setPosition(_this.gMapContext.marker.position);
30136         });
30137         
30138         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30139             _this.fireEvent('mapClick', this, event);
30140             
30141         });
30142
30143         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30144             _this.fireEvent('mapRightClick', this, event);
30145             
30146         });
30147         
30148         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30149             _this.fireEvent('markerClick', this, event);
30150             
30151         });
30152
30153         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30154             _this.fireEvent('markerRightClick', this, event);
30155             
30156         });
30157         
30158         this.setPosition(this.gMapContext.location);
30159         
30160         this.fireEvent('initial', this, this.gMapContext.location);
30161     },
30162     
30163     initOverlayView: function()
30164     {
30165         var _this = this;
30166         
30167         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30168             
30169             draw: function()
30170             {
30171                 _this.fireEvent('OverlayViewDraw', _this);
30172             },
30173             
30174             onAdd: function()
30175             {
30176                 _this.fireEvent('OverlayViewOnAdd', _this);
30177             },
30178             
30179             onRemove: function()
30180             {
30181                 _this.fireEvent('OverlayViewOnRemove', _this);
30182             },
30183             
30184             show: function(cpx)
30185             {
30186                 _this.fireEvent('OverlayViewShow', _this, cpx);
30187             },
30188             
30189             hide: function()
30190             {
30191                 _this.fireEvent('OverlayViewHide', _this);
30192             }
30193             
30194         });
30195     },
30196     
30197     fromLatLngToContainerPixel: function(event)
30198     {
30199         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30200     },
30201     
30202     isApplied: function() 
30203     {
30204         return this.getGmapContext() == false ? false : true;
30205     },
30206     
30207     getGmapContext: function() 
30208     {
30209         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30210     },
30211     
30212     GMapContext: function() 
30213     {
30214         var position = new google.maps.LatLng(this.latitude, this.longitude);
30215         
30216         var _map = new google.maps.Map(this.el.dom, {
30217             center: position,
30218             zoom: this.zoom,
30219             mapTypeId: this.mapTypeId,
30220             mapTypeControl: this.mapTypeControl,
30221             disableDoubleClickZoom: this.disableDoubleClickZoom,
30222             scrollwheel: this.scrollwheel,
30223             streetViewControl: this.streetViewControl,
30224             locationName: this.locationName,
30225             draggable: this.draggable,
30226             enableAutocomplete: this.enableAutocomplete,
30227             enableReverseGeocode: this.enableReverseGeocode
30228         });
30229         
30230         var _marker = new google.maps.Marker({
30231             position: position,
30232             map: _map,
30233             title: this.markerTitle,
30234             draggable: this.draggable
30235         });
30236         
30237         return {
30238             map: _map,
30239             marker: _marker,
30240             circle: null,
30241             location: position,
30242             radius: this.radius,
30243             locationName: this.locationName,
30244             addressComponents: {
30245                 formatted_address: null,
30246                 addressLine1: null,
30247                 addressLine2: null,
30248                 streetName: null,
30249                 streetNumber: null,
30250                 city: null,
30251                 district: null,
30252                 state: null,
30253                 stateOrProvince: null
30254             },
30255             settings: this,
30256             domContainer: this.el.dom,
30257             geodecoder: new google.maps.Geocoder()
30258         };
30259     },
30260     
30261     drawCircle: function(center, radius, options) 
30262     {
30263         if (this.gMapContext.circle != null) {
30264             this.gMapContext.circle.setMap(null);
30265         }
30266         if (radius > 0) {
30267             radius *= 1;
30268             options = Roo.apply({}, options, {
30269                 strokeColor: "#0000FF",
30270                 strokeOpacity: .35,
30271                 strokeWeight: 2,
30272                 fillColor: "#0000FF",
30273                 fillOpacity: .2
30274             });
30275             
30276             options.map = this.gMapContext.map;
30277             options.radius = radius;
30278             options.center = center;
30279             this.gMapContext.circle = new google.maps.Circle(options);
30280             return this.gMapContext.circle;
30281         }
30282         
30283         return null;
30284     },
30285     
30286     setPosition: function(location) 
30287     {
30288         this.gMapContext.location = location;
30289         this.gMapContext.marker.setPosition(location);
30290         this.gMapContext.map.panTo(location);
30291         this.drawCircle(location, this.gMapContext.radius, {});
30292         
30293         var _this = this;
30294         
30295         if (this.gMapContext.settings.enableReverseGeocode) {
30296             this.gMapContext.geodecoder.geocode({
30297                 latLng: this.gMapContext.location
30298             }, function(results, status) {
30299                 
30300                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30301                     _this.gMapContext.locationName = results[0].formatted_address;
30302                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30303                     
30304                     _this.fireEvent('positionchanged', this, location);
30305                 }
30306             });
30307             
30308             return;
30309         }
30310         
30311         this.fireEvent('positionchanged', this, location);
30312     },
30313     
30314     resize: function()
30315     {
30316         google.maps.event.trigger(this.gMapContext.map, "resize");
30317         
30318         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30319         
30320         this.fireEvent('resize', this);
30321     },
30322     
30323     setPositionByLatLng: function(latitude, longitude)
30324     {
30325         this.setPosition(new google.maps.LatLng(latitude, longitude));
30326     },
30327     
30328     getCurrentPosition: function() 
30329     {
30330         return {
30331             latitude: this.gMapContext.location.lat(),
30332             longitude: this.gMapContext.location.lng()
30333         };
30334     },
30335     
30336     getAddressName: function() 
30337     {
30338         return this.gMapContext.locationName;
30339     },
30340     
30341     getAddressComponents: function() 
30342     {
30343         return this.gMapContext.addressComponents;
30344     },
30345     
30346     address_component_from_google_geocode: function(address_components) 
30347     {
30348         var result = {};
30349         
30350         for (var i = 0; i < address_components.length; i++) {
30351             var component = address_components[i];
30352             if (component.types.indexOf("postal_code") >= 0) {
30353                 result.postalCode = component.short_name;
30354             } else if (component.types.indexOf("street_number") >= 0) {
30355                 result.streetNumber = component.short_name;
30356             } else if (component.types.indexOf("route") >= 0) {
30357                 result.streetName = component.short_name;
30358             } else if (component.types.indexOf("neighborhood") >= 0) {
30359                 result.city = component.short_name;
30360             } else if (component.types.indexOf("locality") >= 0) {
30361                 result.city = component.short_name;
30362             } else if (component.types.indexOf("sublocality") >= 0) {
30363                 result.district = component.short_name;
30364             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30365                 result.stateOrProvince = component.short_name;
30366             } else if (component.types.indexOf("country") >= 0) {
30367                 result.country = component.short_name;
30368             }
30369         }
30370         
30371         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30372         result.addressLine2 = "";
30373         return result;
30374     },
30375     
30376     setZoomLevel: function(zoom)
30377     {
30378         this.gMapContext.map.setZoom(zoom);
30379     },
30380     
30381     show: function()
30382     {
30383         if(!this.el){
30384             return;
30385         }
30386         
30387         this.el.show();
30388         
30389         this.resize();
30390         
30391         this.fireEvent('show', this);
30392     },
30393     
30394     hide: function()
30395     {
30396         if(!this.el){
30397             return;
30398         }
30399         
30400         this.el.hide();
30401         
30402         this.fireEvent('hide', this);
30403     }
30404     
30405 });
30406
30407 Roo.apply(Roo.bootstrap.LocationPicker, {
30408     
30409     OverlayView : function(map, options)
30410     {
30411         options = options || {};
30412         
30413         this.setMap(map);
30414     }
30415     
30416     
30417 });/**
30418  * @class Roo.bootstrap.Alert
30419  * @extends Roo.bootstrap.Component
30420  * Bootstrap Alert class - shows an alert area box
30421  * eg
30422  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30423   Enter a valid email address
30424 </div>
30425  * @licence LGPL
30426  * @cfg {String} title The title of alert
30427  * @cfg {String} html The content of alert
30428  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30429  * @cfg {String} fa font-awesomeicon
30430  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30431  * @cfg {Boolean} close true to show a x closer
30432  * 
30433  * 
30434  * @constructor
30435  * Create a new alert
30436  * @param {Object} config The config object
30437  */
30438
30439
30440 Roo.bootstrap.Alert = function(config){
30441     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30442     
30443 };
30444
30445 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30446     
30447     title: '',
30448     html: '',
30449     weight: false,
30450     fa: false,
30451     faicon: false, // BC
30452     close : false,
30453     
30454     
30455     getAutoCreate : function()
30456     {
30457         
30458         var cfg = {
30459             tag : 'div',
30460             cls : 'alert',
30461             cn : [
30462                 {
30463                     tag: 'button',
30464                     type :  "button",
30465                     cls: "close",
30466                     html : '×',
30467                     style : this.close ? '' : 'display:none'
30468                 },
30469                 {
30470                     tag : 'i',
30471                     cls : 'roo-alert-icon'
30472                     
30473                 },
30474                 {
30475                     tag : 'b',
30476                     cls : 'roo-alert-title',
30477                     html : this.title
30478                 },
30479                 {
30480                     tag : 'span',
30481                     cls : 'roo-alert-text',
30482                     html : this.html
30483                 }
30484             ]
30485         };
30486         
30487         if(this.faicon){
30488             cfg.cn[0].cls += ' fa ' + this.faicon;
30489         }
30490         if(this.fa){
30491             cfg.cn[0].cls += ' fa ' + this.fa;
30492         }
30493         
30494         if(this.weight){
30495             cfg.cls += ' alert-' + this.weight;
30496         }
30497         
30498         return cfg;
30499     },
30500     
30501     initEvents: function() 
30502     {
30503         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30504         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30505         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30506         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30507         if (this.seconds > 0) {
30508             this.hide.defer(this.seconds, this);
30509         }
30510     },
30511     /**
30512      * Set the Title Message HTML
30513      * @param {String} html
30514      */
30515     setTitle : function(str)
30516     {
30517         this.titleEl.dom.innerHTML = str;
30518     },
30519      
30520      /**
30521      * Set the Body Message HTML
30522      * @param {String} html
30523      */
30524     setHtml : function(str)
30525     {
30526         this.htmlEl.dom.innerHTML = str;
30527     },
30528     /**
30529      * Set the Weight of the alert
30530      * @param {String} (success|info|warning|danger) weight
30531      */
30532     
30533     setWeight : function(weight)
30534     {
30535         if(this.weight){
30536             this.el.removeClass('alert-' + this.weight);
30537         }
30538         
30539         this.weight = weight;
30540         
30541         this.el.addClass('alert-' + this.weight);
30542     },
30543       /**
30544      * Set the Icon of the alert
30545      * @param {String} see fontawsome names (name without the 'fa-' bit)
30546      */
30547     setIcon : function(icon)
30548     {
30549         if(this.faicon){
30550             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30551         }
30552         
30553         this.faicon = icon;
30554         
30555         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30556     },
30557     /**
30558      * Hide the Alert
30559      */
30560     hide: function() 
30561     {
30562         this.el.hide();   
30563     },
30564     /**
30565      * Show the Alert
30566      */
30567     show: function() 
30568     {  
30569         this.el.show();   
30570     }
30571     
30572 });
30573
30574  
30575 /*
30576 * Licence: LGPL
30577 */
30578
30579 /**
30580  * @class Roo.bootstrap.UploadCropbox
30581  * @extends Roo.bootstrap.Component
30582  * Bootstrap UploadCropbox class
30583  * @cfg {String} emptyText show when image has been loaded
30584  * @cfg {String} rotateNotify show when image too small to rotate
30585  * @cfg {Number} errorTimeout default 3000
30586  * @cfg {Number} minWidth default 300
30587  * @cfg {Number} minHeight default 300
30588  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30589  * @cfg {Boolean} isDocument (true|false) default false
30590  * @cfg {String} url action url
30591  * @cfg {String} paramName default 'imageUpload'
30592  * @cfg {String} method default POST
30593  * @cfg {Boolean} loadMask (true|false) default true
30594  * @cfg {Boolean} loadingText default 'Loading...'
30595  * 
30596  * @constructor
30597  * Create a new UploadCropbox
30598  * @param {Object} config The config object
30599  */
30600
30601 Roo.bootstrap.UploadCropbox = function(config){
30602     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30603     
30604     this.addEvents({
30605         /**
30606          * @event beforeselectfile
30607          * Fire before select file
30608          * @param {Roo.bootstrap.UploadCropbox} this
30609          */
30610         "beforeselectfile" : true,
30611         /**
30612          * @event initial
30613          * Fire after initEvent
30614          * @param {Roo.bootstrap.UploadCropbox} this
30615          */
30616         "initial" : true,
30617         /**
30618          * @event crop
30619          * Fire after initEvent
30620          * @param {Roo.bootstrap.UploadCropbox} this
30621          * @param {String} data
30622          */
30623         "crop" : true,
30624         /**
30625          * @event prepare
30626          * Fire when preparing the file data
30627          * @param {Roo.bootstrap.UploadCropbox} this
30628          * @param {Object} file
30629          */
30630         "prepare" : true,
30631         /**
30632          * @event exception
30633          * Fire when get exception
30634          * @param {Roo.bootstrap.UploadCropbox} this
30635          * @param {XMLHttpRequest} xhr
30636          */
30637         "exception" : true,
30638         /**
30639          * @event beforeloadcanvas
30640          * Fire before load the canvas
30641          * @param {Roo.bootstrap.UploadCropbox} this
30642          * @param {String} src
30643          */
30644         "beforeloadcanvas" : true,
30645         /**
30646          * @event trash
30647          * Fire when trash image
30648          * @param {Roo.bootstrap.UploadCropbox} this
30649          */
30650         "trash" : true,
30651         /**
30652          * @event download
30653          * Fire when download the image
30654          * @param {Roo.bootstrap.UploadCropbox} this
30655          */
30656         "download" : true,
30657         /**
30658          * @event footerbuttonclick
30659          * Fire when footerbuttonclick
30660          * @param {Roo.bootstrap.UploadCropbox} this
30661          * @param {String} type
30662          */
30663         "footerbuttonclick" : true,
30664         /**
30665          * @event resize
30666          * Fire when resize
30667          * @param {Roo.bootstrap.UploadCropbox} this
30668          */
30669         "resize" : true,
30670         /**
30671          * @event rotate
30672          * Fire when rotate the image
30673          * @param {Roo.bootstrap.UploadCropbox} this
30674          * @param {String} pos
30675          */
30676         "rotate" : true,
30677         /**
30678          * @event inspect
30679          * Fire when inspect the file
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          * @param {Object} file
30682          */
30683         "inspect" : true,
30684         /**
30685          * @event upload
30686          * Fire when xhr upload the file
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {Object} data
30689          */
30690         "upload" : true,
30691         /**
30692          * @event arrange
30693          * Fire when arrange the file data
30694          * @param {Roo.bootstrap.UploadCropbox} this
30695          * @param {Object} formData
30696          */
30697         "arrange" : true
30698     });
30699     
30700     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30701 };
30702
30703 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30704     
30705     emptyText : 'Click to upload image',
30706     rotateNotify : 'Image is too small to rotate',
30707     errorTimeout : 3000,
30708     scale : 0,
30709     baseScale : 1,
30710     rotate : 0,
30711     dragable : false,
30712     pinching : false,
30713     mouseX : 0,
30714     mouseY : 0,
30715     cropData : false,
30716     minWidth : 300,
30717     minHeight : 300,
30718     file : false,
30719     exif : {},
30720     baseRotate : 1,
30721     cropType : 'image/jpeg',
30722     buttons : false,
30723     canvasLoaded : false,
30724     isDocument : false,
30725     method : 'POST',
30726     paramName : 'imageUpload',
30727     loadMask : true,
30728     loadingText : 'Loading...',
30729     maskEl : false,
30730     
30731     getAutoCreate : function()
30732     {
30733         var cfg = {
30734             tag : 'div',
30735             cls : 'roo-upload-cropbox',
30736             cn : [
30737                 {
30738                     tag : 'input',
30739                     cls : 'roo-upload-cropbox-selector',
30740                     type : 'file'
30741                 },
30742                 {
30743                     tag : 'div',
30744                     cls : 'roo-upload-cropbox-body',
30745                     style : 'cursor:pointer',
30746                     cn : [
30747                         {
30748                             tag : 'div',
30749                             cls : 'roo-upload-cropbox-preview'
30750                         },
30751                         {
30752                             tag : 'div',
30753                             cls : 'roo-upload-cropbox-thumb'
30754                         },
30755                         {
30756                             tag : 'div',
30757                             cls : 'roo-upload-cropbox-empty-notify',
30758                             html : this.emptyText
30759                         },
30760                         {
30761                             tag : 'div',
30762                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30763                             html : this.rotateNotify
30764                         }
30765                     ]
30766                 },
30767                 {
30768                     tag : 'div',
30769                     cls : 'roo-upload-cropbox-footer',
30770                     cn : {
30771                         tag : 'div',
30772                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30773                         cn : []
30774                     }
30775                 }
30776             ]
30777         };
30778         
30779         return cfg;
30780     },
30781     
30782     onRender : function(ct, position)
30783     {
30784         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30785         
30786         if (this.buttons.length) {
30787             
30788             Roo.each(this.buttons, function(bb) {
30789                 
30790                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30791                 
30792                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30793                 
30794             }, this);
30795         }
30796         
30797         if(this.loadMask){
30798             this.maskEl = this.el;
30799         }
30800     },
30801     
30802     initEvents : function()
30803     {
30804         this.urlAPI = (window.createObjectURL && window) || 
30805                                 (window.URL && URL.revokeObjectURL && URL) || 
30806                                 (window.webkitURL && webkitURL);
30807                         
30808         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30809         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30810         
30811         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30812         this.selectorEl.hide();
30813         
30814         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30815         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30816         
30817         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30818         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819         this.thumbEl.hide();
30820         
30821         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30822         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823         
30824         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30825         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826         this.errorEl.hide();
30827         
30828         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30829         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30830         this.footerEl.hide();
30831         
30832         this.setThumbBoxSize();
30833         
30834         this.bind();
30835         
30836         this.resize();
30837         
30838         this.fireEvent('initial', this);
30839     },
30840
30841     bind : function()
30842     {
30843         var _this = this;
30844         
30845         window.addEventListener("resize", function() { _this.resize(); } );
30846         
30847         this.bodyEl.on('click', this.beforeSelectFile, this);
30848         
30849         if(Roo.isTouch){
30850             this.bodyEl.on('touchstart', this.onTouchStart, this);
30851             this.bodyEl.on('touchmove', this.onTouchMove, this);
30852             this.bodyEl.on('touchend', this.onTouchEnd, this);
30853         }
30854         
30855         if(!Roo.isTouch){
30856             this.bodyEl.on('mousedown', this.onMouseDown, this);
30857             this.bodyEl.on('mousemove', this.onMouseMove, this);
30858             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30859             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30860             Roo.get(document).on('mouseup', this.onMouseUp, this);
30861         }
30862         
30863         this.selectorEl.on('change', this.onFileSelected, this);
30864     },
30865     
30866     reset : function()
30867     {    
30868         this.scale = 0;
30869         this.baseScale = 1;
30870         this.rotate = 0;
30871         this.baseRotate = 1;
30872         this.dragable = false;
30873         this.pinching = false;
30874         this.mouseX = 0;
30875         this.mouseY = 0;
30876         this.cropData = false;
30877         this.notifyEl.dom.innerHTML = this.emptyText;
30878         
30879         this.selectorEl.dom.value = '';
30880         
30881     },
30882     
30883     resize : function()
30884     {
30885         if(this.fireEvent('resize', this) != false){
30886             this.setThumbBoxPosition();
30887             this.setCanvasPosition();
30888         }
30889     },
30890     
30891     onFooterButtonClick : function(e, el, o, type)
30892     {
30893         switch (type) {
30894             case 'rotate-left' :
30895                 this.onRotateLeft(e);
30896                 break;
30897             case 'rotate-right' :
30898                 this.onRotateRight(e);
30899                 break;
30900             case 'picture' :
30901                 this.beforeSelectFile(e);
30902                 break;
30903             case 'trash' :
30904                 this.trash(e);
30905                 break;
30906             case 'crop' :
30907                 this.crop(e);
30908                 break;
30909             case 'download' :
30910                 this.download(e);
30911                 break;
30912             default :
30913                 break;
30914         }
30915         
30916         this.fireEvent('footerbuttonclick', this, type);
30917     },
30918     
30919     beforeSelectFile : function(e)
30920     {
30921         e.preventDefault();
30922         
30923         if(this.fireEvent('beforeselectfile', this) != false){
30924             this.selectorEl.dom.click();
30925         }
30926     },
30927     
30928     onFileSelected : function(e)
30929     {
30930         e.preventDefault();
30931         
30932         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30933             return;
30934         }
30935         
30936         var file = this.selectorEl.dom.files[0];
30937         
30938         if(this.fireEvent('inspect', this, file) != false){
30939             this.prepare(file);
30940         }
30941         
30942     },
30943     
30944     trash : function(e)
30945     {
30946         this.fireEvent('trash', this);
30947     },
30948     
30949     download : function(e)
30950     {
30951         this.fireEvent('download', this);
30952     },
30953     
30954     loadCanvas : function(src)
30955     {   
30956         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30957             
30958             this.reset();
30959             
30960             this.imageEl = document.createElement('img');
30961             
30962             var _this = this;
30963             
30964             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30965             
30966             this.imageEl.src = src;
30967         }
30968     },
30969     
30970     onLoadCanvas : function()
30971     {   
30972         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30973         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30974         
30975         this.bodyEl.un('click', this.beforeSelectFile, this);
30976         
30977         this.notifyEl.hide();
30978         this.thumbEl.show();
30979         this.footerEl.show();
30980         
30981         this.baseRotateLevel();
30982         
30983         if(this.isDocument){
30984             this.setThumbBoxSize();
30985         }
30986         
30987         this.setThumbBoxPosition();
30988         
30989         this.baseScaleLevel();
30990         
30991         this.draw();
30992         
30993         this.resize();
30994         
30995         this.canvasLoaded = true;
30996         
30997         if(this.loadMask){
30998             this.maskEl.unmask();
30999         }
31000         
31001     },
31002     
31003     setCanvasPosition : function()
31004     {   
31005         if(!this.canvasEl){
31006             return;
31007         }
31008         
31009         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31010         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31011         
31012         this.previewEl.setLeft(pw);
31013         this.previewEl.setTop(ph);
31014         
31015     },
31016     
31017     onMouseDown : function(e)
31018     {   
31019         e.stopEvent();
31020         
31021         this.dragable = true;
31022         this.pinching = false;
31023         
31024         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31025             this.dragable = false;
31026             return;
31027         }
31028         
31029         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31030         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31031         
31032     },
31033     
31034     onMouseMove : function(e)
31035     {   
31036         e.stopEvent();
31037         
31038         if(!this.canvasLoaded){
31039             return;
31040         }
31041         
31042         if (!this.dragable){
31043             return;
31044         }
31045         
31046         var minX = Math.ceil(this.thumbEl.getLeft(true));
31047         var minY = Math.ceil(this.thumbEl.getTop(true));
31048         
31049         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31050         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31051         
31052         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31053         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31054         
31055         x = x - this.mouseX;
31056         y = y - this.mouseY;
31057         
31058         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31059         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31060         
31061         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31062         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31063         
31064         this.previewEl.setLeft(bgX);
31065         this.previewEl.setTop(bgY);
31066         
31067         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31068         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31069     },
31070     
31071     onMouseUp : function(e)
31072     {   
31073         e.stopEvent();
31074         
31075         this.dragable = false;
31076     },
31077     
31078     onMouseWheel : function(e)
31079     {   
31080         e.stopEvent();
31081         
31082         this.startScale = this.scale;
31083         
31084         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31085         
31086         if(!this.zoomable()){
31087             this.scale = this.startScale;
31088             return;
31089         }
31090         
31091         this.draw();
31092         
31093         return;
31094     },
31095     
31096     zoomable : function()
31097     {
31098         var minScale = this.thumbEl.getWidth() / this.minWidth;
31099         
31100         if(this.minWidth < this.minHeight){
31101             minScale = this.thumbEl.getHeight() / this.minHeight;
31102         }
31103         
31104         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31105         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31106         
31107         if(
31108                 this.isDocument &&
31109                 (this.rotate == 0 || this.rotate == 180) && 
31110                 (
31111                     width > this.imageEl.OriginWidth || 
31112                     height > this.imageEl.OriginHeight ||
31113                     (width < this.minWidth && height < this.minHeight)
31114                 )
31115         ){
31116             return false;
31117         }
31118         
31119         if(
31120                 this.isDocument &&
31121                 (this.rotate == 90 || this.rotate == 270) && 
31122                 (
31123                     width > this.imageEl.OriginWidth || 
31124                     height > this.imageEl.OriginHeight ||
31125                     (width < this.minHeight && height < this.minWidth)
31126                 )
31127         ){
31128             return false;
31129         }
31130         
31131         if(
31132                 !this.isDocument &&
31133                 (this.rotate == 0 || this.rotate == 180) && 
31134                 (
31135                     width < this.minWidth || 
31136                     width > this.imageEl.OriginWidth || 
31137                     height < this.minHeight || 
31138                     height > this.imageEl.OriginHeight
31139                 )
31140         ){
31141             return false;
31142         }
31143         
31144         if(
31145                 !this.isDocument &&
31146                 (this.rotate == 90 || this.rotate == 270) && 
31147                 (
31148                     width < this.minHeight || 
31149                     width > this.imageEl.OriginWidth || 
31150                     height < this.minWidth || 
31151                     height > this.imageEl.OriginHeight
31152                 )
31153         ){
31154             return false;
31155         }
31156         
31157         return true;
31158         
31159     },
31160     
31161     onRotateLeft : function(e)
31162     {   
31163         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31164             
31165             var minScale = this.thumbEl.getWidth() / this.minWidth;
31166             
31167             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31168             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31169             
31170             this.startScale = this.scale;
31171             
31172             while (this.getScaleLevel() < minScale){
31173             
31174                 this.scale = this.scale + 1;
31175                 
31176                 if(!this.zoomable()){
31177                     break;
31178                 }
31179                 
31180                 if(
31181                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31182                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31183                 ){
31184                     continue;
31185                 }
31186                 
31187                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31188
31189                 this.draw();
31190                 
31191                 return;
31192             }
31193             
31194             this.scale = this.startScale;
31195             
31196             this.onRotateFail();
31197             
31198             return false;
31199         }
31200         
31201         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31202
31203         if(this.isDocument){
31204             this.setThumbBoxSize();
31205             this.setThumbBoxPosition();
31206             this.setCanvasPosition();
31207         }
31208         
31209         this.draw();
31210         
31211         this.fireEvent('rotate', this, 'left');
31212         
31213     },
31214     
31215     onRotateRight : function(e)
31216     {
31217         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31218             
31219             var minScale = this.thumbEl.getWidth() / this.minWidth;
31220         
31221             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31222             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31223             
31224             this.startScale = this.scale;
31225             
31226             while (this.getScaleLevel() < minScale){
31227             
31228                 this.scale = this.scale + 1;
31229                 
31230                 if(!this.zoomable()){
31231                     break;
31232                 }
31233                 
31234                 if(
31235                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31236                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31237                 ){
31238                     continue;
31239                 }
31240                 
31241                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31242
31243                 this.draw();
31244                 
31245                 return;
31246             }
31247             
31248             this.scale = this.startScale;
31249             
31250             this.onRotateFail();
31251             
31252             return false;
31253         }
31254         
31255         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31256
31257         if(this.isDocument){
31258             this.setThumbBoxSize();
31259             this.setThumbBoxPosition();
31260             this.setCanvasPosition();
31261         }
31262         
31263         this.draw();
31264         
31265         this.fireEvent('rotate', this, 'right');
31266     },
31267     
31268     onRotateFail : function()
31269     {
31270         this.errorEl.show(true);
31271         
31272         var _this = this;
31273         
31274         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31275     },
31276     
31277     draw : function()
31278     {
31279         this.previewEl.dom.innerHTML = '';
31280         
31281         var canvasEl = document.createElement("canvas");
31282         
31283         var contextEl = canvasEl.getContext("2d");
31284         
31285         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31286         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31287         var center = this.imageEl.OriginWidth / 2;
31288         
31289         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31290             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31291             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31292             center = this.imageEl.OriginHeight / 2;
31293         }
31294         
31295         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31296         
31297         contextEl.translate(center, center);
31298         contextEl.rotate(this.rotate * Math.PI / 180);
31299
31300         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31301         
31302         this.canvasEl = document.createElement("canvas");
31303         
31304         this.contextEl = this.canvasEl.getContext("2d");
31305         
31306         switch (this.rotate) {
31307             case 0 :
31308                 
31309                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31310                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31311                 
31312                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31313                 
31314                 break;
31315             case 90 : 
31316                 
31317                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31318                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31319                 
31320                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31321                     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);
31322                     break;
31323                 }
31324                 
31325                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326                 
31327                 break;
31328             case 180 :
31329                 
31330                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31331                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31332                 
31333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31334                     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);
31335                     break;
31336                 }
31337                 
31338                 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);
31339                 
31340                 break;
31341             case 270 :
31342                 
31343                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31344                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31345         
31346                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31347                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31348                     break;
31349                 }
31350                 
31351                 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);
31352                 
31353                 break;
31354             default : 
31355                 break;
31356         }
31357         
31358         this.previewEl.appendChild(this.canvasEl);
31359         
31360         this.setCanvasPosition();
31361     },
31362     
31363     crop : function()
31364     {
31365         if(!this.canvasLoaded){
31366             return;
31367         }
31368         
31369         var imageCanvas = document.createElement("canvas");
31370         
31371         var imageContext = imageCanvas.getContext("2d");
31372         
31373         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31374         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31375         
31376         var center = imageCanvas.width / 2;
31377         
31378         imageContext.translate(center, center);
31379         
31380         imageContext.rotate(this.rotate * Math.PI / 180);
31381         
31382         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31383         
31384         var canvas = document.createElement("canvas");
31385         
31386         var context = canvas.getContext("2d");
31387                 
31388         canvas.width = this.minWidth;
31389         canvas.height = this.minHeight;
31390
31391         switch (this.rotate) {
31392             case 0 :
31393                 
31394                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31395                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31396                 
31397                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31398                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31399                 
31400                 var targetWidth = this.minWidth - 2 * x;
31401                 var targetHeight = this.minHeight - 2 * y;
31402                 
31403                 var scale = 1;
31404                 
31405                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31406                     scale = targetWidth / width;
31407                 }
31408                 
31409                 if(x > 0 && y == 0){
31410                     scale = targetHeight / height;
31411                 }
31412                 
31413                 if(x > 0 && y > 0){
31414                     scale = targetWidth / width;
31415                     
31416                     if(width < height){
31417                         scale = targetHeight / height;
31418                     }
31419                 }
31420                 
31421                 context.scale(scale, scale);
31422                 
31423                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31424                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31425
31426                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31427                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31428
31429                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31430                 
31431                 break;
31432             case 90 : 
31433                 
31434                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31435                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31436                 
31437                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31438                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31439                 
31440                 var targetWidth = this.minWidth - 2 * x;
31441                 var targetHeight = this.minHeight - 2 * y;
31442                 
31443                 var scale = 1;
31444                 
31445                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31446                     scale = targetWidth / width;
31447                 }
31448                 
31449                 if(x > 0 && y == 0){
31450                     scale = targetHeight / height;
31451                 }
31452                 
31453                 if(x > 0 && y > 0){
31454                     scale = targetWidth / width;
31455                     
31456                     if(width < height){
31457                         scale = targetHeight / height;
31458                     }
31459                 }
31460                 
31461                 context.scale(scale, scale);
31462                 
31463                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31464                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31465
31466                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31467                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31468                 
31469                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31470                 
31471                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31472                 
31473                 break;
31474             case 180 :
31475                 
31476                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31477                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31478                 
31479                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31480                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31481                 
31482                 var targetWidth = this.minWidth - 2 * x;
31483                 var targetHeight = this.minHeight - 2 * y;
31484                 
31485                 var scale = 1;
31486                 
31487                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31488                     scale = targetWidth / width;
31489                 }
31490                 
31491                 if(x > 0 && y == 0){
31492                     scale = targetHeight / height;
31493                 }
31494                 
31495                 if(x > 0 && y > 0){
31496                     scale = targetWidth / width;
31497                     
31498                     if(width < height){
31499                         scale = targetHeight / height;
31500                     }
31501                 }
31502                 
31503                 context.scale(scale, scale);
31504                 
31505                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31506                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31507
31508                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31509                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31510
31511                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31512                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31513                 
31514                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31515                 
31516                 break;
31517             case 270 :
31518                 
31519                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31520                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31521                 
31522                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31523                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31524                 
31525                 var targetWidth = this.minWidth - 2 * x;
31526                 var targetHeight = this.minHeight - 2 * y;
31527                 
31528                 var scale = 1;
31529                 
31530                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31531                     scale = targetWidth / width;
31532                 }
31533                 
31534                 if(x > 0 && y == 0){
31535                     scale = targetHeight / height;
31536                 }
31537                 
31538                 if(x > 0 && y > 0){
31539                     scale = targetWidth / width;
31540                     
31541                     if(width < height){
31542                         scale = targetHeight / height;
31543                     }
31544                 }
31545                 
31546                 context.scale(scale, scale);
31547                 
31548                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31549                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31550
31551                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31552                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31553                 
31554                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31555                 
31556                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31557                 
31558                 break;
31559             default : 
31560                 break;
31561         }
31562         
31563         this.cropData = canvas.toDataURL(this.cropType);
31564         
31565         if(this.fireEvent('crop', this, this.cropData) !== false){
31566             this.process(this.file, this.cropData);
31567         }
31568         
31569         return;
31570         
31571     },
31572     
31573     setThumbBoxSize : function()
31574     {
31575         var width, height;
31576         
31577         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31578             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31579             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31580             
31581             this.minWidth = width;
31582             this.minHeight = height;
31583             
31584             if(this.rotate == 90 || this.rotate == 270){
31585                 this.minWidth = height;
31586                 this.minHeight = width;
31587             }
31588         }
31589         
31590         height = 300;
31591         width = Math.ceil(this.minWidth * height / this.minHeight);
31592         
31593         if(this.minWidth > this.minHeight){
31594             width = 300;
31595             height = Math.ceil(this.minHeight * width / this.minWidth);
31596         }
31597         
31598         this.thumbEl.setStyle({
31599             width : width + 'px',
31600             height : height + 'px'
31601         });
31602
31603         return;
31604             
31605     },
31606     
31607     setThumbBoxPosition : function()
31608     {
31609         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31610         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31611         
31612         this.thumbEl.setLeft(x);
31613         this.thumbEl.setTop(y);
31614         
31615     },
31616     
31617     baseRotateLevel : function()
31618     {
31619         this.baseRotate = 1;
31620         
31621         if(
31622                 typeof(this.exif) != 'undefined' &&
31623                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31624                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31625         ){
31626             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31627         }
31628         
31629         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31630         
31631     },
31632     
31633     baseScaleLevel : function()
31634     {
31635         var width, height;
31636         
31637         if(this.isDocument){
31638             
31639             if(this.baseRotate == 6 || this.baseRotate == 8){
31640             
31641                 height = this.thumbEl.getHeight();
31642                 this.baseScale = height / this.imageEl.OriginWidth;
31643
31644                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31645                     width = this.thumbEl.getWidth();
31646                     this.baseScale = width / this.imageEl.OriginHeight;
31647                 }
31648
31649                 return;
31650             }
31651
31652             height = this.thumbEl.getHeight();
31653             this.baseScale = height / this.imageEl.OriginHeight;
31654
31655             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31656                 width = this.thumbEl.getWidth();
31657                 this.baseScale = width / this.imageEl.OriginWidth;
31658             }
31659
31660             return;
31661         }
31662         
31663         if(this.baseRotate == 6 || this.baseRotate == 8){
31664             
31665             width = this.thumbEl.getHeight();
31666             this.baseScale = width / this.imageEl.OriginHeight;
31667             
31668             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31669                 height = this.thumbEl.getWidth();
31670                 this.baseScale = height / this.imageEl.OriginHeight;
31671             }
31672             
31673             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31674                 height = this.thumbEl.getWidth();
31675                 this.baseScale = height / this.imageEl.OriginHeight;
31676                 
31677                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31678                     width = this.thumbEl.getHeight();
31679                     this.baseScale = width / this.imageEl.OriginWidth;
31680                 }
31681             }
31682             
31683             return;
31684         }
31685         
31686         width = this.thumbEl.getWidth();
31687         this.baseScale = width / this.imageEl.OriginWidth;
31688         
31689         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31690             height = this.thumbEl.getHeight();
31691             this.baseScale = height / this.imageEl.OriginHeight;
31692         }
31693         
31694         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31695             
31696             height = this.thumbEl.getHeight();
31697             this.baseScale = height / this.imageEl.OriginHeight;
31698             
31699             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31700                 width = this.thumbEl.getWidth();
31701                 this.baseScale = width / this.imageEl.OriginWidth;
31702             }
31703             
31704         }
31705         
31706         return;
31707     },
31708     
31709     getScaleLevel : function()
31710     {
31711         return this.baseScale * Math.pow(1.1, this.scale);
31712     },
31713     
31714     onTouchStart : function(e)
31715     {
31716         if(!this.canvasLoaded){
31717             this.beforeSelectFile(e);
31718             return;
31719         }
31720         
31721         var touches = e.browserEvent.touches;
31722         
31723         if(!touches){
31724             return;
31725         }
31726         
31727         if(touches.length == 1){
31728             this.onMouseDown(e);
31729             return;
31730         }
31731         
31732         if(touches.length != 2){
31733             return;
31734         }
31735         
31736         var coords = [];
31737         
31738         for(var i = 0, finger; finger = touches[i]; i++){
31739             coords.push(finger.pageX, finger.pageY);
31740         }
31741         
31742         var x = Math.pow(coords[0] - coords[2], 2);
31743         var y = Math.pow(coords[1] - coords[3], 2);
31744         
31745         this.startDistance = Math.sqrt(x + y);
31746         
31747         this.startScale = this.scale;
31748         
31749         this.pinching = true;
31750         this.dragable = false;
31751         
31752     },
31753     
31754     onTouchMove : function(e)
31755     {
31756         if(!this.pinching && !this.dragable){
31757             return;
31758         }
31759         
31760         var touches = e.browserEvent.touches;
31761         
31762         if(!touches){
31763             return;
31764         }
31765         
31766         if(this.dragable){
31767             this.onMouseMove(e);
31768             return;
31769         }
31770         
31771         var coords = [];
31772         
31773         for(var i = 0, finger; finger = touches[i]; i++){
31774             coords.push(finger.pageX, finger.pageY);
31775         }
31776         
31777         var x = Math.pow(coords[0] - coords[2], 2);
31778         var y = Math.pow(coords[1] - coords[3], 2);
31779         
31780         this.endDistance = Math.sqrt(x + y);
31781         
31782         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31783         
31784         if(!this.zoomable()){
31785             this.scale = this.startScale;
31786             return;
31787         }
31788         
31789         this.draw();
31790         
31791     },
31792     
31793     onTouchEnd : function(e)
31794     {
31795         this.pinching = false;
31796         this.dragable = false;
31797         
31798     },
31799     
31800     process : function(file, crop)
31801     {
31802         if(this.loadMask){
31803             this.maskEl.mask(this.loadingText);
31804         }
31805         
31806         this.xhr = new XMLHttpRequest();
31807         
31808         file.xhr = this.xhr;
31809
31810         this.xhr.open(this.method, this.url, true);
31811         
31812         var headers = {
31813             "Accept": "application/json",
31814             "Cache-Control": "no-cache",
31815             "X-Requested-With": "XMLHttpRequest"
31816         };
31817         
31818         for (var headerName in headers) {
31819             var headerValue = headers[headerName];
31820             if (headerValue) {
31821                 this.xhr.setRequestHeader(headerName, headerValue);
31822             }
31823         }
31824         
31825         var _this = this;
31826         
31827         this.xhr.onload = function()
31828         {
31829             _this.xhrOnLoad(_this.xhr);
31830         }
31831         
31832         this.xhr.onerror = function()
31833         {
31834             _this.xhrOnError(_this.xhr);
31835         }
31836         
31837         var formData = new FormData();
31838
31839         formData.append('returnHTML', 'NO');
31840         
31841         if(crop){
31842             formData.append('crop', crop);
31843         }
31844         
31845         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31846             formData.append(this.paramName, file, file.name);
31847         }
31848         
31849         if(typeof(file.filename) != 'undefined'){
31850             formData.append('filename', file.filename);
31851         }
31852         
31853         if(typeof(file.mimetype) != 'undefined'){
31854             formData.append('mimetype', file.mimetype);
31855         }
31856         
31857         if(this.fireEvent('arrange', this, formData) != false){
31858             this.xhr.send(formData);
31859         };
31860     },
31861     
31862     xhrOnLoad : function(xhr)
31863     {
31864         if(this.loadMask){
31865             this.maskEl.unmask();
31866         }
31867         
31868         if (xhr.readyState !== 4) {
31869             this.fireEvent('exception', this, xhr);
31870             return;
31871         }
31872
31873         var response = Roo.decode(xhr.responseText);
31874         
31875         if(!response.success){
31876             this.fireEvent('exception', this, xhr);
31877             return;
31878         }
31879         
31880         var response = Roo.decode(xhr.responseText);
31881         
31882         this.fireEvent('upload', this, response);
31883         
31884     },
31885     
31886     xhrOnError : function()
31887     {
31888         if(this.loadMask){
31889             this.maskEl.unmask();
31890         }
31891         
31892         Roo.log('xhr on error');
31893         
31894         var response = Roo.decode(xhr.responseText);
31895           
31896         Roo.log(response);
31897         
31898     },
31899     
31900     prepare : function(file)
31901     {   
31902         if(this.loadMask){
31903             this.maskEl.mask(this.loadingText);
31904         }
31905         
31906         this.file = false;
31907         this.exif = {};
31908         
31909         if(typeof(file) === 'string'){
31910             this.loadCanvas(file);
31911             return;
31912         }
31913         
31914         if(!file || !this.urlAPI){
31915             return;
31916         }
31917         
31918         this.file = file;
31919         this.cropType = file.type;
31920         
31921         var _this = this;
31922         
31923         if(this.fireEvent('prepare', this, this.file) != false){
31924             
31925             var reader = new FileReader();
31926             
31927             reader.onload = function (e) {
31928                 if (e.target.error) {
31929                     Roo.log(e.target.error);
31930                     return;
31931                 }
31932                 
31933                 var buffer = e.target.result,
31934                     dataView = new DataView(buffer),
31935                     offset = 2,
31936                     maxOffset = dataView.byteLength - 4,
31937                     markerBytes,
31938                     markerLength;
31939                 
31940                 if (dataView.getUint16(0) === 0xffd8) {
31941                     while (offset < maxOffset) {
31942                         markerBytes = dataView.getUint16(offset);
31943                         
31944                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31945                             markerLength = dataView.getUint16(offset + 2) + 2;
31946                             if (offset + markerLength > dataView.byteLength) {
31947                                 Roo.log('Invalid meta data: Invalid segment size.');
31948                                 break;
31949                             }
31950                             
31951                             if(markerBytes == 0xffe1){
31952                                 _this.parseExifData(
31953                                     dataView,
31954                                     offset,
31955                                     markerLength
31956                                 );
31957                             }
31958                             
31959                             offset += markerLength;
31960                             
31961                             continue;
31962                         }
31963                         
31964                         break;
31965                     }
31966                     
31967                 }
31968                 
31969                 var url = _this.urlAPI.createObjectURL(_this.file);
31970                 
31971                 _this.loadCanvas(url);
31972                 
31973                 return;
31974             }
31975             
31976             reader.readAsArrayBuffer(this.file);
31977             
31978         }
31979         
31980     },
31981     
31982     parseExifData : function(dataView, offset, length)
31983     {
31984         var tiffOffset = offset + 10,
31985             littleEndian,
31986             dirOffset;
31987     
31988         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31989             // No Exif data, might be XMP data instead
31990             return;
31991         }
31992         
31993         // Check for the ASCII code for "Exif" (0x45786966):
31994         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31995             // No Exif data, might be XMP data instead
31996             return;
31997         }
31998         if (tiffOffset + 8 > dataView.byteLength) {
31999             Roo.log('Invalid Exif data: Invalid segment size.');
32000             return;
32001         }
32002         // Check for the two null bytes:
32003         if (dataView.getUint16(offset + 8) !== 0x0000) {
32004             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32005             return;
32006         }
32007         // Check the byte alignment:
32008         switch (dataView.getUint16(tiffOffset)) {
32009         case 0x4949:
32010             littleEndian = true;
32011             break;
32012         case 0x4D4D:
32013             littleEndian = false;
32014             break;
32015         default:
32016             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32017             return;
32018         }
32019         // Check for the TIFF tag marker (0x002A):
32020         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32021             Roo.log('Invalid Exif data: Missing TIFF marker.');
32022             return;
32023         }
32024         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32025         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32026         
32027         this.parseExifTags(
32028             dataView,
32029             tiffOffset,
32030             tiffOffset + dirOffset,
32031             littleEndian
32032         );
32033     },
32034     
32035     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32036     {
32037         var tagsNumber,
32038             dirEndOffset,
32039             i;
32040         if (dirOffset + 6 > dataView.byteLength) {
32041             Roo.log('Invalid Exif data: Invalid directory offset.');
32042             return;
32043         }
32044         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32045         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32046         if (dirEndOffset + 4 > dataView.byteLength) {
32047             Roo.log('Invalid Exif data: Invalid directory size.');
32048             return;
32049         }
32050         for (i = 0; i < tagsNumber; i += 1) {
32051             this.parseExifTag(
32052                 dataView,
32053                 tiffOffset,
32054                 dirOffset + 2 + 12 * i, // tag offset
32055                 littleEndian
32056             );
32057         }
32058         // Return the offset to the next directory:
32059         return dataView.getUint32(dirEndOffset, littleEndian);
32060     },
32061     
32062     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32063     {
32064         var tag = dataView.getUint16(offset, littleEndian);
32065         
32066         this.exif[tag] = this.getExifValue(
32067             dataView,
32068             tiffOffset,
32069             offset,
32070             dataView.getUint16(offset + 2, littleEndian), // tag type
32071             dataView.getUint32(offset + 4, littleEndian), // tag length
32072             littleEndian
32073         );
32074     },
32075     
32076     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32077     {
32078         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32079             tagSize,
32080             dataOffset,
32081             values,
32082             i,
32083             str,
32084             c;
32085     
32086         if (!tagType) {
32087             Roo.log('Invalid Exif data: Invalid tag type.');
32088             return;
32089         }
32090         
32091         tagSize = tagType.size * length;
32092         // Determine if the value is contained in the dataOffset bytes,
32093         // or if the value at the dataOffset is a pointer to the actual data:
32094         dataOffset = tagSize > 4 ?
32095                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32096         if (dataOffset + tagSize > dataView.byteLength) {
32097             Roo.log('Invalid Exif data: Invalid data offset.');
32098             return;
32099         }
32100         if (length === 1) {
32101             return tagType.getValue(dataView, dataOffset, littleEndian);
32102         }
32103         values = [];
32104         for (i = 0; i < length; i += 1) {
32105             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32106         }
32107         
32108         if (tagType.ascii) {
32109             str = '';
32110             // Concatenate the chars:
32111             for (i = 0; i < values.length; i += 1) {
32112                 c = values[i];
32113                 // Ignore the terminating NULL byte(s):
32114                 if (c === '\u0000') {
32115                     break;
32116                 }
32117                 str += c;
32118             }
32119             return str;
32120         }
32121         return values;
32122     }
32123     
32124 });
32125
32126 Roo.apply(Roo.bootstrap.UploadCropbox, {
32127     tags : {
32128         'Orientation': 0x0112
32129     },
32130     
32131     Orientation: {
32132             1: 0, //'top-left',
32133 //            2: 'top-right',
32134             3: 180, //'bottom-right',
32135 //            4: 'bottom-left',
32136 //            5: 'left-top',
32137             6: 90, //'right-top',
32138 //            7: 'right-bottom',
32139             8: 270 //'left-bottom'
32140     },
32141     
32142     exifTagTypes : {
32143         // byte, 8-bit unsigned int:
32144         1: {
32145             getValue: function (dataView, dataOffset) {
32146                 return dataView.getUint8(dataOffset);
32147             },
32148             size: 1
32149         },
32150         // ascii, 8-bit byte:
32151         2: {
32152             getValue: function (dataView, dataOffset) {
32153                 return String.fromCharCode(dataView.getUint8(dataOffset));
32154             },
32155             size: 1,
32156             ascii: true
32157         },
32158         // short, 16 bit int:
32159         3: {
32160             getValue: function (dataView, dataOffset, littleEndian) {
32161                 return dataView.getUint16(dataOffset, littleEndian);
32162             },
32163             size: 2
32164         },
32165         // long, 32 bit int:
32166         4: {
32167             getValue: function (dataView, dataOffset, littleEndian) {
32168                 return dataView.getUint32(dataOffset, littleEndian);
32169             },
32170             size: 4
32171         },
32172         // rational = two long values, first is numerator, second is denominator:
32173         5: {
32174             getValue: function (dataView, dataOffset, littleEndian) {
32175                 return dataView.getUint32(dataOffset, littleEndian) /
32176                     dataView.getUint32(dataOffset + 4, littleEndian);
32177             },
32178             size: 8
32179         },
32180         // slong, 32 bit signed int:
32181         9: {
32182             getValue: function (dataView, dataOffset, littleEndian) {
32183                 return dataView.getInt32(dataOffset, littleEndian);
32184             },
32185             size: 4
32186         },
32187         // srational, two slongs, first is numerator, second is denominator:
32188         10: {
32189             getValue: function (dataView, dataOffset, littleEndian) {
32190                 return dataView.getInt32(dataOffset, littleEndian) /
32191                     dataView.getInt32(dataOffset + 4, littleEndian);
32192             },
32193             size: 8
32194         }
32195     },
32196     
32197     footer : {
32198         STANDARD : [
32199             {
32200                 tag : 'div',
32201                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32202                 action : 'rotate-left',
32203                 cn : [
32204                     {
32205                         tag : 'button',
32206                         cls : 'btn btn-default',
32207                         html : '<i class="fa fa-undo"></i>'
32208                     }
32209                 ]
32210             },
32211             {
32212                 tag : 'div',
32213                 cls : 'btn-group roo-upload-cropbox-picture',
32214                 action : 'picture',
32215                 cn : [
32216                     {
32217                         tag : 'button',
32218                         cls : 'btn btn-default',
32219                         html : '<i class="fa fa-picture-o"></i>'
32220                     }
32221                 ]
32222             },
32223             {
32224                 tag : 'div',
32225                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32226                 action : 'rotate-right',
32227                 cn : [
32228                     {
32229                         tag : 'button',
32230                         cls : 'btn btn-default',
32231                         html : '<i class="fa fa-repeat"></i>'
32232                     }
32233                 ]
32234             }
32235         ],
32236         DOCUMENT : [
32237             {
32238                 tag : 'div',
32239                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32240                 action : 'rotate-left',
32241                 cn : [
32242                     {
32243                         tag : 'button',
32244                         cls : 'btn btn-default',
32245                         html : '<i class="fa fa-undo"></i>'
32246                     }
32247                 ]
32248             },
32249             {
32250                 tag : 'div',
32251                 cls : 'btn-group roo-upload-cropbox-download',
32252                 action : 'download',
32253                 cn : [
32254                     {
32255                         tag : 'button',
32256                         cls : 'btn btn-default',
32257                         html : '<i class="fa fa-download"></i>'
32258                     }
32259                 ]
32260             },
32261             {
32262                 tag : 'div',
32263                 cls : 'btn-group roo-upload-cropbox-crop',
32264                 action : 'crop',
32265                 cn : [
32266                     {
32267                         tag : 'button',
32268                         cls : 'btn btn-default',
32269                         html : '<i class="fa fa-crop"></i>'
32270                     }
32271                 ]
32272             },
32273             {
32274                 tag : 'div',
32275                 cls : 'btn-group roo-upload-cropbox-trash',
32276                 action : 'trash',
32277                 cn : [
32278                     {
32279                         tag : 'button',
32280                         cls : 'btn btn-default',
32281                         html : '<i class="fa fa-trash"></i>'
32282                     }
32283                 ]
32284             },
32285             {
32286                 tag : 'div',
32287                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32288                 action : 'rotate-right',
32289                 cn : [
32290                     {
32291                         tag : 'button',
32292                         cls : 'btn btn-default',
32293                         html : '<i class="fa fa-repeat"></i>'
32294                     }
32295                 ]
32296             }
32297         ],
32298         ROTATOR : [
32299             {
32300                 tag : 'div',
32301                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32302                 action : 'rotate-left',
32303                 cn : [
32304                     {
32305                         tag : 'button',
32306                         cls : 'btn btn-default',
32307                         html : '<i class="fa fa-undo"></i>'
32308                     }
32309                 ]
32310             },
32311             {
32312                 tag : 'div',
32313                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32314                 action : 'rotate-right',
32315                 cn : [
32316                     {
32317                         tag : 'button',
32318                         cls : 'btn btn-default',
32319                         html : '<i class="fa fa-repeat"></i>'
32320                     }
32321                 ]
32322             }
32323         ]
32324     }
32325 });
32326
32327 /*
32328 * Licence: LGPL
32329 */
32330
32331 /**
32332  * @class Roo.bootstrap.DocumentManager
32333  * @extends Roo.bootstrap.Component
32334  * Bootstrap DocumentManager class
32335  * @cfg {String} paramName default 'imageUpload'
32336  * @cfg {String} toolTipName default 'filename'
32337  * @cfg {String} method default POST
32338  * @cfg {String} url action url
32339  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32340  * @cfg {Boolean} multiple multiple upload default true
32341  * @cfg {Number} thumbSize default 300
32342  * @cfg {String} fieldLabel
32343  * @cfg {Number} labelWidth default 4
32344  * @cfg {String} labelAlign (left|top) default left
32345  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32346 * @cfg {Number} labellg set the width of label (1-12)
32347  * @cfg {Number} labelmd set the width of label (1-12)
32348  * @cfg {Number} labelsm set the width of label (1-12)
32349  * @cfg {Number} labelxs set the width of label (1-12)
32350  * 
32351  * @constructor
32352  * Create a new DocumentManager
32353  * @param {Object} config The config object
32354  */
32355
32356 Roo.bootstrap.DocumentManager = function(config){
32357     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32358     
32359     this.files = [];
32360     this.delegates = [];
32361     
32362     this.addEvents({
32363         /**
32364          * @event initial
32365          * Fire when initial the DocumentManager
32366          * @param {Roo.bootstrap.DocumentManager} this
32367          */
32368         "initial" : true,
32369         /**
32370          * @event inspect
32371          * inspect selected file
32372          * @param {Roo.bootstrap.DocumentManager} this
32373          * @param {File} file
32374          */
32375         "inspect" : true,
32376         /**
32377          * @event exception
32378          * Fire when xhr load exception
32379          * @param {Roo.bootstrap.DocumentManager} this
32380          * @param {XMLHttpRequest} xhr
32381          */
32382         "exception" : true,
32383         /**
32384          * @event afterupload
32385          * Fire when xhr load exception
32386          * @param {Roo.bootstrap.DocumentManager} this
32387          * @param {XMLHttpRequest} xhr
32388          */
32389         "afterupload" : true,
32390         /**
32391          * @event prepare
32392          * prepare the form data
32393          * @param {Roo.bootstrap.DocumentManager} this
32394          * @param {Object} formData
32395          */
32396         "prepare" : true,
32397         /**
32398          * @event remove
32399          * Fire when remove the file
32400          * @param {Roo.bootstrap.DocumentManager} this
32401          * @param {Object} file
32402          */
32403         "remove" : true,
32404         /**
32405          * @event refresh
32406          * Fire after refresh the file
32407          * @param {Roo.bootstrap.DocumentManager} this
32408          */
32409         "refresh" : true,
32410         /**
32411          * @event click
32412          * Fire after click the image
32413          * @param {Roo.bootstrap.DocumentManager} this
32414          * @param {Object} file
32415          */
32416         "click" : true,
32417         /**
32418          * @event edit
32419          * Fire when upload a image and editable set to true
32420          * @param {Roo.bootstrap.DocumentManager} this
32421          * @param {Object} file
32422          */
32423         "edit" : true,
32424         /**
32425          * @event beforeselectfile
32426          * Fire before select file
32427          * @param {Roo.bootstrap.DocumentManager} this
32428          */
32429         "beforeselectfile" : true,
32430         /**
32431          * @event process
32432          * Fire before process file
32433          * @param {Roo.bootstrap.DocumentManager} this
32434          * @param {Object} file
32435          */
32436         "process" : true,
32437         /**
32438          * @event previewrendered
32439          * Fire when preview rendered
32440          * @param {Roo.bootstrap.DocumentManager} this
32441          * @param {Object} file
32442          */
32443         "previewrendered" : true,
32444         /**
32445          */
32446         "previewResize" : true
32447         
32448     });
32449 };
32450
32451 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32452     
32453     boxes : 0,
32454     inputName : '',
32455     thumbSize : 300,
32456     multiple : true,
32457     files : false,
32458     method : 'POST',
32459     url : '',
32460     paramName : 'imageUpload',
32461     toolTipName : 'filename',
32462     fieldLabel : '',
32463     labelWidth : 4,
32464     labelAlign : 'left',
32465     editable : true,
32466     delegates : false,
32467     xhr : false, 
32468     
32469     labellg : 0,
32470     labelmd : 0,
32471     labelsm : 0,
32472     labelxs : 0,
32473     
32474     getAutoCreate : function()
32475     {   
32476         var managerWidget = {
32477             tag : 'div',
32478             cls : 'roo-document-manager',
32479             cn : [
32480                 {
32481                     tag : 'input',
32482                     cls : 'roo-document-manager-selector',
32483                     type : 'file'
32484                 },
32485                 {
32486                     tag : 'div',
32487                     cls : 'roo-document-manager-uploader',
32488                     cn : [
32489                         {
32490                             tag : 'div',
32491                             cls : 'roo-document-manager-upload-btn',
32492                             html : '<i class="fa fa-plus"></i>'
32493                         }
32494                     ]
32495                     
32496                 }
32497             ]
32498         };
32499         
32500         var content = [
32501             {
32502                 tag : 'div',
32503                 cls : 'column col-md-12',
32504                 cn : managerWidget
32505             }
32506         ];
32507         
32508         if(this.fieldLabel.length){
32509             
32510             content = [
32511                 {
32512                     tag : 'div',
32513                     cls : 'column col-md-12',
32514                     html : this.fieldLabel
32515                 },
32516                 {
32517                     tag : 'div',
32518                     cls : 'column col-md-12',
32519                     cn : managerWidget
32520                 }
32521             ];
32522
32523             if(this.labelAlign == 'left'){
32524                 content = [
32525                     {
32526                         tag : 'div',
32527                         cls : 'column',
32528                         html : this.fieldLabel
32529                     },
32530                     {
32531                         tag : 'div',
32532                         cls : 'column',
32533                         cn : managerWidget
32534                     }
32535                 ];
32536                 
32537                 if(this.labelWidth > 12){
32538                     content[0].style = "width: " + this.labelWidth + 'px';
32539                 }
32540
32541                 if(this.labelWidth < 13 && this.labelmd == 0){
32542                     this.labelmd = this.labelWidth;
32543                 }
32544
32545                 if(this.labellg > 0){
32546                     content[0].cls += ' col-lg-' + this.labellg;
32547                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32548                 }
32549
32550                 if(this.labelmd > 0){
32551                     content[0].cls += ' col-md-' + this.labelmd;
32552                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32553                 }
32554
32555                 if(this.labelsm > 0){
32556                     content[0].cls += ' col-sm-' + this.labelsm;
32557                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32558                 }
32559
32560                 if(this.labelxs > 0){
32561                     content[0].cls += ' col-xs-' + this.labelxs;
32562                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32563                 }
32564                 
32565             }
32566         }
32567         
32568         var cfg = {
32569             tag : 'div',
32570             cls : 'row clearfix',
32571             cn : content
32572         };
32573         
32574         return cfg;
32575         
32576     },
32577     
32578     initEvents : function()
32579     {
32580         this.managerEl = this.el.select('.roo-document-manager', true).first();
32581         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32582         
32583         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32584         this.selectorEl.hide();
32585         
32586         if(this.multiple){
32587             this.selectorEl.attr('multiple', 'multiple');
32588         }
32589         
32590         this.selectorEl.on('change', this.onFileSelected, this);
32591         
32592         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32593         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32594         
32595         this.uploader.on('click', this.onUploaderClick, this);
32596         
32597         this.renderProgressDialog();
32598         
32599         var _this = this;
32600         
32601         window.addEventListener("resize", function() { _this.refresh(); } );
32602         
32603         this.fireEvent('initial', this);
32604     },
32605     
32606     renderProgressDialog : function()
32607     {
32608         var _this = this;
32609         
32610         this.progressDialog = new Roo.bootstrap.Modal({
32611             cls : 'roo-document-manager-progress-dialog',
32612             allow_close : false,
32613             animate : false,
32614             title : '',
32615             buttons : [
32616                 {
32617                     name  :'cancel',
32618                     weight : 'danger',
32619                     html : 'Cancel'
32620                 }
32621             ], 
32622             listeners : { 
32623                 btnclick : function() {
32624                     _this.uploadCancel();
32625                     this.hide();
32626                 }
32627             }
32628         });
32629          
32630         this.progressDialog.render(Roo.get(document.body));
32631          
32632         this.progress = new Roo.bootstrap.Progress({
32633             cls : 'roo-document-manager-progress',
32634             active : true,
32635             striped : true
32636         });
32637         
32638         this.progress.render(this.progressDialog.getChildContainer());
32639         
32640         this.progressBar = new Roo.bootstrap.ProgressBar({
32641             cls : 'roo-document-manager-progress-bar',
32642             aria_valuenow : 0,
32643             aria_valuemin : 0,
32644             aria_valuemax : 12,
32645             panel : 'success'
32646         });
32647         
32648         this.progressBar.render(this.progress.getChildContainer());
32649     },
32650     
32651     onUploaderClick : function(e)
32652     {
32653         e.preventDefault();
32654      
32655         if(this.fireEvent('beforeselectfile', this) != false){
32656             this.selectorEl.dom.click();
32657         }
32658         
32659     },
32660     
32661     onFileSelected : function(e)
32662     {
32663         e.preventDefault();
32664         
32665         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32666             return;
32667         }
32668         
32669         Roo.each(this.selectorEl.dom.files, function(file){
32670             if(this.fireEvent('inspect', this, file) != false){
32671                 this.files.push(file);
32672             }
32673         }, this);
32674         
32675         this.queue();
32676         
32677     },
32678     
32679     queue : function()
32680     {
32681         this.selectorEl.dom.value = '';
32682         
32683         if(!this.files || !this.files.length){
32684             return;
32685         }
32686         
32687         if(this.boxes > 0 && this.files.length > this.boxes){
32688             this.files = this.files.slice(0, this.boxes);
32689         }
32690         
32691         this.uploader.show();
32692         
32693         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32694             this.uploader.hide();
32695         }
32696         
32697         var _this = this;
32698         
32699         var files = [];
32700         
32701         var docs = [];
32702         
32703         Roo.each(this.files, function(file){
32704             
32705             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32706                 var f = this.renderPreview(file);
32707                 files.push(f);
32708                 return;
32709             }
32710             
32711             if(file.type.indexOf('image') != -1){
32712                 this.delegates.push(
32713                     (function(){
32714                         _this.process(file);
32715                     }).createDelegate(this)
32716                 );
32717         
32718                 return;
32719             }
32720             
32721             docs.push(
32722                 (function(){
32723                     _this.process(file);
32724                 }).createDelegate(this)
32725             );
32726             
32727         }, this);
32728         
32729         this.files = files;
32730         
32731         this.delegates = this.delegates.concat(docs);
32732         
32733         if(!this.delegates.length){
32734             this.refresh();
32735             return;
32736         }
32737         
32738         this.progressBar.aria_valuemax = this.delegates.length;
32739         
32740         this.arrange();
32741         
32742         return;
32743     },
32744     
32745     arrange : function()
32746     {
32747         if(!this.delegates.length){
32748             this.progressDialog.hide();
32749             this.refresh();
32750             return;
32751         }
32752         
32753         var delegate = this.delegates.shift();
32754         
32755         this.progressDialog.show();
32756         
32757         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32758         
32759         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32760         
32761         delegate();
32762     },
32763     
32764     refresh : function()
32765     {
32766         this.uploader.show();
32767         
32768         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32769             this.uploader.hide();
32770         }
32771         
32772         Roo.isTouch ? this.closable(false) : this.closable(true);
32773         
32774         this.fireEvent('refresh', this);
32775     },
32776     
32777     onRemove : function(e, el, o)
32778     {
32779         e.preventDefault();
32780         
32781         this.fireEvent('remove', this, o);
32782         
32783     },
32784     
32785     remove : function(o)
32786     {
32787         var files = [];
32788         
32789         Roo.each(this.files, function(file){
32790             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32791                 files.push(file);
32792                 return;
32793             }
32794
32795             o.target.remove();
32796
32797         }, this);
32798         
32799         this.files = files;
32800         
32801         this.refresh();
32802     },
32803     
32804     clear : function()
32805     {
32806         Roo.each(this.files, function(file){
32807             if(!file.target){
32808                 return;
32809             }
32810             
32811             file.target.remove();
32812
32813         }, this);
32814         
32815         this.files = [];
32816         
32817         this.refresh();
32818     },
32819     
32820     onClick : function(e, el, o)
32821     {
32822         e.preventDefault();
32823         
32824         this.fireEvent('click', this, o);
32825         
32826     },
32827     
32828     closable : function(closable)
32829     {
32830         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32831             
32832             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32833             
32834             if(closable){
32835                 el.show();
32836                 return;
32837             }
32838             
32839             el.hide();
32840             
32841         }, this);
32842     },
32843     
32844     xhrOnLoad : function(xhr)
32845     {
32846         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32847             el.remove();
32848         }, this);
32849         
32850         if (xhr.readyState !== 4) {
32851             this.arrange();
32852             this.fireEvent('exception', this, xhr);
32853             return;
32854         }
32855
32856         var response = Roo.decode(xhr.responseText);
32857         
32858         if(!response.success){
32859             this.arrange();
32860             this.fireEvent('exception', this, xhr);
32861             return;
32862         }
32863         
32864         var file = this.renderPreview(response.data);
32865         
32866         this.files.push(file);
32867         
32868         this.arrange();
32869         
32870         this.fireEvent('afterupload', this, xhr);
32871         
32872     },
32873     
32874     xhrOnError : function(xhr)
32875     {
32876         Roo.log('xhr on error');
32877         
32878         var response = Roo.decode(xhr.responseText);
32879           
32880         Roo.log(response);
32881         
32882         this.arrange();
32883     },
32884     
32885     process : function(file)
32886     {
32887         if(this.fireEvent('process', this, file) !== false){
32888             if(this.editable && file.type.indexOf('image') != -1){
32889                 this.fireEvent('edit', this, file);
32890                 return;
32891             }
32892
32893             this.uploadStart(file, false);
32894
32895             return;
32896         }
32897         
32898     },
32899     
32900     uploadStart : function(file, crop)
32901     {
32902         this.xhr = new XMLHttpRequest();
32903         
32904         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32905             this.arrange();
32906             return;
32907         }
32908         
32909         file.xhr = this.xhr;
32910             
32911         this.managerEl.createChild({
32912             tag : 'div',
32913             cls : 'roo-document-manager-loading',
32914             cn : [
32915                 {
32916                     tag : 'div',
32917                     tooltip : file.name,
32918                     cls : 'roo-document-manager-thumb',
32919                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32920                 }
32921             ]
32922
32923         });
32924
32925         this.xhr.open(this.method, this.url, true);
32926         
32927         var headers = {
32928             "Accept": "application/json",
32929             "Cache-Control": "no-cache",
32930             "X-Requested-With": "XMLHttpRequest"
32931         };
32932         
32933         for (var headerName in headers) {
32934             var headerValue = headers[headerName];
32935             if (headerValue) {
32936                 this.xhr.setRequestHeader(headerName, headerValue);
32937             }
32938         }
32939         
32940         var _this = this;
32941         
32942         this.xhr.onload = function()
32943         {
32944             _this.xhrOnLoad(_this.xhr);
32945         }
32946         
32947         this.xhr.onerror = function()
32948         {
32949             _this.xhrOnError(_this.xhr);
32950         }
32951         
32952         var formData = new FormData();
32953
32954         formData.append('returnHTML', 'NO');
32955         
32956         if(crop){
32957             formData.append('crop', crop);
32958         }
32959         
32960         formData.append(this.paramName, file, file.name);
32961         
32962         var options = {
32963             file : file, 
32964             manually : false
32965         };
32966         
32967         if(this.fireEvent('prepare', this, formData, options) != false){
32968             
32969             if(options.manually){
32970                 return;
32971             }
32972             
32973             this.xhr.send(formData);
32974             return;
32975         };
32976         
32977         this.uploadCancel();
32978     },
32979     
32980     uploadCancel : function()
32981     {
32982         if (this.xhr) {
32983             this.xhr.abort();
32984         }
32985         
32986         this.delegates = [];
32987         
32988         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32989             el.remove();
32990         }, this);
32991         
32992         this.arrange();
32993     },
32994     
32995     renderPreview : function(file)
32996     {
32997         if(typeof(file.target) != 'undefined' && file.target){
32998             return file;
32999         }
33000         
33001         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33002         
33003         var previewEl = this.managerEl.createChild({
33004             tag : 'div',
33005             cls : 'roo-document-manager-preview',
33006             cn : [
33007                 {
33008                     tag : 'div',
33009                     tooltip : file[this.toolTipName],
33010                     cls : 'roo-document-manager-thumb',
33011                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33012                 },
33013                 {
33014                     tag : 'button',
33015                     cls : 'close',
33016                     html : '<i class="fa fa-times-circle"></i>'
33017                 }
33018             ]
33019         });
33020
33021         var close = previewEl.select('button.close', true).first();
33022
33023         close.on('click', this.onRemove, this, file);
33024
33025         file.target = previewEl;
33026
33027         var image = previewEl.select('img', true).first();
33028         
33029         var _this = this;
33030         
33031         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33032         
33033         image.on('click', this.onClick, this, file);
33034         
33035         this.fireEvent('previewrendered', this, file);
33036         
33037         return file;
33038         
33039     },
33040     
33041     onPreviewLoad : function(file, image)
33042     {
33043         if(typeof(file.target) == 'undefined' || !file.target){
33044             return;
33045         }
33046         
33047         var width = image.dom.naturalWidth || image.dom.width;
33048         var height = image.dom.naturalHeight || image.dom.height;
33049         
33050         if(!this.previewResize) {
33051             return;
33052         }
33053         
33054         if(width > height){
33055             file.target.addClass('wide');
33056             return;
33057         }
33058         
33059         file.target.addClass('tall');
33060         return;
33061         
33062     },
33063     
33064     uploadFromSource : function(file, crop)
33065     {
33066         this.xhr = new XMLHttpRequest();
33067         
33068         this.managerEl.createChild({
33069             tag : 'div',
33070             cls : 'roo-document-manager-loading',
33071             cn : [
33072                 {
33073                     tag : 'div',
33074                     tooltip : file.name,
33075                     cls : 'roo-document-manager-thumb',
33076                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33077                 }
33078             ]
33079
33080         });
33081
33082         this.xhr.open(this.method, this.url, true);
33083         
33084         var headers = {
33085             "Accept": "application/json",
33086             "Cache-Control": "no-cache",
33087             "X-Requested-With": "XMLHttpRequest"
33088         };
33089         
33090         for (var headerName in headers) {
33091             var headerValue = headers[headerName];
33092             if (headerValue) {
33093                 this.xhr.setRequestHeader(headerName, headerValue);
33094             }
33095         }
33096         
33097         var _this = this;
33098         
33099         this.xhr.onload = function()
33100         {
33101             _this.xhrOnLoad(_this.xhr);
33102         }
33103         
33104         this.xhr.onerror = function()
33105         {
33106             _this.xhrOnError(_this.xhr);
33107         }
33108         
33109         var formData = new FormData();
33110
33111         formData.append('returnHTML', 'NO');
33112         
33113         formData.append('crop', crop);
33114         
33115         if(typeof(file.filename) != 'undefined'){
33116             formData.append('filename', file.filename);
33117         }
33118         
33119         if(typeof(file.mimetype) != 'undefined'){
33120             formData.append('mimetype', file.mimetype);
33121         }
33122         
33123         Roo.log(formData);
33124         
33125         if(this.fireEvent('prepare', this, formData) != false){
33126             this.xhr.send(formData);
33127         };
33128     }
33129 });
33130
33131 /*
33132 * Licence: LGPL
33133 */
33134
33135 /**
33136  * @class Roo.bootstrap.DocumentViewer
33137  * @extends Roo.bootstrap.Component
33138  * Bootstrap DocumentViewer class
33139  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33140  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33141  * 
33142  * @constructor
33143  * Create a new DocumentViewer
33144  * @param {Object} config The config object
33145  */
33146
33147 Roo.bootstrap.DocumentViewer = function(config){
33148     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33149     
33150     this.addEvents({
33151         /**
33152          * @event initial
33153          * Fire after initEvent
33154          * @param {Roo.bootstrap.DocumentViewer} this
33155          */
33156         "initial" : true,
33157         /**
33158          * @event click
33159          * Fire after click
33160          * @param {Roo.bootstrap.DocumentViewer} this
33161          */
33162         "click" : true,
33163         /**
33164          * @event download
33165          * Fire after download button
33166          * @param {Roo.bootstrap.DocumentViewer} this
33167          */
33168         "download" : true,
33169         /**
33170          * @event trash
33171          * Fire after trash button
33172          * @param {Roo.bootstrap.DocumentViewer} this
33173          */
33174         "trash" : true
33175         
33176     });
33177 };
33178
33179 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33180     
33181     showDownload : true,
33182     
33183     showTrash : true,
33184     
33185     getAutoCreate : function()
33186     {
33187         var cfg = {
33188             tag : 'div',
33189             cls : 'roo-document-viewer',
33190             cn : [
33191                 {
33192                     tag : 'div',
33193                     cls : 'roo-document-viewer-body',
33194                     cn : [
33195                         {
33196                             tag : 'div',
33197                             cls : 'roo-document-viewer-thumb',
33198                             cn : [
33199                                 {
33200                                     tag : 'img',
33201                                     cls : 'roo-document-viewer-image'
33202                                 }
33203                             ]
33204                         }
33205                     ]
33206                 },
33207                 {
33208                     tag : 'div',
33209                     cls : 'roo-document-viewer-footer',
33210                     cn : {
33211                         tag : 'div',
33212                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33213                         cn : [
33214                             {
33215                                 tag : 'div',
33216                                 cls : 'btn-group roo-document-viewer-download',
33217                                 cn : [
33218                                     {
33219                                         tag : 'button',
33220                                         cls : 'btn btn-default',
33221                                         html : '<i class="fa fa-download"></i>'
33222                                     }
33223                                 ]
33224                             },
33225                             {
33226                                 tag : 'div',
33227                                 cls : 'btn-group roo-document-viewer-trash',
33228                                 cn : [
33229                                     {
33230                                         tag : 'button',
33231                                         cls : 'btn btn-default',
33232                                         html : '<i class="fa fa-trash"></i>'
33233                                     }
33234                                 ]
33235                             }
33236                         ]
33237                     }
33238                 }
33239             ]
33240         };
33241         
33242         return cfg;
33243     },
33244     
33245     initEvents : function()
33246     {
33247         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33248         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33249         
33250         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33251         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33252         
33253         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33254         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33255         
33256         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33257         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33258         
33259         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33260         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33261         
33262         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33263         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33264         
33265         this.bodyEl.on('click', this.onClick, this);
33266         this.downloadBtn.on('click', this.onDownload, this);
33267         this.trashBtn.on('click', this.onTrash, this);
33268         
33269         this.downloadBtn.hide();
33270         this.trashBtn.hide();
33271         
33272         if(this.showDownload){
33273             this.downloadBtn.show();
33274         }
33275         
33276         if(this.showTrash){
33277             this.trashBtn.show();
33278         }
33279         
33280         if(!this.showDownload && !this.showTrash) {
33281             this.footerEl.hide();
33282         }
33283         
33284     },
33285     
33286     initial : function()
33287     {
33288         this.fireEvent('initial', this);
33289         
33290     },
33291     
33292     onClick : function(e)
33293     {
33294         e.preventDefault();
33295         
33296         this.fireEvent('click', this);
33297     },
33298     
33299     onDownload : function(e)
33300     {
33301         e.preventDefault();
33302         
33303         this.fireEvent('download', this);
33304     },
33305     
33306     onTrash : function(e)
33307     {
33308         e.preventDefault();
33309         
33310         this.fireEvent('trash', this);
33311     }
33312     
33313 });
33314 /*
33315  * - LGPL
33316  *
33317  * nav progress bar
33318  * 
33319  */
33320
33321 /**
33322  * @class Roo.bootstrap.NavProgressBar
33323  * @extends Roo.bootstrap.Component
33324  * Bootstrap NavProgressBar class
33325  * 
33326  * @constructor
33327  * Create a new nav progress bar
33328  * @param {Object} config The config object
33329  */
33330
33331 Roo.bootstrap.NavProgressBar = function(config){
33332     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33333
33334     this.bullets = this.bullets || [];
33335    
33336 //    Roo.bootstrap.NavProgressBar.register(this);
33337      this.addEvents({
33338         /**
33339              * @event changed
33340              * Fires when the active item changes
33341              * @param {Roo.bootstrap.NavProgressBar} this
33342              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33343              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33344          */
33345         'changed': true
33346      });
33347     
33348 };
33349
33350 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33351     
33352     bullets : [],
33353     barItems : [],
33354     
33355     getAutoCreate : function()
33356     {
33357         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33358         
33359         cfg = {
33360             tag : 'div',
33361             cls : 'roo-navigation-bar-group',
33362             cn : [
33363                 {
33364                     tag : 'div',
33365                     cls : 'roo-navigation-top-bar'
33366                 },
33367                 {
33368                     tag : 'div',
33369                     cls : 'roo-navigation-bullets-bar',
33370                     cn : [
33371                         {
33372                             tag : 'ul',
33373                             cls : 'roo-navigation-bar'
33374                         }
33375                     ]
33376                 },
33377                 
33378                 {
33379                     tag : 'div',
33380                     cls : 'roo-navigation-bottom-bar'
33381                 }
33382             ]
33383             
33384         };
33385         
33386         return cfg;
33387         
33388     },
33389     
33390     initEvents: function() 
33391     {
33392         
33393     },
33394     
33395     onRender : function(ct, position) 
33396     {
33397         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33398         
33399         if(this.bullets.length){
33400             Roo.each(this.bullets, function(b){
33401                this.addItem(b);
33402             }, this);
33403         }
33404         
33405         this.format();
33406         
33407     },
33408     
33409     addItem : function(cfg)
33410     {
33411         var item = new Roo.bootstrap.NavProgressItem(cfg);
33412         
33413         item.parentId = this.id;
33414         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33415         
33416         if(cfg.html){
33417             var top = new Roo.bootstrap.Element({
33418                 tag : 'div',
33419                 cls : 'roo-navigation-bar-text'
33420             });
33421             
33422             var bottom = new Roo.bootstrap.Element({
33423                 tag : 'div',
33424                 cls : 'roo-navigation-bar-text'
33425             });
33426             
33427             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33428             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33429             
33430             var topText = new Roo.bootstrap.Element({
33431                 tag : 'span',
33432                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33433             });
33434             
33435             var bottomText = new Roo.bootstrap.Element({
33436                 tag : 'span',
33437                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33438             });
33439             
33440             topText.onRender(top.el, null);
33441             bottomText.onRender(bottom.el, null);
33442             
33443             item.topEl = top;
33444             item.bottomEl = bottom;
33445         }
33446         
33447         this.barItems.push(item);
33448         
33449         return item;
33450     },
33451     
33452     getActive : function()
33453     {
33454         var active = false;
33455         
33456         Roo.each(this.barItems, function(v){
33457             
33458             if (!v.isActive()) {
33459                 return;
33460             }
33461             
33462             active = v;
33463             return false;
33464             
33465         });
33466         
33467         return active;
33468     },
33469     
33470     setActiveItem : function(item)
33471     {
33472         var prev = false;
33473         
33474         Roo.each(this.barItems, function(v){
33475             if (v.rid == item.rid) {
33476                 return ;
33477             }
33478             
33479             if (v.isActive()) {
33480                 v.setActive(false);
33481                 prev = v;
33482             }
33483         });
33484
33485         item.setActive(true);
33486         
33487         this.fireEvent('changed', this, item, prev);
33488     },
33489     
33490     getBarItem: function(rid)
33491     {
33492         var ret = false;
33493         
33494         Roo.each(this.barItems, function(e) {
33495             if (e.rid != rid) {
33496                 return;
33497             }
33498             
33499             ret =  e;
33500             return false;
33501         });
33502         
33503         return ret;
33504     },
33505     
33506     indexOfItem : function(item)
33507     {
33508         var index = false;
33509         
33510         Roo.each(this.barItems, function(v, i){
33511             
33512             if (v.rid != item.rid) {
33513                 return;
33514             }
33515             
33516             index = i;
33517             return false
33518         });
33519         
33520         return index;
33521     },
33522     
33523     setActiveNext : function()
33524     {
33525         var i = this.indexOfItem(this.getActive());
33526         
33527         if (i > this.barItems.length) {
33528             return;
33529         }
33530         
33531         this.setActiveItem(this.barItems[i+1]);
33532     },
33533     
33534     setActivePrev : function()
33535     {
33536         var i = this.indexOfItem(this.getActive());
33537         
33538         if (i  < 1) {
33539             return;
33540         }
33541         
33542         this.setActiveItem(this.barItems[i-1]);
33543     },
33544     
33545     format : function()
33546     {
33547         if(!this.barItems.length){
33548             return;
33549         }
33550      
33551         var width = 100 / this.barItems.length;
33552         
33553         Roo.each(this.barItems, function(i){
33554             i.el.setStyle('width', width + '%');
33555             i.topEl.el.setStyle('width', width + '%');
33556             i.bottomEl.el.setStyle('width', width + '%');
33557         }, this);
33558         
33559     }
33560     
33561 });
33562 /*
33563  * - LGPL
33564  *
33565  * Nav Progress Item
33566  * 
33567  */
33568
33569 /**
33570  * @class Roo.bootstrap.NavProgressItem
33571  * @extends Roo.bootstrap.Component
33572  * Bootstrap NavProgressItem class
33573  * @cfg {String} rid the reference id
33574  * @cfg {Boolean} active (true|false) Is item active default false
33575  * @cfg {Boolean} disabled (true|false) Is item active default false
33576  * @cfg {String} html
33577  * @cfg {String} position (top|bottom) text position default bottom
33578  * @cfg {String} icon show icon instead of number
33579  * 
33580  * @constructor
33581  * Create a new NavProgressItem
33582  * @param {Object} config The config object
33583  */
33584 Roo.bootstrap.NavProgressItem = function(config){
33585     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33586     this.addEvents({
33587         // raw events
33588         /**
33589          * @event click
33590          * The raw click event for the entire grid.
33591          * @param {Roo.bootstrap.NavProgressItem} this
33592          * @param {Roo.EventObject} e
33593          */
33594         "click" : true
33595     });
33596    
33597 };
33598
33599 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33600     
33601     rid : '',
33602     active : false,
33603     disabled : false,
33604     html : '',
33605     position : 'bottom',
33606     icon : false,
33607     
33608     getAutoCreate : function()
33609     {
33610         var iconCls = 'roo-navigation-bar-item-icon';
33611         
33612         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33613         
33614         var cfg = {
33615             tag: 'li',
33616             cls: 'roo-navigation-bar-item',
33617             cn : [
33618                 {
33619                     tag : 'i',
33620                     cls : iconCls
33621                 }
33622             ]
33623         };
33624         
33625         if(this.active){
33626             cfg.cls += ' active';
33627         }
33628         if(this.disabled){
33629             cfg.cls += ' disabled';
33630         }
33631         
33632         return cfg;
33633     },
33634     
33635     disable : function()
33636     {
33637         this.setDisabled(true);
33638     },
33639     
33640     enable : function()
33641     {
33642         this.setDisabled(false);
33643     },
33644     
33645     initEvents: function() 
33646     {
33647         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33648         
33649         this.iconEl.on('click', this.onClick, this);
33650     },
33651     
33652     onClick : function(e)
33653     {
33654         e.preventDefault();
33655         
33656         if(this.disabled){
33657             return;
33658         }
33659         
33660         if(this.fireEvent('click', this, e) === false){
33661             return;
33662         };
33663         
33664         this.parent().setActiveItem(this);
33665     },
33666     
33667     isActive: function () 
33668     {
33669         return this.active;
33670     },
33671     
33672     setActive : function(state)
33673     {
33674         if(this.active == state){
33675             return;
33676         }
33677         
33678         this.active = state;
33679         
33680         if (state) {
33681             this.el.addClass('active');
33682             return;
33683         }
33684         
33685         this.el.removeClass('active');
33686         
33687         return;
33688     },
33689     
33690     setDisabled : function(state)
33691     {
33692         if(this.disabled == state){
33693             return;
33694         }
33695         
33696         this.disabled = state;
33697         
33698         if (state) {
33699             this.el.addClass('disabled');
33700             return;
33701         }
33702         
33703         this.el.removeClass('disabled');
33704     },
33705     
33706     tooltipEl : function()
33707     {
33708         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33709     }
33710 });
33711  
33712
33713  /*
33714  * - LGPL
33715  *
33716  * FieldLabel
33717  * 
33718  */
33719
33720 /**
33721  * @class Roo.bootstrap.FieldLabel
33722  * @extends Roo.bootstrap.Component
33723  * Bootstrap FieldLabel class
33724  * @cfg {String} html contents of the element
33725  * @cfg {String} tag tag of the element default label
33726  * @cfg {String} cls class of the element
33727  * @cfg {String} target label target 
33728  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33729  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33730  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33731  * @cfg {String} iconTooltip default "This field is required"
33732  * @cfg {String} indicatorpos (left|right) default left
33733  * 
33734  * @constructor
33735  * Create a new FieldLabel
33736  * @param {Object} config The config object
33737  */
33738
33739 Roo.bootstrap.FieldLabel = function(config){
33740     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33741     
33742     this.addEvents({
33743             /**
33744              * @event invalid
33745              * Fires after the field has been marked as invalid.
33746              * @param {Roo.form.FieldLabel} this
33747              * @param {String} msg The validation message
33748              */
33749             invalid : true,
33750             /**
33751              * @event valid
33752              * Fires after the field has been validated with no errors.
33753              * @param {Roo.form.FieldLabel} this
33754              */
33755             valid : true
33756         });
33757 };
33758
33759 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33760     
33761     tag: 'label',
33762     cls: '',
33763     html: '',
33764     target: '',
33765     allowBlank : true,
33766     invalidClass : 'has-warning',
33767     validClass : 'has-success',
33768     iconTooltip : 'This field is required',
33769     indicatorpos : 'left',
33770     
33771     getAutoCreate : function(){
33772         
33773         var cls = "";
33774         if (!this.allowBlank) {
33775             cls  = "visible";
33776         }
33777         
33778         var cfg = {
33779             tag : this.tag,
33780             cls : 'roo-bootstrap-field-label ' + this.cls,
33781             for : this.target,
33782             cn : [
33783                 {
33784                     tag : 'i',
33785                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33786                     tooltip : this.iconTooltip
33787                 },
33788                 {
33789                     tag : 'span',
33790                     html : this.html
33791                 }
33792             ] 
33793         };
33794         
33795         if(this.indicatorpos == 'right'){
33796             var cfg = {
33797                 tag : this.tag,
33798                 cls : 'roo-bootstrap-field-label ' + this.cls,
33799                 for : this.target,
33800                 cn : [
33801                     {
33802                         tag : 'span',
33803                         html : this.html
33804                     },
33805                     {
33806                         tag : 'i',
33807                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33808                         tooltip : this.iconTooltip
33809                     }
33810                 ] 
33811             };
33812         }
33813         
33814         return cfg;
33815     },
33816     
33817     initEvents: function() 
33818     {
33819         Roo.bootstrap.Element.superclass.initEvents.call(this);
33820         
33821         this.indicator = this.indicatorEl();
33822         
33823         if(this.indicator){
33824             this.indicator.removeClass('visible');
33825             this.indicator.addClass('invisible');
33826         }
33827         
33828         Roo.bootstrap.FieldLabel.register(this);
33829     },
33830     
33831     indicatorEl : function()
33832     {
33833         var indicator = this.el.select('i.roo-required-indicator',true).first();
33834         
33835         if(!indicator){
33836             return false;
33837         }
33838         
33839         return indicator;
33840         
33841     },
33842     
33843     /**
33844      * Mark this field as valid
33845      */
33846     markValid : function()
33847     {
33848         if(this.indicator){
33849             this.indicator.removeClass('visible');
33850             this.indicator.addClass('invisible');
33851         }
33852         if (Roo.bootstrap.version == 3) {
33853             this.el.removeClass(this.invalidClass);
33854             this.el.addClass(this.validClass);
33855         } else {
33856             this.el.removeClass('is-invalid');
33857             this.el.addClass('is-valid');
33858         }
33859         
33860         
33861         this.fireEvent('valid', this);
33862     },
33863     
33864     /**
33865      * Mark this field as invalid
33866      * @param {String} msg The validation message
33867      */
33868     markInvalid : function(msg)
33869     {
33870         if(this.indicator){
33871             this.indicator.removeClass('invisible');
33872             this.indicator.addClass('visible');
33873         }
33874           if (Roo.bootstrap.version == 3) {
33875             this.el.removeClass(this.validClass);
33876             this.el.addClass(this.invalidClass);
33877         } else {
33878             this.el.removeClass('is-valid');
33879             this.el.addClass('is-invalid');
33880         }
33881         
33882         
33883         this.fireEvent('invalid', this, msg);
33884     }
33885     
33886    
33887 });
33888
33889 Roo.apply(Roo.bootstrap.FieldLabel, {
33890     
33891     groups: {},
33892     
33893      /**
33894     * register a FieldLabel Group
33895     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33896     */
33897     register : function(label)
33898     {
33899         if(this.groups.hasOwnProperty(label.target)){
33900             return;
33901         }
33902      
33903         this.groups[label.target] = label;
33904         
33905     },
33906     /**
33907     * fetch a FieldLabel Group based on the target
33908     * @param {string} target
33909     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33910     */
33911     get: function(target) {
33912         if (typeof(this.groups[target]) == 'undefined') {
33913             return false;
33914         }
33915         
33916         return this.groups[target] ;
33917     }
33918 });
33919
33920  
33921
33922  /*
33923  * - LGPL
33924  *
33925  * page DateSplitField.
33926  * 
33927  */
33928
33929
33930 /**
33931  * @class Roo.bootstrap.DateSplitField
33932  * @extends Roo.bootstrap.Component
33933  * Bootstrap DateSplitField class
33934  * @cfg {string} fieldLabel - the label associated
33935  * @cfg {Number} labelWidth set the width of label (0-12)
33936  * @cfg {String} labelAlign (top|left)
33937  * @cfg {Boolean} dayAllowBlank (true|false) default false
33938  * @cfg {Boolean} monthAllowBlank (true|false) default false
33939  * @cfg {Boolean} yearAllowBlank (true|false) default false
33940  * @cfg {string} dayPlaceholder 
33941  * @cfg {string} monthPlaceholder
33942  * @cfg {string} yearPlaceholder
33943  * @cfg {string} dayFormat default 'd'
33944  * @cfg {string} monthFormat default 'm'
33945  * @cfg {string} yearFormat default 'Y'
33946  * @cfg {Number} labellg set the width of label (1-12)
33947  * @cfg {Number} labelmd set the width of label (1-12)
33948  * @cfg {Number} labelsm set the width of label (1-12)
33949  * @cfg {Number} labelxs set the width of label (1-12)
33950
33951  *     
33952  * @constructor
33953  * Create a new DateSplitField
33954  * @param {Object} config The config object
33955  */
33956
33957 Roo.bootstrap.DateSplitField = function(config){
33958     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33959     
33960     this.addEvents({
33961         // raw events
33962          /**
33963          * @event years
33964          * getting the data of years
33965          * @param {Roo.bootstrap.DateSplitField} this
33966          * @param {Object} years
33967          */
33968         "years" : true,
33969         /**
33970          * @event days
33971          * getting the data of days
33972          * @param {Roo.bootstrap.DateSplitField} this
33973          * @param {Object} days
33974          */
33975         "days" : true,
33976         /**
33977          * @event invalid
33978          * Fires after the field has been marked as invalid.
33979          * @param {Roo.form.Field} this
33980          * @param {String} msg The validation message
33981          */
33982         invalid : true,
33983        /**
33984          * @event valid
33985          * Fires after the field has been validated with no errors.
33986          * @param {Roo.form.Field} this
33987          */
33988         valid : true
33989     });
33990 };
33991
33992 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33993     
33994     fieldLabel : '',
33995     labelAlign : 'top',
33996     labelWidth : 3,
33997     dayAllowBlank : false,
33998     monthAllowBlank : false,
33999     yearAllowBlank : false,
34000     dayPlaceholder : '',
34001     monthPlaceholder : '',
34002     yearPlaceholder : '',
34003     dayFormat : 'd',
34004     monthFormat : 'm',
34005     yearFormat : 'Y',
34006     isFormField : true,
34007     labellg : 0,
34008     labelmd : 0,
34009     labelsm : 0,
34010     labelxs : 0,
34011     
34012     getAutoCreate : function()
34013     {
34014         var cfg = {
34015             tag : 'div',
34016             cls : 'row roo-date-split-field-group',
34017             cn : [
34018                 {
34019                     tag : 'input',
34020                     type : 'hidden',
34021                     cls : 'form-hidden-field roo-date-split-field-group-value',
34022                     name : this.name
34023                 }
34024             ]
34025         };
34026         
34027         var labelCls = 'col-md-12';
34028         var contentCls = 'col-md-4';
34029         
34030         if(this.fieldLabel){
34031             
34032             var label = {
34033                 tag : 'div',
34034                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34035                 cn : [
34036                     {
34037                         tag : 'label',
34038                         html : this.fieldLabel
34039                     }
34040                 ]
34041             };
34042             
34043             if(this.labelAlign == 'left'){
34044             
34045                 if(this.labelWidth > 12){
34046                     label.style = "width: " + this.labelWidth + 'px';
34047                 }
34048
34049                 if(this.labelWidth < 13 && this.labelmd == 0){
34050                     this.labelmd = this.labelWidth;
34051                 }
34052
34053                 if(this.labellg > 0){
34054                     labelCls = ' col-lg-' + this.labellg;
34055                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34056                 }
34057
34058                 if(this.labelmd > 0){
34059                     labelCls = ' col-md-' + this.labelmd;
34060                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34061                 }
34062
34063                 if(this.labelsm > 0){
34064                     labelCls = ' col-sm-' + this.labelsm;
34065                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34066                 }
34067
34068                 if(this.labelxs > 0){
34069                     labelCls = ' col-xs-' + this.labelxs;
34070                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34071                 }
34072             }
34073             
34074             label.cls += ' ' + labelCls;
34075             
34076             cfg.cn.push(label);
34077         }
34078         
34079         Roo.each(['day', 'month', 'year'], function(t){
34080             cfg.cn.push({
34081                 tag : 'div',
34082                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34083             });
34084         }, this);
34085         
34086         return cfg;
34087     },
34088     
34089     inputEl: function ()
34090     {
34091         return this.el.select('.roo-date-split-field-group-value', true).first();
34092     },
34093     
34094     onRender : function(ct, position) 
34095     {
34096         var _this = this;
34097         
34098         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34099         
34100         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34101         
34102         this.dayField = new Roo.bootstrap.ComboBox({
34103             allowBlank : this.dayAllowBlank,
34104             alwaysQuery : true,
34105             displayField : 'value',
34106             editable : false,
34107             fieldLabel : '',
34108             forceSelection : true,
34109             mode : 'local',
34110             placeholder : this.dayPlaceholder,
34111             selectOnFocus : true,
34112             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34113             triggerAction : 'all',
34114             typeAhead : true,
34115             valueField : 'value',
34116             store : new Roo.data.SimpleStore({
34117                 data : (function() {    
34118                     var days = [];
34119                     _this.fireEvent('days', _this, days);
34120                     return days;
34121                 })(),
34122                 fields : [ 'value' ]
34123             }),
34124             listeners : {
34125                 select : function (_self, record, index)
34126                 {
34127                     _this.setValue(_this.getValue());
34128                 }
34129             }
34130         });
34131
34132         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34133         
34134         this.monthField = new Roo.bootstrap.MonthField({
34135             after : '<i class=\"fa fa-calendar\"></i>',
34136             allowBlank : this.monthAllowBlank,
34137             placeholder : this.monthPlaceholder,
34138             readOnly : true,
34139             listeners : {
34140                 render : function (_self)
34141                 {
34142                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34143                         e.preventDefault();
34144                         _self.focus();
34145                     });
34146                 },
34147                 select : function (_self, oldvalue, newvalue)
34148                 {
34149                     _this.setValue(_this.getValue());
34150                 }
34151             }
34152         });
34153         
34154         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34155         
34156         this.yearField = new Roo.bootstrap.ComboBox({
34157             allowBlank : this.yearAllowBlank,
34158             alwaysQuery : true,
34159             displayField : 'value',
34160             editable : false,
34161             fieldLabel : '',
34162             forceSelection : true,
34163             mode : 'local',
34164             placeholder : this.yearPlaceholder,
34165             selectOnFocus : true,
34166             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34167             triggerAction : 'all',
34168             typeAhead : true,
34169             valueField : 'value',
34170             store : new Roo.data.SimpleStore({
34171                 data : (function() {
34172                     var years = [];
34173                     _this.fireEvent('years', _this, years);
34174                     return years;
34175                 })(),
34176                 fields : [ 'value' ]
34177             }),
34178             listeners : {
34179                 select : function (_self, record, index)
34180                 {
34181                     _this.setValue(_this.getValue());
34182                 }
34183             }
34184         });
34185
34186         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34187     },
34188     
34189     setValue : function(v, format)
34190     {
34191         this.inputEl.dom.value = v;
34192         
34193         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34194         
34195         var d = Date.parseDate(v, f);
34196         
34197         if(!d){
34198             this.validate();
34199             return;
34200         }
34201         
34202         this.setDay(d.format(this.dayFormat));
34203         this.setMonth(d.format(this.monthFormat));
34204         this.setYear(d.format(this.yearFormat));
34205         
34206         this.validate();
34207         
34208         return;
34209     },
34210     
34211     setDay : function(v)
34212     {
34213         this.dayField.setValue(v);
34214         this.inputEl.dom.value = this.getValue();
34215         this.validate();
34216         return;
34217     },
34218     
34219     setMonth : function(v)
34220     {
34221         this.monthField.setValue(v, true);
34222         this.inputEl.dom.value = this.getValue();
34223         this.validate();
34224         return;
34225     },
34226     
34227     setYear : function(v)
34228     {
34229         this.yearField.setValue(v);
34230         this.inputEl.dom.value = this.getValue();
34231         this.validate();
34232         return;
34233     },
34234     
34235     getDay : function()
34236     {
34237         return this.dayField.getValue();
34238     },
34239     
34240     getMonth : function()
34241     {
34242         return this.monthField.getValue();
34243     },
34244     
34245     getYear : function()
34246     {
34247         return this.yearField.getValue();
34248     },
34249     
34250     getValue : function()
34251     {
34252         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34253         
34254         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34255         
34256         return date;
34257     },
34258     
34259     reset : function()
34260     {
34261         this.setDay('');
34262         this.setMonth('');
34263         this.setYear('');
34264         this.inputEl.dom.value = '';
34265         this.validate();
34266         return;
34267     },
34268     
34269     validate : function()
34270     {
34271         var d = this.dayField.validate();
34272         var m = this.monthField.validate();
34273         var y = this.yearField.validate();
34274         
34275         var valid = true;
34276         
34277         if(
34278                 (!this.dayAllowBlank && !d) ||
34279                 (!this.monthAllowBlank && !m) ||
34280                 (!this.yearAllowBlank && !y)
34281         ){
34282             valid = false;
34283         }
34284         
34285         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34286             return valid;
34287         }
34288         
34289         if(valid){
34290             this.markValid();
34291             return valid;
34292         }
34293         
34294         this.markInvalid();
34295         
34296         return valid;
34297     },
34298     
34299     markValid : function()
34300     {
34301         
34302         var label = this.el.select('label', true).first();
34303         var icon = this.el.select('i.fa-star', true).first();
34304
34305         if(label && icon){
34306             icon.remove();
34307         }
34308         
34309         this.fireEvent('valid', this);
34310     },
34311     
34312      /**
34313      * Mark this field as invalid
34314      * @param {String} msg The validation message
34315      */
34316     markInvalid : function(msg)
34317     {
34318         
34319         var label = this.el.select('label', true).first();
34320         var icon = this.el.select('i.fa-star', true).first();
34321
34322         if(label && !icon){
34323             this.el.select('.roo-date-split-field-label', true).createChild({
34324                 tag : 'i',
34325                 cls : 'text-danger fa fa-lg fa-star',
34326                 tooltip : 'This field is required',
34327                 style : 'margin-right:5px;'
34328             }, label, true);
34329         }
34330         
34331         this.fireEvent('invalid', this, msg);
34332     },
34333     
34334     clearInvalid : function()
34335     {
34336         var label = this.el.select('label', true).first();
34337         var icon = this.el.select('i.fa-star', true).first();
34338
34339         if(label && icon){
34340             icon.remove();
34341         }
34342         
34343         this.fireEvent('valid', this);
34344     },
34345     
34346     getName: function()
34347     {
34348         return this.name;
34349     }
34350     
34351 });
34352
34353  /**
34354  *
34355  * This is based on 
34356  * http://masonry.desandro.com
34357  *
34358  * The idea is to render all the bricks based on vertical width...
34359  *
34360  * The original code extends 'outlayer' - we might need to use that....
34361  * 
34362  */
34363
34364
34365 /**
34366  * @class Roo.bootstrap.LayoutMasonry
34367  * @extends Roo.bootstrap.Component
34368  * Bootstrap Layout Masonry class
34369  * 
34370  * @constructor
34371  * Create a new Element
34372  * @param {Object} config The config object
34373  */
34374
34375 Roo.bootstrap.LayoutMasonry = function(config){
34376     
34377     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34378     
34379     this.bricks = [];
34380     
34381     Roo.bootstrap.LayoutMasonry.register(this);
34382     
34383     this.addEvents({
34384         // raw events
34385         /**
34386          * @event layout
34387          * Fire after layout the items
34388          * @param {Roo.bootstrap.LayoutMasonry} this
34389          * @param {Roo.EventObject} e
34390          */
34391         "layout" : true
34392     });
34393     
34394 };
34395
34396 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34397     
34398     /**
34399      * @cfg {Boolean} isLayoutInstant = no animation?
34400      */   
34401     isLayoutInstant : false, // needed?
34402    
34403     /**
34404      * @cfg {Number} boxWidth  width of the columns
34405      */   
34406     boxWidth : 450,
34407     
34408       /**
34409      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34410      */   
34411     boxHeight : 0,
34412     
34413     /**
34414      * @cfg {Number} padWidth padding below box..
34415      */   
34416     padWidth : 10, 
34417     
34418     /**
34419      * @cfg {Number} gutter gutter width..
34420      */   
34421     gutter : 10,
34422     
34423      /**
34424      * @cfg {Number} maxCols maximum number of columns
34425      */   
34426     
34427     maxCols: 0,
34428     
34429     /**
34430      * @cfg {Boolean} isAutoInitial defalut true
34431      */   
34432     isAutoInitial : true, 
34433     
34434     containerWidth: 0,
34435     
34436     /**
34437      * @cfg {Boolean} isHorizontal defalut false
34438      */   
34439     isHorizontal : false, 
34440
34441     currentSize : null,
34442     
34443     tag: 'div',
34444     
34445     cls: '',
34446     
34447     bricks: null, //CompositeElement
34448     
34449     cols : 1,
34450     
34451     _isLayoutInited : false,
34452     
34453 //    isAlternative : false, // only use for vertical layout...
34454     
34455     /**
34456      * @cfg {Number} alternativePadWidth padding below box..
34457      */   
34458     alternativePadWidth : 50,
34459     
34460     selectedBrick : [],
34461     
34462     getAutoCreate : function(){
34463         
34464         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34465         
34466         var cfg = {
34467             tag: this.tag,
34468             cls: 'blog-masonary-wrapper ' + this.cls,
34469             cn : {
34470                 cls : 'mas-boxes masonary'
34471             }
34472         };
34473         
34474         return cfg;
34475     },
34476     
34477     getChildContainer: function( )
34478     {
34479         if (this.boxesEl) {
34480             return this.boxesEl;
34481         }
34482         
34483         this.boxesEl = this.el.select('.mas-boxes').first();
34484         
34485         return this.boxesEl;
34486     },
34487     
34488     
34489     initEvents : function()
34490     {
34491         var _this = this;
34492         
34493         if(this.isAutoInitial){
34494             Roo.log('hook children rendered');
34495             this.on('childrenrendered', function() {
34496                 Roo.log('children rendered');
34497                 _this.initial();
34498             } ,this);
34499         }
34500     },
34501     
34502     initial : function()
34503     {
34504         this.selectedBrick = [];
34505         
34506         this.currentSize = this.el.getBox(true);
34507         
34508         Roo.EventManager.onWindowResize(this.resize, this); 
34509
34510         if(!this.isAutoInitial){
34511             this.layout();
34512             return;
34513         }
34514         
34515         this.layout();
34516         
34517         return;
34518         //this.layout.defer(500,this);
34519         
34520     },
34521     
34522     resize : function()
34523     {
34524         var cs = this.el.getBox(true);
34525         
34526         if (
34527                 this.currentSize.width == cs.width && 
34528                 this.currentSize.x == cs.x && 
34529                 this.currentSize.height == cs.height && 
34530                 this.currentSize.y == cs.y 
34531         ) {
34532             Roo.log("no change in with or X or Y");
34533             return;
34534         }
34535         
34536         this.currentSize = cs;
34537         
34538         this.layout();
34539         
34540     },
34541     
34542     layout : function()
34543     {   
34544         this._resetLayout();
34545         
34546         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34547         
34548         this.layoutItems( isInstant );
34549       
34550         this._isLayoutInited = true;
34551         
34552         this.fireEvent('layout', this);
34553         
34554     },
34555     
34556     _resetLayout : function()
34557     {
34558         if(this.isHorizontal){
34559             this.horizontalMeasureColumns();
34560             return;
34561         }
34562         
34563         this.verticalMeasureColumns();
34564         
34565     },
34566     
34567     verticalMeasureColumns : function()
34568     {
34569         this.getContainerWidth();
34570         
34571 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34572 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34573 //            return;
34574 //        }
34575         
34576         var boxWidth = this.boxWidth + this.padWidth;
34577         
34578         if(this.containerWidth < this.boxWidth){
34579             boxWidth = this.containerWidth
34580         }
34581         
34582         var containerWidth = this.containerWidth;
34583         
34584         var cols = Math.floor(containerWidth / boxWidth);
34585         
34586         this.cols = Math.max( cols, 1 );
34587         
34588         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34589         
34590         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34591         
34592         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34593         
34594         this.colWidth = boxWidth + avail - this.padWidth;
34595         
34596         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34597         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34598     },
34599     
34600     horizontalMeasureColumns : function()
34601     {
34602         this.getContainerWidth();
34603         
34604         var boxWidth = this.boxWidth;
34605         
34606         if(this.containerWidth < boxWidth){
34607             boxWidth = this.containerWidth;
34608         }
34609         
34610         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34611         
34612         this.el.setHeight(boxWidth);
34613         
34614     },
34615     
34616     getContainerWidth : function()
34617     {
34618         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34619     },
34620     
34621     layoutItems : function( isInstant )
34622     {
34623         Roo.log(this.bricks);
34624         
34625         var items = Roo.apply([], this.bricks);
34626         
34627         if(this.isHorizontal){
34628             this._horizontalLayoutItems( items , isInstant );
34629             return;
34630         }
34631         
34632 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34633 //            this._verticalAlternativeLayoutItems( items , isInstant );
34634 //            return;
34635 //        }
34636         
34637         this._verticalLayoutItems( items , isInstant );
34638         
34639     },
34640     
34641     _verticalLayoutItems : function ( items , isInstant)
34642     {
34643         if ( !items || !items.length ) {
34644             return;
34645         }
34646         
34647         var standard = [
34648             ['xs', 'xs', 'xs', 'tall'],
34649             ['xs', 'xs', 'tall'],
34650             ['xs', 'xs', 'sm'],
34651             ['xs', 'xs', 'xs'],
34652             ['xs', 'tall'],
34653             ['xs', 'sm'],
34654             ['xs', 'xs'],
34655             ['xs'],
34656             
34657             ['sm', 'xs', 'xs'],
34658             ['sm', 'xs'],
34659             ['sm'],
34660             
34661             ['tall', 'xs', 'xs', 'xs'],
34662             ['tall', 'xs', 'xs'],
34663             ['tall', 'xs'],
34664             ['tall']
34665             
34666         ];
34667         
34668         var queue = [];
34669         
34670         var boxes = [];
34671         
34672         var box = [];
34673         
34674         Roo.each(items, function(item, k){
34675             
34676             switch (item.size) {
34677                 // these layouts take up a full box,
34678                 case 'md' :
34679                 case 'md-left' :
34680                 case 'md-right' :
34681                 case 'wide' :
34682                     
34683                     if(box.length){
34684                         boxes.push(box);
34685                         box = [];
34686                     }
34687                     
34688                     boxes.push([item]);
34689                     
34690                     break;
34691                     
34692                 case 'xs' :
34693                 case 'sm' :
34694                 case 'tall' :
34695                     
34696                     box.push(item);
34697                     
34698                     break;
34699                 default :
34700                     break;
34701                     
34702             }
34703             
34704         }, this);
34705         
34706         if(box.length){
34707             boxes.push(box);
34708             box = [];
34709         }
34710         
34711         var filterPattern = function(box, length)
34712         {
34713             if(!box.length){
34714                 return;
34715             }
34716             
34717             var match = false;
34718             
34719             var pattern = box.slice(0, length);
34720             
34721             var format = [];
34722             
34723             Roo.each(pattern, function(i){
34724                 format.push(i.size);
34725             }, this);
34726             
34727             Roo.each(standard, function(s){
34728                 
34729                 if(String(s) != String(format)){
34730                     return;
34731                 }
34732                 
34733                 match = true;
34734                 return false;
34735                 
34736             }, this);
34737             
34738             if(!match && length == 1){
34739                 return;
34740             }
34741             
34742             if(!match){
34743                 filterPattern(box, length - 1);
34744                 return;
34745             }
34746                 
34747             queue.push(pattern);
34748
34749             box = box.slice(length, box.length);
34750
34751             filterPattern(box, 4);
34752
34753             return;
34754             
34755         }
34756         
34757         Roo.each(boxes, function(box, k){
34758             
34759             if(!box.length){
34760                 return;
34761             }
34762             
34763             if(box.length == 1){
34764                 queue.push(box);
34765                 return;
34766             }
34767             
34768             filterPattern(box, 4);
34769             
34770         }, this);
34771         
34772         this._processVerticalLayoutQueue( queue, isInstant );
34773         
34774     },
34775     
34776 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34777 //    {
34778 //        if ( !items || !items.length ) {
34779 //            return;
34780 //        }
34781 //
34782 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34783 //        
34784 //    },
34785     
34786     _horizontalLayoutItems : function ( items , isInstant)
34787     {
34788         if ( !items || !items.length || items.length < 3) {
34789             return;
34790         }
34791         
34792         items.reverse();
34793         
34794         var eItems = items.slice(0, 3);
34795         
34796         items = items.slice(3, items.length);
34797         
34798         var standard = [
34799             ['xs', 'xs', 'xs', 'wide'],
34800             ['xs', 'xs', 'wide'],
34801             ['xs', 'xs', 'sm'],
34802             ['xs', 'xs', 'xs'],
34803             ['xs', 'wide'],
34804             ['xs', 'sm'],
34805             ['xs', 'xs'],
34806             ['xs'],
34807             
34808             ['sm', 'xs', 'xs'],
34809             ['sm', 'xs'],
34810             ['sm'],
34811             
34812             ['wide', 'xs', 'xs', 'xs'],
34813             ['wide', 'xs', 'xs'],
34814             ['wide', 'xs'],
34815             ['wide'],
34816             
34817             ['wide-thin']
34818         ];
34819         
34820         var queue = [];
34821         
34822         var boxes = [];
34823         
34824         var box = [];
34825         
34826         Roo.each(items, function(item, k){
34827             
34828             switch (item.size) {
34829                 case 'md' :
34830                 case 'md-left' :
34831                 case 'md-right' :
34832                 case 'tall' :
34833                     
34834                     if(box.length){
34835                         boxes.push(box);
34836                         box = [];
34837                     }
34838                     
34839                     boxes.push([item]);
34840                     
34841                     break;
34842                     
34843                 case 'xs' :
34844                 case 'sm' :
34845                 case 'wide' :
34846                 case 'wide-thin' :
34847                     
34848                     box.push(item);
34849                     
34850                     break;
34851                 default :
34852                     break;
34853                     
34854             }
34855             
34856         }, this);
34857         
34858         if(box.length){
34859             boxes.push(box);
34860             box = [];
34861         }
34862         
34863         var filterPattern = function(box, length)
34864         {
34865             if(!box.length){
34866                 return;
34867             }
34868             
34869             var match = false;
34870             
34871             var pattern = box.slice(0, length);
34872             
34873             var format = [];
34874             
34875             Roo.each(pattern, function(i){
34876                 format.push(i.size);
34877             }, this);
34878             
34879             Roo.each(standard, function(s){
34880                 
34881                 if(String(s) != String(format)){
34882                     return;
34883                 }
34884                 
34885                 match = true;
34886                 return false;
34887                 
34888             }, this);
34889             
34890             if(!match && length == 1){
34891                 return;
34892             }
34893             
34894             if(!match){
34895                 filterPattern(box, length - 1);
34896                 return;
34897             }
34898                 
34899             queue.push(pattern);
34900
34901             box = box.slice(length, box.length);
34902
34903             filterPattern(box, 4);
34904
34905             return;
34906             
34907         }
34908         
34909         Roo.each(boxes, function(box, k){
34910             
34911             if(!box.length){
34912                 return;
34913             }
34914             
34915             if(box.length == 1){
34916                 queue.push(box);
34917                 return;
34918             }
34919             
34920             filterPattern(box, 4);
34921             
34922         }, this);
34923         
34924         
34925         var prune = [];
34926         
34927         var pos = this.el.getBox(true);
34928         
34929         var minX = pos.x;
34930         
34931         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34932         
34933         var hit_end = false;
34934         
34935         Roo.each(queue, function(box){
34936             
34937             if(hit_end){
34938                 
34939                 Roo.each(box, function(b){
34940                 
34941                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34942                     b.el.hide();
34943
34944                 }, this);
34945
34946                 return;
34947             }
34948             
34949             var mx = 0;
34950             
34951             Roo.each(box, function(b){
34952                 
34953                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34954                 b.el.show();
34955
34956                 mx = Math.max(mx, b.x);
34957                 
34958             }, this);
34959             
34960             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34961             
34962             if(maxX < minX){
34963                 
34964                 Roo.each(box, function(b){
34965                 
34966                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34967                     b.el.hide();
34968                     
34969                 }, this);
34970                 
34971                 hit_end = true;
34972                 
34973                 return;
34974             }
34975             
34976             prune.push(box);
34977             
34978         }, this);
34979         
34980         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34981     },
34982     
34983     /** Sets position of item in DOM
34984     * @param {Element} item
34985     * @param {Number} x - horizontal position
34986     * @param {Number} y - vertical position
34987     * @param {Boolean} isInstant - disables transitions
34988     */
34989     _processVerticalLayoutQueue : function( queue, isInstant )
34990     {
34991         var pos = this.el.getBox(true);
34992         var x = pos.x;
34993         var y = pos.y;
34994         var maxY = [];
34995         
34996         for (var i = 0; i < this.cols; i++){
34997             maxY[i] = pos.y;
34998         }
34999         
35000         Roo.each(queue, function(box, k){
35001             
35002             var col = k % this.cols;
35003             
35004             Roo.each(box, function(b,kk){
35005                 
35006                 b.el.position('absolute');
35007                 
35008                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35009                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35010                 
35011                 if(b.size == 'md-left' || b.size == 'md-right'){
35012                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35013                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35014                 }
35015                 
35016                 b.el.setWidth(width);
35017                 b.el.setHeight(height);
35018                 // iframe?
35019                 b.el.select('iframe',true).setSize(width,height);
35020                 
35021             }, this);
35022             
35023             for (var i = 0; i < this.cols; i++){
35024                 
35025                 if(maxY[i] < maxY[col]){
35026                     col = i;
35027                     continue;
35028                 }
35029                 
35030                 col = Math.min(col, i);
35031                 
35032             }
35033             
35034             x = pos.x + col * (this.colWidth + this.padWidth);
35035             
35036             y = maxY[col];
35037             
35038             var positions = [];
35039             
35040             switch (box.length){
35041                 case 1 :
35042                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35043                     break;
35044                 case 2 :
35045                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35046                     break;
35047                 case 3 :
35048                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35049                     break;
35050                 case 4 :
35051                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35052                     break;
35053                 default :
35054                     break;
35055             }
35056             
35057             Roo.each(box, function(b,kk){
35058                 
35059                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35060                 
35061                 var sz = b.el.getSize();
35062                 
35063                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35064                 
35065             }, this);
35066             
35067         }, this);
35068         
35069         var mY = 0;
35070         
35071         for (var i = 0; i < this.cols; i++){
35072             mY = Math.max(mY, maxY[i]);
35073         }
35074         
35075         this.el.setHeight(mY - pos.y);
35076         
35077     },
35078     
35079 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35080 //    {
35081 //        var pos = this.el.getBox(true);
35082 //        var x = pos.x;
35083 //        var y = pos.y;
35084 //        var maxX = pos.right;
35085 //        
35086 //        var maxHeight = 0;
35087 //        
35088 //        Roo.each(items, function(item, k){
35089 //            
35090 //            var c = k % 2;
35091 //            
35092 //            item.el.position('absolute');
35093 //                
35094 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35095 //
35096 //            item.el.setWidth(width);
35097 //
35098 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35099 //
35100 //            item.el.setHeight(height);
35101 //            
35102 //            if(c == 0){
35103 //                item.el.setXY([x, y], isInstant ? false : true);
35104 //            } else {
35105 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35106 //            }
35107 //            
35108 //            y = y + height + this.alternativePadWidth;
35109 //            
35110 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35111 //            
35112 //        }, this);
35113 //        
35114 //        this.el.setHeight(maxHeight);
35115 //        
35116 //    },
35117     
35118     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35119     {
35120         var pos = this.el.getBox(true);
35121         
35122         var minX = pos.x;
35123         var minY = pos.y;
35124         
35125         var maxX = pos.right;
35126         
35127         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35128         
35129         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35130         
35131         Roo.each(queue, function(box, k){
35132             
35133             Roo.each(box, function(b, kk){
35134                 
35135                 b.el.position('absolute');
35136                 
35137                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35138                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35139                 
35140                 if(b.size == 'md-left' || b.size == 'md-right'){
35141                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35142                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35143                 }
35144                 
35145                 b.el.setWidth(width);
35146                 b.el.setHeight(height);
35147                 
35148             }, this);
35149             
35150             if(!box.length){
35151                 return;
35152             }
35153             
35154             var positions = [];
35155             
35156             switch (box.length){
35157                 case 1 :
35158                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35159                     break;
35160                 case 2 :
35161                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35162                     break;
35163                 case 3 :
35164                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35165                     break;
35166                 case 4 :
35167                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35168                     break;
35169                 default :
35170                     break;
35171             }
35172             
35173             Roo.each(box, function(b,kk){
35174                 
35175                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35176                 
35177                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35178                 
35179             }, this);
35180             
35181         }, this);
35182         
35183     },
35184     
35185     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35186     {
35187         Roo.each(eItems, function(b,k){
35188             
35189             b.size = (k == 0) ? 'sm' : 'xs';
35190             b.x = (k == 0) ? 2 : 1;
35191             b.y = (k == 0) ? 2 : 1;
35192             
35193             b.el.position('absolute');
35194             
35195             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35196                 
35197             b.el.setWidth(width);
35198             
35199             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35200             
35201             b.el.setHeight(height);
35202             
35203         }, this);
35204
35205         var positions = [];
35206         
35207         positions.push({
35208             x : maxX - this.unitWidth * 2 - this.gutter,
35209             y : minY
35210         });
35211         
35212         positions.push({
35213             x : maxX - this.unitWidth,
35214             y : minY + (this.unitWidth + this.gutter) * 2
35215         });
35216         
35217         positions.push({
35218             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35219             y : minY
35220         });
35221         
35222         Roo.each(eItems, function(b,k){
35223             
35224             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35225
35226         }, this);
35227         
35228     },
35229     
35230     getVerticalOneBoxColPositions : function(x, y, box)
35231     {
35232         var pos = [];
35233         
35234         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35235         
35236         if(box[0].size == 'md-left'){
35237             rand = 0;
35238         }
35239         
35240         if(box[0].size == 'md-right'){
35241             rand = 1;
35242         }
35243         
35244         pos.push({
35245             x : x + (this.unitWidth + this.gutter) * rand,
35246             y : y
35247         });
35248         
35249         return pos;
35250     },
35251     
35252     getVerticalTwoBoxColPositions : function(x, y, box)
35253     {
35254         var pos = [];
35255         
35256         if(box[0].size == 'xs'){
35257             
35258             pos.push({
35259                 x : x,
35260                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35261             });
35262
35263             pos.push({
35264                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35265                 y : y
35266             });
35267             
35268             return pos;
35269             
35270         }
35271         
35272         pos.push({
35273             x : x,
35274             y : y
35275         });
35276
35277         pos.push({
35278             x : x + (this.unitWidth + this.gutter) * 2,
35279             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35280         });
35281         
35282         return pos;
35283         
35284     },
35285     
35286     getVerticalThreeBoxColPositions : function(x, y, box)
35287     {
35288         var pos = [];
35289         
35290         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35291             
35292             pos.push({
35293                 x : x,
35294                 y : y
35295             });
35296
35297             pos.push({
35298                 x : x + (this.unitWidth + this.gutter) * 1,
35299                 y : y
35300             });
35301             
35302             pos.push({
35303                 x : x + (this.unitWidth + this.gutter) * 2,
35304                 y : y
35305             });
35306             
35307             return pos;
35308             
35309         }
35310         
35311         if(box[0].size == 'xs' && box[1].size == 'xs'){
35312             
35313             pos.push({
35314                 x : x,
35315                 y : y
35316             });
35317
35318             pos.push({
35319                 x : x,
35320                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35321             });
35322             
35323             pos.push({
35324                 x : x + (this.unitWidth + this.gutter) * 1,
35325                 y : y
35326             });
35327             
35328             return pos;
35329             
35330         }
35331         
35332         pos.push({
35333             x : x,
35334             y : y
35335         });
35336
35337         pos.push({
35338             x : x + (this.unitWidth + this.gutter) * 2,
35339             y : y
35340         });
35341
35342         pos.push({
35343             x : x + (this.unitWidth + this.gutter) * 2,
35344             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35345         });
35346             
35347         return pos;
35348         
35349     },
35350     
35351     getVerticalFourBoxColPositions : function(x, y, box)
35352     {
35353         var pos = [];
35354         
35355         if(box[0].size == 'xs'){
35356             
35357             pos.push({
35358                 x : x,
35359                 y : y
35360             });
35361
35362             pos.push({
35363                 x : x,
35364                 y : y + (this.unitHeight + this.gutter) * 1
35365             });
35366             
35367             pos.push({
35368                 x : x,
35369                 y : y + (this.unitHeight + this.gutter) * 2
35370             });
35371             
35372             pos.push({
35373                 x : x + (this.unitWidth + this.gutter) * 1,
35374                 y : y
35375             });
35376             
35377             return pos;
35378             
35379         }
35380         
35381         pos.push({
35382             x : x,
35383             y : y
35384         });
35385
35386         pos.push({
35387             x : x + (this.unitWidth + this.gutter) * 2,
35388             y : y
35389         });
35390
35391         pos.push({
35392             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35393             y : y + (this.unitHeight + this.gutter) * 1
35394         });
35395
35396         pos.push({
35397             x : x + (this.unitWidth + this.gutter) * 2,
35398             y : y + (this.unitWidth + this.gutter) * 2
35399         });
35400
35401         return pos;
35402         
35403     },
35404     
35405     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35406     {
35407         var pos = [];
35408         
35409         if(box[0].size == 'md-left'){
35410             pos.push({
35411                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35412                 y : minY
35413             });
35414             
35415             return pos;
35416         }
35417         
35418         if(box[0].size == 'md-right'){
35419             pos.push({
35420                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35421                 y : minY + (this.unitWidth + this.gutter) * 1
35422             });
35423             
35424             return pos;
35425         }
35426         
35427         var rand = Math.floor(Math.random() * (4 - box[0].y));
35428         
35429         pos.push({
35430             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35431             y : minY + (this.unitWidth + this.gutter) * rand
35432         });
35433         
35434         return pos;
35435         
35436     },
35437     
35438     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35439     {
35440         var pos = [];
35441         
35442         if(box[0].size == 'xs'){
35443             
35444             pos.push({
35445                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35446                 y : minY
35447             });
35448
35449             pos.push({
35450                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35451                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35452             });
35453             
35454             return pos;
35455             
35456         }
35457         
35458         pos.push({
35459             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35460             y : minY
35461         });
35462
35463         pos.push({
35464             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35465             y : minY + (this.unitWidth + this.gutter) * 2
35466         });
35467         
35468         return pos;
35469         
35470     },
35471     
35472     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35473     {
35474         var pos = [];
35475         
35476         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35477             
35478             pos.push({
35479                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35480                 y : minY
35481             });
35482
35483             pos.push({
35484                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35485                 y : minY + (this.unitWidth + this.gutter) * 1
35486             });
35487             
35488             pos.push({
35489                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35490                 y : minY + (this.unitWidth + this.gutter) * 2
35491             });
35492             
35493             return pos;
35494             
35495         }
35496         
35497         if(box[0].size == 'xs' && box[1].size == 'xs'){
35498             
35499             pos.push({
35500                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35501                 y : minY
35502             });
35503
35504             pos.push({
35505                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35506                 y : minY
35507             });
35508             
35509             pos.push({
35510                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35511                 y : minY + (this.unitWidth + this.gutter) * 1
35512             });
35513             
35514             return pos;
35515             
35516         }
35517         
35518         pos.push({
35519             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35520             y : minY
35521         });
35522
35523         pos.push({
35524             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35525             y : minY + (this.unitWidth + this.gutter) * 2
35526         });
35527
35528         pos.push({
35529             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35530             y : minY + (this.unitWidth + this.gutter) * 2
35531         });
35532             
35533         return pos;
35534         
35535     },
35536     
35537     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35538     {
35539         var pos = [];
35540         
35541         if(box[0].size == 'xs'){
35542             
35543             pos.push({
35544                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35545                 y : minY
35546             });
35547
35548             pos.push({
35549                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35550                 y : minY
35551             });
35552             
35553             pos.push({
35554                 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),
35555                 y : minY
35556             });
35557             
35558             pos.push({
35559                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35560                 y : minY + (this.unitWidth + this.gutter) * 1
35561             });
35562             
35563             return pos;
35564             
35565         }
35566         
35567         pos.push({
35568             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35569             y : minY
35570         });
35571         
35572         pos.push({
35573             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35574             y : minY + (this.unitWidth + this.gutter) * 2
35575         });
35576         
35577         pos.push({
35578             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35579             y : minY + (this.unitWidth + this.gutter) * 2
35580         });
35581         
35582         pos.push({
35583             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),
35584             y : minY + (this.unitWidth + this.gutter) * 2
35585         });
35586
35587         return pos;
35588         
35589     },
35590     
35591     /**
35592     * remove a Masonry Brick
35593     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35594     */
35595     removeBrick : function(brick_id)
35596     {
35597         if (!brick_id) {
35598             return;
35599         }
35600         
35601         for (var i = 0; i<this.bricks.length; i++) {
35602             if (this.bricks[i].id == brick_id) {
35603                 this.bricks.splice(i,1);
35604                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35605                 this.initial();
35606             }
35607         }
35608     },
35609     
35610     /**
35611     * adds a Masonry Brick
35612     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35613     */
35614     addBrick : function(cfg)
35615     {
35616         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35617         //this.register(cn);
35618         cn.parentId = this.id;
35619         cn.render(this.el);
35620         return cn;
35621     },
35622     
35623     /**
35624     * register a Masonry Brick
35625     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35626     */
35627     
35628     register : function(brick)
35629     {
35630         this.bricks.push(brick);
35631         brick.masonryId = this.id;
35632     },
35633     
35634     /**
35635     * clear all the Masonry Brick
35636     */
35637     clearAll : function()
35638     {
35639         this.bricks = [];
35640         //this.getChildContainer().dom.innerHTML = "";
35641         this.el.dom.innerHTML = '';
35642     },
35643     
35644     getSelected : function()
35645     {
35646         if (!this.selectedBrick) {
35647             return false;
35648         }
35649         
35650         return this.selectedBrick;
35651     }
35652 });
35653
35654 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35655     
35656     groups: {},
35657      /**
35658     * register a Masonry Layout
35659     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35660     */
35661     
35662     register : function(layout)
35663     {
35664         this.groups[layout.id] = layout;
35665     },
35666     /**
35667     * fetch a  Masonry Layout based on the masonry layout ID
35668     * @param {string} the masonry layout to add
35669     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35670     */
35671     
35672     get: function(layout_id) {
35673         if (typeof(this.groups[layout_id]) == 'undefined') {
35674             return false;
35675         }
35676         return this.groups[layout_id] ;
35677     }
35678     
35679     
35680     
35681 });
35682
35683  
35684
35685  /**
35686  *
35687  * This is based on 
35688  * http://masonry.desandro.com
35689  *
35690  * The idea is to render all the bricks based on vertical width...
35691  *
35692  * The original code extends 'outlayer' - we might need to use that....
35693  * 
35694  */
35695
35696
35697 /**
35698  * @class Roo.bootstrap.LayoutMasonryAuto
35699  * @extends Roo.bootstrap.Component
35700  * Bootstrap Layout Masonry class
35701  * 
35702  * @constructor
35703  * Create a new Element
35704  * @param {Object} config The config object
35705  */
35706
35707 Roo.bootstrap.LayoutMasonryAuto = function(config){
35708     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35709 };
35710
35711 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35712     
35713       /**
35714      * @cfg {Boolean} isFitWidth  - resize the width..
35715      */   
35716     isFitWidth : false,  // options..
35717     /**
35718      * @cfg {Boolean} isOriginLeft = left align?
35719      */   
35720     isOriginLeft : true,
35721     /**
35722      * @cfg {Boolean} isOriginTop = top align?
35723      */   
35724     isOriginTop : false,
35725     /**
35726      * @cfg {Boolean} isLayoutInstant = no animation?
35727      */   
35728     isLayoutInstant : false, // needed?
35729     /**
35730      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35731      */   
35732     isResizingContainer : true,
35733     /**
35734      * @cfg {Number} columnWidth  width of the columns 
35735      */   
35736     
35737     columnWidth : 0,
35738     
35739     /**
35740      * @cfg {Number} maxCols maximum number of columns
35741      */   
35742     
35743     maxCols: 0,
35744     /**
35745      * @cfg {Number} padHeight padding below box..
35746      */   
35747     
35748     padHeight : 10, 
35749     
35750     /**
35751      * @cfg {Boolean} isAutoInitial defalut true
35752      */   
35753     
35754     isAutoInitial : true, 
35755     
35756     // private?
35757     gutter : 0,
35758     
35759     containerWidth: 0,
35760     initialColumnWidth : 0,
35761     currentSize : null,
35762     
35763     colYs : null, // array.
35764     maxY : 0,
35765     padWidth: 10,
35766     
35767     
35768     tag: 'div',
35769     cls: '',
35770     bricks: null, //CompositeElement
35771     cols : 0, // array?
35772     // element : null, // wrapped now this.el
35773     _isLayoutInited : null, 
35774     
35775     
35776     getAutoCreate : function(){
35777         
35778         var cfg = {
35779             tag: this.tag,
35780             cls: 'blog-masonary-wrapper ' + this.cls,
35781             cn : {
35782                 cls : 'mas-boxes masonary'
35783             }
35784         };
35785         
35786         return cfg;
35787     },
35788     
35789     getChildContainer: function( )
35790     {
35791         if (this.boxesEl) {
35792             return this.boxesEl;
35793         }
35794         
35795         this.boxesEl = this.el.select('.mas-boxes').first();
35796         
35797         return this.boxesEl;
35798     },
35799     
35800     
35801     initEvents : function()
35802     {
35803         var _this = this;
35804         
35805         if(this.isAutoInitial){
35806             Roo.log('hook children rendered');
35807             this.on('childrenrendered', function() {
35808                 Roo.log('children rendered');
35809                 _this.initial();
35810             } ,this);
35811         }
35812         
35813     },
35814     
35815     initial : function()
35816     {
35817         this.reloadItems();
35818
35819         this.currentSize = this.el.getBox(true);
35820
35821         /// was window resize... - let's see if this works..
35822         Roo.EventManager.onWindowResize(this.resize, this); 
35823
35824         if(!this.isAutoInitial){
35825             this.layout();
35826             return;
35827         }
35828         
35829         this.layout.defer(500,this);
35830     },
35831     
35832     reloadItems: function()
35833     {
35834         this.bricks = this.el.select('.masonry-brick', true);
35835         
35836         this.bricks.each(function(b) {
35837             //Roo.log(b.getSize());
35838             if (!b.attr('originalwidth')) {
35839                 b.attr('originalwidth',  b.getSize().width);
35840             }
35841             
35842         });
35843         
35844         Roo.log(this.bricks.elements.length);
35845     },
35846     
35847     resize : function()
35848     {
35849         Roo.log('resize');
35850         var cs = this.el.getBox(true);
35851         
35852         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35853             Roo.log("no change in with or X");
35854             return;
35855         }
35856         this.currentSize = cs;
35857         this.layout();
35858     },
35859     
35860     layout : function()
35861     {
35862          Roo.log('layout');
35863         this._resetLayout();
35864         //this._manageStamps();
35865       
35866         // don't animate first layout
35867         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35868         this.layoutItems( isInstant );
35869       
35870         // flag for initalized
35871         this._isLayoutInited = true;
35872     },
35873     
35874     layoutItems : function( isInstant )
35875     {
35876         //var items = this._getItemsForLayout( this.items );
35877         // original code supports filtering layout items.. we just ignore it..
35878         
35879         this._layoutItems( this.bricks , isInstant );
35880       
35881         this._postLayout();
35882     },
35883     _layoutItems : function ( items , isInstant)
35884     {
35885        //this.fireEvent( 'layout', this, items );
35886     
35887
35888         if ( !items || !items.elements.length ) {
35889           // no items, emit event with empty array
35890             return;
35891         }
35892
35893         var queue = [];
35894         items.each(function(item) {
35895             Roo.log("layout item");
35896             Roo.log(item);
35897             // get x/y object from method
35898             var position = this._getItemLayoutPosition( item );
35899             // enqueue
35900             position.item = item;
35901             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35902             queue.push( position );
35903         }, this);
35904       
35905         this._processLayoutQueue( queue );
35906     },
35907     /** Sets position of item in DOM
35908     * @param {Element} item
35909     * @param {Number} x - horizontal position
35910     * @param {Number} y - vertical position
35911     * @param {Boolean} isInstant - disables transitions
35912     */
35913     _processLayoutQueue : function( queue )
35914     {
35915         for ( var i=0, len = queue.length; i < len; i++ ) {
35916             var obj = queue[i];
35917             obj.item.position('absolute');
35918             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35919         }
35920     },
35921       
35922     
35923     /**
35924     * Any logic you want to do after each layout,
35925     * i.e. size the container
35926     */
35927     _postLayout : function()
35928     {
35929         this.resizeContainer();
35930     },
35931     
35932     resizeContainer : function()
35933     {
35934         if ( !this.isResizingContainer ) {
35935             return;
35936         }
35937         var size = this._getContainerSize();
35938         if ( size ) {
35939             this.el.setSize(size.width,size.height);
35940             this.boxesEl.setSize(size.width,size.height);
35941         }
35942     },
35943     
35944     
35945     
35946     _resetLayout : function()
35947     {
35948         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35949         this.colWidth = this.el.getWidth();
35950         //this.gutter = this.el.getWidth(); 
35951         
35952         this.measureColumns();
35953
35954         // reset column Y
35955         var i = this.cols;
35956         this.colYs = [];
35957         while (i--) {
35958             this.colYs.push( 0 );
35959         }
35960     
35961         this.maxY = 0;
35962     },
35963
35964     measureColumns : function()
35965     {
35966         this.getContainerWidth();
35967       // if columnWidth is 0, default to outerWidth of first item
35968         if ( !this.columnWidth ) {
35969             var firstItem = this.bricks.first();
35970             Roo.log(firstItem);
35971             this.columnWidth  = this.containerWidth;
35972             if (firstItem && firstItem.attr('originalwidth') ) {
35973                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35974             }
35975             // columnWidth fall back to item of first element
35976             Roo.log("set column width?");
35977                         this.initialColumnWidth = this.columnWidth  ;
35978
35979             // if first elem has no width, default to size of container
35980             
35981         }
35982         
35983         
35984         if (this.initialColumnWidth) {
35985             this.columnWidth = this.initialColumnWidth;
35986         }
35987         
35988         
35989             
35990         // column width is fixed at the top - however if container width get's smaller we should
35991         // reduce it...
35992         
35993         // this bit calcs how man columns..
35994             
35995         var columnWidth = this.columnWidth += this.gutter;
35996       
35997         // calculate columns
35998         var containerWidth = this.containerWidth + this.gutter;
35999         
36000         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36001         // fix rounding errors, typically with gutters
36002         var excess = columnWidth - containerWidth % columnWidth;
36003         
36004         
36005         // if overshoot is less than a pixel, round up, otherwise floor it
36006         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36007         cols = Math[ mathMethod ]( cols );
36008         this.cols = Math.max( cols, 1 );
36009         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36010         
36011          // padding positioning..
36012         var totalColWidth = this.cols * this.columnWidth;
36013         var padavail = this.containerWidth - totalColWidth;
36014         // so for 2 columns - we need 3 'pads'
36015         
36016         var padNeeded = (1+this.cols) * this.padWidth;
36017         
36018         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36019         
36020         this.columnWidth += padExtra
36021         //this.padWidth = Math.floor(padavail /  ( this.cols));
36022         
36023         // adjust colum width so that padding is fixed??
36024         
36025         // we have 3 columns ... total = width * 3
36026         // we have X left over... that should be used by 
36027         
36028         //if (this.expandC) {
36029             
36030         //}
36031         
36032         
36033         
36034     },
36035     
36036     getContainerWidth : function()
36037     {
36038        /* // container is parent if fit width
36039         var container = this.isFitWidth ? this.element.parentNode : this.element;
36040         // check that this.size and size are there
36041         // IE8 triggers resize on body size change, so they might not be
36042         
36043         var size = getSize( container );  //FIXME
36044         this.containerWidth = size && size.innerWidth; //FIXME
36045         */
36046          
36047         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36048         
36049     },
36050     
36051     _getItemLayoutPosition : function( item )  // what is item?
36052     {
36053         // we resize the item to our columnWidth..
36054       
36055         item.setWidth(this.columnWidth);
36056         item.autoBoxAdjust  = false;
36057         
36058         var sz = item.getSize();
36059  
36060         // how many columns does this brick span
36061         var remainder = this.containerWidth % this.columnWidth;
36062         
36063         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36064         // round if off by 1 pixel, otherwise use ceil
36065         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36066         colSpan = Math.min( colSpan, this.cols );
36067         
36068         // normally this should be '1' as we dont' currently allow multi width columns..
36069         
36070         var colGroup = this._getColGroup( colSpan );
36071         // get the minimum Y value from the columns
36072         var minimumY = Math.min.apply( Math, colGroup );
36073         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36074         
36075         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36076          
36077         // position the brick
36078         var position = {
36079             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36080             y: this.currentSize.y + minimumY + this.padHeight
36081         };
36082         
36083         Roo.log(position);
36084         // apply setHeight to necessary columns
36085         var setHeight = minimumY + sz.height + this.padHeight;
36086         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36087         
36088         var setSpan = this.cols + 1 - colGroup.length;
36089         for ( var i = 0; i < setSpan; i++ ) {
36090           this.colYs[ shortColIndex + i ] = setHeight ;
36091         }
36092       
36093         return position;
36094     },
36095     
36096     /**
36097      * @param {Number} colSpan - number of columns the element spans
36098      * @returns {Array} colGroup
36099      */
36100     _getColGroup : function( colSpan )
36101     {
36102         if ( colSpan < 2 ) {
36103           // if brick spans only one column, use all the column Ys
36104           return this.colYs;
36105         }
36106       
36107         var colGroup = [];
36108         // how many different places could this brick fit horizontally
36109         var groupCount = this.cols + 1 - colSpan;
36110         // for each group potential horizontal position
36111         for ( var i = 0; i < groupCount; i++ ) {
36112           // make an array of colY values for that one group
36113           var groupColYs = this.colYs.slice( i, i + colSpan );
36114           // and get the max value of the array
36115           colGroup[i] = Math.max.apply( Math, groupColYs );
36116         }
36117         return colGroup;
36118     },
36119     /*
36120     _manageStamp : function( stamp )
36121     {
36122         var stampSize =  stamp.getSize();
36123         var offset = stamp.getBox();
36124         // get the columns that this stamp affects
36125         var firstX = this.isOriginLeft ? offset.x : offset.right;
36126         var lastX = firstX + stampSize.width;
36127         var firstCol = Math.floor( firstX / this.columnWidth );
36128         firstCol = Math.max( 0, firstCol );
36129         
36130         var lastCol = Math.floor( lastX / this.columnWidth );
36131         // lastCol should not go over if multiple of columnWidth #425
36132         lastCol -= lastX % this.columnWidth ? 0 : 1;
36133         lastCol = Math.min( this.cols - 1, lastCol );
36134         
36135         // set colYs to bottom of the stamp
36136         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36137             stampSize.height;
36138             
36139         for ( var i = firstCol; i <= lastCol; i++ ) {
36140           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36141         }
36142     },
36143     */
36144     
36145     _getContainerSize : function()
36146     {
36147         this.maxY = Math.max.apply( Math, this.colYs );
36148         var size = {
36149             height: this.maxY
36150         };
36151       
36152         if ( this.isFitWidth ) {
36153             size.width = this._getContainerFitWidth();
36154         }
36155       
36156         return size;
36157     },
36158     
36159     _getContainerFitWidth : function()
36160     {
36161         var unusedCols = 0;
36162         // count unused columns
36163         var i = this.cols;
36164         while ( --i ) {
36165           if ( this.colYs[i] !== 0 ) {
36166             break;
36167           }
36168           unusedCols++;
36169         }
36170         // fit container to columns that have been used
36171         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36172     },
36173     
36174     needsResizeLayout : function()
36175     {
36176         var previousWidth = this.containerWidth;
36177         this.getContainerWidth();
36178         return previousWidth !== this.containerWidth;
36179     }
36180  
36181 });
36182
36183  
36184
36185  /*
36186  * - LGPL
36187  *
36188  * element
36189  * 
36190  */
36191
36192 /**
36193  * @class Roo.bootstrap.MasonryBrick
36194  * @extends Roo.bootstrap.Component
36195  * Bootstrap MasonryBrick class
36196  * 
36197  * @constructor
36198  * Create a new MasonryBrick
36199  * @param {Object} config The config object
36200  */
36201
36202 Roo.bootstrap.MasonryBrick = function(config){
36203     
36204     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36205     
36206     Roo.bootstrap.MasonryBrick.register(this);
36207     
36208     this.addEvents({
36209         // raw events
36210         /**
36211          * @event click
36212          * When a MasonryBrick is clcik
36213          * @param {Roo.bootstrap.MasonryBrick} this
36214          * @param {Roo.EventObject} e
36215          */
36216         "click" : true
36217     });
36218 };
36219
36220 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36221     
36222     /**
36223      * @cfg {String} title
36224      */   
36225     title : '',
36226     /**
36227      * @cfg {String} html
36228      */   
36229     html : '',
36230     /**
36231      * @cfg {String} bgimage
36232      */   
36233     bgimage : '',
36234     /**
36235      * @cfg {String} videourl
36236      */   
36237     videourl : '',
36238     /**
36239      * @cfg {String} cls
36240      */   
36241     cls : '',
36242     /**
36243      * @cfg {String} href
36244      */   
36245     href : '',
36246     /**
36247      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36248      */   
36249     size : 'xs',
36250     
36251     /**
36252      * @cfg {String} placetitle (center|bottom)
36253      */   
36254     placetitle : '',
36255     
36256     /**
36257      * @cfg {Boolean} isFitContainer defalut true
36258      */   
36259     isFitContainer : true, 
36260     
36261     /**
36262      * @cfg {Boolean} preventDefault defalut false
36263      */   
36264     preventDefault : false, 
36265     
36266     /**
36267      * @cfg {Boolean} inverse defalut false
36268      */   
36269     maskInverse : false, 
36270     
36271     getAutoCreate : function()
36272     {
36273         if(!this.isFitContainer){
36274             return this.getSplitAutoCreate();
36275         }
36276         
36277         var cls = 'masonry-brick masonry-brick-full';
36278         
36279         if(this.href.length){
36280             cls += ' masonry-brick-link';
36281         }
36282         
36283         if(this.bgimage.length){
36284             cls += ' masonry-brick-image';
36285         }
36286         
36287         if(this.maskInverse){
36288             cls += ' mask-inverse';
36289         }
36290         
36291         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36292             cls += ' enable-mask';
36293         }
36294         
36295         if(this.size){
36296             cls += ' masonry-' + this.size + '-brick';
36297         }
36298         
36299         if(this.placetitle.length){
36300             
36301             switch (this.placetitle) {
36302                 case 'center' :
36303                     cls += ' masonry-center-title';
36304                     break;
36305                 case 'bottom' :
36306                     cls += ' masonry-bottom-title';
36307                     break;
36308                 default:
36309                     break;
36310             }
36311             
36312         } else {
36313             if(!this.html.length && !this.bgimage.length){
36314                 cls += ' masonry-center-title';
36315             }
36316
36317             if(!this.html.length && this.bgimage.length){
36318                 cls += ' masonry-bottom-title';
36319             }
36320         }
36321         
36322         if(this.cls){
36323             cls += ' ' + this.cls;
36324         }
36325         
36326         var cfg = {
36327             tag: (this.href.length) ? 'a' : 'div',
36328             cls: cls,
36329             cn: [
36330                 {
36331                     tag: 'div',
36332                     cls: 'masonry-brick-mask'
36333                 },
36334                 {
36335                     tag: 'div',
36336                     cls: 'masonry-brick-paragraph',
36337                     cn: []
36338                 }
36339             ]
36340         };
36341         
36342         if(this.href.length){
36343             cfg.href = this.href;
36344         }
36345         
36346         var cn = cfg.cn[1].cn;
36347         
36348         if(this.title.length){
36349             cn.push({
36350                 tag: 'h4',
36351                 cls: 'masonry-brick-title',
36352                 html: this.title
36353             });
36354         }
36355         
36356         if(this.html.length){
36357             cn.push({
36358                 tag: 'p',
36359                 cls: 'masonry-brick-text',
36360                 html: this.html
36361             });
36362         }
36363         
36364         if (!this.title.length && !this.html.length) {
36365             cfg.cn[1].cls += ' hide';
36366         }
36367         
36368         if(this.bgimage.length){
36369             cfg.cn.push({
36370                 tag: 'img',
36371                 cls: 'masonry-brick-image-view',
36372                 src: this.bgimage
36373             });
36374         }
36375         
36376         if(this.videourl.length){
36377             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36378             // youtube support only?
36379             cfg.cn.push({
36380                 tag: 'iframe',
36381                 cls: 'masonry-brick-image-view',
36382                 src: vurl,
36383                 frameborder : 0,
36384                 allowfullscreen : true
36385             });
36386         }
36387         
36388         return cfg;
36389         
36390     },
36391     
36392     getSplitAutoCreate : function()
36393     {
36394         var cls = 'masonry-brick masonry-brick-split';
36395         
36396         if(this.href.length){
36397             cls += ' masonry-brick-link';
36398         }
36399         
36400         if(this.bgimage.length){
36401             cls += ' masonry-brick-image';
36402         }
36403         
36404         if(this.size){
36405             cls += ' masonry-' + this.size + '-brick';
36406         }
36407         
36408         switch (this.placetitle) {
36409             case 'center' :
36410                 cls += ' masonry-center-title';
36411                 break;
36412             case 'bottom' :
36413                 cls += ' masonry-bottom-title';
36414                 break;
36415             default:
36416                 if(!this.bgimage.length){
36417                     cls += ' masonry-center-title';
36418                 }
36419
36420                 if(this.bgimage.length){
36421                     cls += ' masonry-bottom-title';
36422                 }
36423                 break;
36424         }
36425         
36426         if(this.cls){
36427             cls += ' ' + this.cls;
36428         }
36429         
36430         var cfg = {
36431             tag: (this.href.length) ? 'a' : 'div',
36432             cls: cls,
36433             cn: [
36434                 {
36435                     tag: 'div',
36436                     cls: 'masonry-brick-split-head',
36437                     cn: [
36438                         {
36439                             tag: 'div',
36440                             cls: 'masonry-brick-paragraph',
36441                             cn: []
36442                         }
36443                     ]
36444                 },
36445                 {
36446                     tag: 'div',
36447                     cls: 'masonry-brick-split-body',
36448                     cn: []
36449                 }
36450             ]
36451         };
36452         
36453         if(this.href.length){
36454             cfg.href = this.href;
36455         }
36456         
36457         if(this.title.length){
36458             cfg.cn[0].cn[0].cn.push({
36459                 tag: 'h4',
36460                 cls: 'masonry-brick-title',
36461                 html: this.title
36462             });
36463         }
36464         
36465         if(this.html.length){
36466             cfg.cn[1].cn.push({
36467                 tag: 'p',
36468                 cls: 'masonry-brick-text',
36469                 html: this.html
36470             });
36471         }
36472
36473         if(this.bgimage.length){
36474             cfg.cn[0].cn.push({
36475                 tag: 'img',
36476                 cls: 'masonry-brick-image-view',
36477                 src: this.bgimage
36478             });
36479         }
36480         
36481         if(this.videourl.length){
36482             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36483             // youtube support only?
36484             cfg.cn[0].cn.cn.push({
36485                 tag: 'iframe',
36486                 cls: 'masonry-brick-image-view',
36487                 src: vurl,
36488                 frameborder : 0,
36489                 allowfullscreen : true
36490             });
36491         }
36492         
36493         return cfg;
36494     },
36495     
36496     initEvents: function() 
36497     {
36498         switch (this.size) {
36499             case 'xs' :
36500                 this.x = 1;
36501                 this.y = 1;
36502                 break;
36503             case 'sm' :
36504                 this.x = 2;
36505                 this.y = 2;
36506                 break;
36507             case 'md' :
36508             case 'md-left' :
36509             case 'md-right' :
36510                 this.x = 3;
36511                 this.y = 3;
36512                 break;
36513             case 'tall' :
36514                 this.x = 2;
36515                 this.y = 3;
36516                 break;
36517             case 'wide' :
36518                 this.x = 3;
36519                 this.y = 2;
36520                 break;
36521             case 'wide-thin' :
36522                 this.x = 3;
36523                 this.y = 1;
36524                 break;
36525                         
36526             default :
36527                 break;
36528         }
36529         
36530         if(Roo.isTouch){
36531             this.el.on('touchstart', this.onTouchStart, this);
36532             this.el.on('touchmove', this.onTouchMove, this);
36533             this.el.on('touchend', this.onTouchEnd, this);
36534             this.el.on('contextmenu', this.onContextMenu, this);
36535         } else {
36536             this.el.on('mouseenter'  ,this.enter, this);
36537             this.el.on('mouseleave', this.leave, this);
36538             this.el.on('click', this.onClick, this);
36539         }
36540         
36541         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36542             this.parent().bricks.push(this);   
36543         }
36544         
36545     },
36546     
36547     onClick: function(e, el)
36548     {
36549         var time = this.endTimer - this.startTimer;
36550         // Roo.log(e.preventDefault());
36551         if(Roo.isTouch){
36552             if(time > 1000){
36553                 e.preventDefault();
36554                 return;
36555             }
36556         }
36557         
36558         if(!this.preventDefault){
36559             return;
36560         }
36561         
36562         e.preventDefault();
36563         
36564         if (this.activeClass != '') {
36565             this.selectBrick();
36566         }
36567         
36568         this.fireEvent('click', this, e);
36569     },
36570     
36571     enter: function(e, el)
36572     {
36573         e.preventDefault();
36574         
36575         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36576             return;
36577         }
36578         
36579         if(this.bgimage.length && this.html.length){
36580             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36581         }
36582     },
36583     
36584     leave: function(e, el)
36585     {
36586         e.preventDefault();
36587         
36588         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36589             return;
36590         }
36591         
36592         if(this.bgimage.length && this.html.length){
36593             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36594         }
36595     },
36596     
36597     onTouchStart: function(e, el)
36598     {
36599 //        e.preventDefault();
36600         
36601         this.touchmoved = false;
36602         
36603         if(!this.isFitContainer){
36604             return;
36605         }
36606         
36607         if(!this.bgimage.length || !this.html.length){
36608             return;
36609         }
36610         
36611         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36612         
36613         this.timer = new Date().getTime();
36614         
36615     },
36616     
36617     onTouchMove: function(e, el)
36618     {
36619         this.touchmoved = true;
36620     },
36621     
36622     onContextMenu : function(e,el)
36623     {
36624         e.preventDefault();
36625         e.stopPropagation();
36626         return false;
36627     },
36628     
36629     onTouchEnd: function(e, el)
36630     {
36631 //        e.preventDefault();
36632         
36633         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36634         
36635             this.leave(e,el);
36636             
36637             return;
36638         }
36639         
36640         if(!this.bgimage.length || !this.html.length){
36641             
36642             if(this.href.length){
36643                 window.location.href = this.href;
36644             }
36645             
36646             return;
36647         }
36648         
36649         if(!this.isFitContainer){
36650             return;
36651         }
36652         
36653         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36654         
36655         window.location.href = this.href;
36656     },
36657     
36658     //selection on single brick only
36659     selectBrick : function() {
36660         
36661         if (!this.parentId) {
36662             return;
36663         }
36664         
36665         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36666         var index = m.selectedBrick.indexOf(this.id);
36667         
36668         if ( index > -1) {
36669             m.selectedBrick.splice(index,1);
36670             this.el.removeClass(this.activeClass);
36671             return;
36672         }
36673         
36674         for(var i = 0; i < m.selectedBrick.length; i++) {
36675             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36676             b.el.removeClass(b.activeClass);
36677         }
36678         
36679         m.selectedBrick = [];
36680         
36681         m.selectedBrick.push(this.id);
36682         this.el.addClass(this.activeClass);
36683         return;
36684     },
36685     
36686     isSelected : function(){
36687         return this.el.hasClass(this.activeClass);
36688         
36689     }
36690 });
36691
36692 Roo.apply(Roo.bootstrap.MasonryBrick, {
36693     
36694     //groups: {},
36695     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36696      /**
36697     * register a Masonry Brick
36698     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36699     */
36700     
36701     register : function(brick)
36702     {
36703         //this.groups[brick.id] = brick;
36704         this.groups.add(brick.id, brick);
36705     },
36706     /**
36707     * fetch a  masonry brick based on the masonry brick ID
36708     * @param {string} the masonry brick to add
36709     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36710     */
36711     
36712     get: function(brick_id) 
36713     {
36714         // if (typeof(this.groups[brick_id]) == 'undefined') {
36715         //     return false;
36716         // }
36717         // return this.groups[brick_id] ;
36718         
36719         if(this.groups.key(brick_id)) {
36720             return this.groups.key(brick_id);
36721         }
36722         
36723         return false;
36724     }
36725     
36726     
36727     
36728 });
36729
36730  /*
36731  * - LGPL
36732  *
36733  * element
36734  * 
36735  */
36736
36737 /**
36738  * @class Roo.bootstrap.Brick
36739  * @extends Roo.bootstrap.Component
36740  * Bootstrap Brick class
36741  * 
36742  * @constructor
36743  * Create a new Brick
36744  * @param {Object} config The config object
36745  */
36746
36747 Roo.bootstrap.Brick = function(config){
36748     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36749     
36750     this.addEvents({
36751         // raw events
36752         /**
36753          * @event click
36754          * When a Brick is click
36755          * @param {Roo.bootstrap.Brick} this
36756          * @param {Roo.EventObject} e
36757          */
36758         "click" : true
36759     });
36760 };
36761
36762 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36763     
36764     /**
36765      * @cfg {String} title
36766      */   
36767     title : '',
36768     /**
36769      * @cfg {String} html
36770      */   
36771     html : '',
36772     /**
36773      * @cfg {String} bgimage
36774      */   
36775     bgimage : '',
36776     /**
36777      * @cfg {String} cls
36778      */   
36779     cls : '',
36780     /**
36781      * @cfg {String} href
36782      */   
36783     href : '',
36784     /**
36785      * @cfg {String} video
36786      */   
36787     video : '',
36788     /**
36789      * @cfg {Boolean} square
36790      */   
36791     square : true,
36792     
36793     getAutoCreate : function()
36794     {
36795         var cls = 'roo-brick';
36796         
36797         if(this.href.length){
36798             cls += ' roo-brick-link';
36799         }
36800         
36801         if(this.bgimage.length){
36802             cls += ' roo-brick-image';
36803         }
36804         
36805         if(!this.html.length && !this.bgimage.length){
36806             cls += ' roo-brick-center-title';
36807         }
36808         
36809         if(!this.html.length && this.bgimage.length){
36810             cls += ' roo-brick-bottom-title';
36811         }
36812         
36813         if(this.cls){
36814             cls += ' ' + this.cls;
36815         }
36816         
36817         var cfg = {
36818             tag: (this.href.length) ? 'a' : 'div',
36819             cls: cls,
36820             cn: [
36821                 {
36822                     tag: 'div',
36823                     cls: 'roo-brick-paragraph',
36824                     cn: []
36825                 }
36826             ]
36827         };
36828         
36829         if(this.href.length){
36830             cfg.href = this.href;
36831         }
36832         
36833         var cn = cfg.cn[0].cn;
36834         
36835         if(this.title.length){
36836             cn.push({
36837                 tag: 'h4',
36838                 cls: 'roo-brick-title',
36839                 html: this.title
36840             });
36841         }
36842         
36843         if(this.html.length){
36844             cn.push({
36845                 tag: 'p',
36846                 cls: 'roo-brick-text',
36847                 html: this.html
36848             });
36849         } else {
36850             cn.cls += ' hide';
36851         }
36852         
36853         if(this.bgimage.length){
36854             cfg.cn.push({
36855                 tag: 'img',
36856                 cls: 'roo-brick-image-view',
36857                 src: this.bgimage
36858             });
36859         }
36860         
36861         return cfg;
36862     },
36863     
36864     initEvents: function() 
36865     {
36866         if(this.title.length || this.html.length){
36867             this.el.on('mouseenter'  ,this.enter, this);
36868             this.el.on('mouseleave', this.leave, this);
36869         }
36870         
36871         Roo.EventManager.onWindowResize(this.resize, this); 
36872         
36873         if(this.bgimage.length){
36874             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36875             this.imageEl.on('load', this.onImageLoad, this);
36876             return;
36877         }
36878         
36879         this.resize();
36880     },
36881     
36882     onImageLoad : function()
36883     {
36884         this.resize();
36885     },
36886     
36887     resize : function()
36888     {
36889         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36890         
36891         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36892         
36893         if(this.bgimage.length){
36894             var image = this.el.select('.roo-brick-image-view', true).first();
36895             
36896             image.setWidth(paragraph.getWidth());
36897             
36898             if(this.square){
36899                 image.setHeight(paragraph.getWidth());
36900             }
36901             
36902             this.el.setHeight(image.getHeight());
36903             paragraph.setHeight(image.getHeight());
36904             
36905         }
36906         
36907     },
36908     
36909     enter: function(e, el)
36910     {
36911         e.preventDefault();
36912         
36913         if(this.bgimage.length){
36914             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36915             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36916         }
36917     },
36918     
36919     leave: function(e, el)
36920     {
36921         e.preventDefault();
36922         
36923         if(this.bgimage.length){
36924             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36925             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36926         }
36927     }
36928     
36929 });
36930
36931  
36932
36933  /*
36934  * - LGPL
36935  *
36936  * Number field 
36937  */
36938
36939 /**
36940  * @class Roo.bootstrap.NumberField
36941  * @extends Roo.bootstrap.Input
36942  * Bootstrap NumberField class
36943  * 
36944  * 
36945  * 
36946  * 
36947  * @constructor
36948  * Create a new NumberField
36949  * @param {Object} config The config object
36950  */
36951
36952 Roo.bootstrap.NumberField = function(config){
36953     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36954 };
36955
36956 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36957     
36958     /**
36959      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36960      */
36961     allowDecimals : true,
36962     /**
36963      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36964      */
36965     decimalSeparator : ".",
36966     /**
36967      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36968      */
36969     decimalPrecision : 2,
36970     /**
36971      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36972      */
36973     allowNegative : true,
36974     
36975     /**
36976      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36977      */
36978     allowZero: true,
36979     /**
36980      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36981      */
36982     minValue : Number.NEGATIVE_INFINITY,
36983     /**
36984      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36985      */
36986     maxValue : Number.MAX_VALUE,
36987     /**
36988      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36989      */
36990     minText : "The minimum value for this field is {0}",
36991     /**
36992      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36993      */
36994     maxText : "The maximum value for this field is {0}",
36995     /**
36996      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36997      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36998      */
36999     nanText : "{0} is not a valid number",
37000     /**
37001      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37002      */
37003     thousandsDelimiter : false,
37004     /**
37005      * @cfg {String} valueAlign alignment of value
37006      */
37007     valueAlign : "left",
37008
37009     getAutoCreate : function()
37010     {
37011         var hiddenInput = {
37012             tag: 'input',
37013             type: 'hidden',
37014             id: Roo.id(),
37015             cls: 'hidden-number-input'
37016         };
37017         
37018         if (this.name) {
37019             hiddenInput.name = this.name;
37020         }
37021         
37022         this.name = '';
37023         
37024         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37025         
37026         this.name = hiddenInput.name;
37027         
37028         if(cfg.cn.length > 0) {
37029             cfg.cn.push(hiddenInput);
37030         }
37031         
37032         return cfg;
37033     },
37034
37035     // private
37036     initEvents : function()
37037     {   
37038         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37039         
37040         var allowed = "0123456789";
37041         
37042         if(this.allowDecimals){
37043             allowed += this.decimalSeparator;
37044         }
37045         
37046         if(this.allowNegative){
37047             allowed += "-";
37048         }
37049         
37050         if(this.thousandsDelimiter) {
37051             allowed += ",";
37052         }
37053         
37054         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37055         
37056         var keyPress = function(e){
37057             
37058             var k = e.getKey();
37059             
37060             var c = e.getCharCode();
37061             
37062             if(
37063                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37064                     allowed.indexOf(String.fromCharCode(c)) === -1
37065             ){
37066                 e.stopEvent();
37067                 return;
37068             }
37069             
37070             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37071                 return;
37072             }
37073             
37074             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37075                 e.stopEvent();
37076             }
37077         };
37078         
37079         this.el.on("keypress", keyPress, this);
37080     },
37081     
37082     validateValue : function(value)
37083     {
37084         
37085         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37086             return false;
37087         }
37088         
37089         var num = this.parseValue(value);
37090         
37091         if(isNaN(num)){
37092             this.markInvalid(String.format(this.nanText, value));
37093             return false;
37094         }
37095         
37096         if(num < this.minValue){
37097             this.markInvalid(String.format(this.minText, this.minValue));
37098             return false;
37099         }
37100         
37101         if(num > this.maxValue){
37102             this.markInvalid(String.format(this.maxText, this.maxValue));
37103             return false;
37104         }
37105         
37106         return true;
37107     },
37108
37109     getValue : function()
37110     {
37111         var v = this.hiddenEl().getValue();
37112         
37113         return this.fixPrecision(this.parseValue(v));
37114     },
37115
37116     parseValue : function(value)
37117     {
37118         if(this.thousandsDelimiter) {
37119             value += "";
37120             r = new RegExp(",", "g");
37121             value = value.replace(r, "");
37122         }
37123         
37124         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37125         return isNaN(value) ? '' : value;
37126     },
37127
37128     fixPrecision : function(value)
37129     {
37130         if(this.thousandsDelimiter) {
37131             value += "";
37132             r = new RegExp(",", "g");
37133             value = value.replace(r, "");
37134         }
37135         
37136         var nan = isNaN(value);
37137         
37138         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37139             return nan ? '' : value;
37140         }
37141         return parseFloat(value).toFixed(this.decimalPrecision);
37142     },
37143
37144     setValue : function(v)
37145     {
37146         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37147         
37148         this.value = v;
37149         
37150         if(this.rendered){
37151             
37152             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37153             
37154             this.inputEl().dom.value = (v == '') ? '' :
37155                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37156             
37157             if(!this.allowZero && v === '0') {
37158                 this.hiddenEl().dom.value = '';
37159                 this.inputEl().dom.value = '';
37160             }
37161             
37162             this.validate();
37163         }
37164     },
37165
37166     decimalPrecisionFcn : function(v)
37167     {
37168         return Math.floor(v);
37169     },
37170
37171     beforeBlur : function()
37172     {
37173         var v = this.parseValue(this.getRawValue());
37174         
37175         if(v || v === 0 || v === ''){
37176             this.setValue(v);
37177         }
37178     },
37179     
37180     hiddenEl : function()
37181     {
37182         return this.el.select('input.hidden-number-input',true).first();
37183     }
37184     
37185 });
37186
37187  
37188
37189 /*
37190 * Licence: LGPL
37191 */
37192
37193 /**
37194  * @class Roo.bootstrap.DocumentSlider
37195  * @extends Roo.bootstrap.Component
37196  * Bootstrap DocumentSlider class
37197  * 
37198  * @constructor
37199  * Create a new DocumentViewer
37200  * @param {Object} config The config object
37201  */
37202
37203 Roo.bootstrap.DocumentSlider = function(config){
37204     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37205     
37206     this.files = [];
37207     
37208     this.addEvents({
37209         /**
37210          * @event initial
37211          * Fire after initEvent
37212          * @param {Roo.bootstrap.DocumentSlider} this
37213          */
37214         "initial" : true,
37215         /**
37216          * @event update
37217          * Fire after update
37218          * @param {Roo.bootstrap.DocumentSlider} this
37219          */
37220         "update" : true,
37221         /**
37222          * @event click
37223          * Fire after click
37224          * @param {Roo.bootstrap.DocumentSlider} this
37225          */
37226         "click" : true
37227     });
37228 };
37229
37230 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37231     
37232     files : false,
37233     
37234     indicator : 0,
37235     
37236     getAutoCreate : function()
37237     {
37238         var cfg = {
37239             tag : 'div',
37240             cls : 'roo-document-slider',
37241             cn : [
37242                 {
37243                     tag : 'div',
37244                     cls : 'roo-document-slider-header',
37245                     cn : [
37246                         {
37247                             tag : 'div',
37248                             cls : 'roo-document-slider-header-title'
37249                         }
37250                     ]
37251                 },
37252                 {
37253                     tag : 'div',
37254                     cls : 'roo-document-slider-body',
37255                     cn : [
37256                         {
37257                             tag : 'div',
37258                             cls : 'roo-document-slider-prev',
37259                             cn : [
37260                                 {
37261                                     tag : 'i',
37262                                     cls : 'fa fa-chevron-left'
37263                                 }
37264                             ]
37265                         },
37266                         {
37267                             tag : 'div',
37268                             cls : 'roo-document-slider-thumb',
37269                             cn : [
37270                                 {
37271                                     tag : 'img',
37272                                     cls : 'roo-document-slider-image'
37273                                 }
37274                             ]
37275                         },
37276                         {
37277                             tag : 'div',
37278                             cls : 'roo-document-slider-next',
37279                             cn : [
37280                                 {
37281                                     tag : 'i',
37282                                     cls : 'fa fa-chevron-right'
37283                                 }
37284                             ]
37285                         }
37286                     ]
37287                 }
37288             ]
37289         };
37290         
37291         return cfg;
37292     },
37293     
37294     initEvents : function()
37295     {
37296         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37297         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37298         
37299         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37300         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37301         
37302         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37303         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37304         
37305         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37306         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37307         
37308         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37309         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37310         
37311         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37312         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37313         
37314         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37315         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37316         
37317         this.thumbEl.on('click', this.onClick, this);
37318         
37319         this.prevIndicator.on('click', this.prev, this);
37320         
37321         this.nextIndicator.on('click', this.next, this);
37322         
37323     },
37324     
37325     initial : function()
37326     {
37327         if(this.files.length){
37328             this.indicator = 1;
37329             this.update()
37330         }
37331         
37332         this.fireEvent('initial', this);
37333     },
37334     
37335     update : function()
37336     {
37337         this.imageEl.attr('src', this.files[this.indicator - 1]);
37338         
37339         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37340         
37341         this.prevIndicator.show();
37342         
37343         if(this.indicator == 1){
37344             this.prevIndicator.hide();
37345         }
37346         
37347         this.nextIndicator.show();
37348         
37349         if(this.indicator == this.files.length){
37350             this.nextIndicator.hide();
37351         }
37352         
37353         this.thumbEl.scrollTo('top');
37354         
37355         this.fireEvent('update', this);
37356     },
37357     
37358     onClick : function(e)
37359     {
37360         e.preventDefault();
37361         
37362         this.fireEvent('click', this);
37363     },
37364     
37365     prev : function(e)
37366     {
37367         e.preventDefault();
37368         
37369         this.indicator = Math.max(1, this.indicator - 1);
37370         
37371         this.update();
37372     },
37373     
37374     next : function(e)
37375     {
37376         e.preventDefault();
37377         
37378         this.indicator = Math.min(this.files.length, this.indicator + 1);
37379         
37380         this.update();
37381     }
37382 });
37383 /*
37384  * - LGPL
37385  *
37386  * RadioSet
37387  *
37388  *
37389  */
37390
37391 /**
37392  * @class Roo.bootstrap.RadioSet
37393  * @extends Roo.bootstrap.Input
37394  * Bootstrap RadioSet class
37395  * @cfg {String} indicatorpos (left|right) default left
37396  * @cfg {Boolean} inline (true|false) inline the element (default true)
37397  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37398  * @constructor
37399  * Create a new RadioSet
37400  * @param {Object} config The config object
37401  */
37402
37403 Roo.bootstrap.RadioSet = function(config){
37404     
37405     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37406     
37407     this.radioes = [];
37408     
37409     Roo.bootstrap.RadioSet.register(this);
37410     
37411     this.addEvents({
37412         /**
37413         * @event check
37414         * Fires when the element is checked or unchecked.
37415         * @param {Roo.bootstrap.RadioSet} this This radio
37416         * @param {Roo.bootstrap.Radio} item The checked item
37417         */
37418        check : true,
37419        /**
37420         * @event click
37421         * Fires when the element is click.
37422         * @param {Roo.bootstrap.RadioSet} this This radio set
37423         * @param {Roo.bootstrap.Radio} item The checked item
37424         * @param {Roo.EventObject} e The event object
37425         */
37426        click : true
37427     });
37428     
37429 };
37430
37431 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37432
37433     radioes : false,
37434     
37435     inline : true,
37436     
37437     weight : '',
37438     
37439     indicatorpos : 'left',
37440     
37441     getAutoCreate : function()
37442     {
37443         var label = {
37444             tag : 'label',
37445             cls : 'roo-radio-set-label',
37446             cn : [
37447                 {
37448                     tag : 'span',
37449                     html : this.fieldLabel
37450                 }
37451             ]
37452         };
37453         if (Roo.bootstrap.version == 3) {
37454             
37455             
37456             if(this.indicatorpos == 'left'){
37457                 label.cn.unshift({
37458                     tag : 'i',
37459                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37460                     tooltip : 'This field is required'
37461                 });
37462             } else {
37463                 label.cn.push({
37464                     tag : 'i',
37465                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37466                     tooltip : 'This field is required'
37467                 });
37468             }
37469         }
37470         var items = {
37471             tag : 'div',
37472             cls : 'roo-radio-set-items'
37473         };
37474         
37475         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37476         
37477         if (align === 'left' && this.fieldLabel.length) {
37478             
37479             items = {
37480                 cls : "roo-radio-set-right", 
37481                 cn: [
37482                     items
37483                 ]
37484             };
37485             
37486             if(this.labelWidth > 12){
37487                 label.style = "width: " + this.labelWidth + 'px';
37488             }
37489             
37490             if(this.labelWidth < 13 && this.labelmd == 0){
37491                 this.labelmd = this.labelWidth;
37492             }
37493             
37494             if(this.labellg > 0){
37495                 label.cls += ' col-lg-' + this.labellg;
37496                 items.cls += ' col-lg-' + (12 - this.labellg);
37497             }
37498             
37499             if(this.labelmd > 0){
37500                 label.cls += ' col-md-' + this.labelmd;
37501                 items.cls += ' col-md-' + (12 - this.labelmd);
37502             }
37503             
37504             if(this.labelsm > 0){
37505                 label.cls += ' col-sm-' + this.labelsm;
37506                 items.cls += ' col-sm-' + (12 - this.labelsm);
37507             }
37508             
37509             if(this.labelxs > 0){
37510                 label.cls += ' col-xs-' + this.labelxs;
37511                 items.cls += ' col-xs-' + (12 - this.labelxs);
37512             }
37513         }
37514         
37515         var cfg = {
37516             tag : 'div',
37517             cls : 'roo-radio-set',
37518             cn : [
37519                 {
37520                     tag : 'input',
37521                     cls : 'roo-radio-set-input',
37522                     type : 'hidden',
37523                     name : this.name,
37524                     value : this.value ? this.value :  ''
37525                 },
37526                 label,
37527                 items
37528             ]
37529         };
37530         
37531         if(this.weight.length){
37532             cfg.cls += ' roo-radio-' + this.weight;
37533         }
37534         
37535         if(this.inline) {
37536             cfg.cls += ' roo-radio-set-inline';
37537         }
37538         
37539         var settings=this;
37540         ['xs','sm','md','lg'].map(function(size){
37541             if (settings[size]) {
37542                 cfg.cls += ' col-' + size + '-' + settings[size];
37543             }
37544         });
37545         
37546         return cfg;
37547         
37548     },
37549
37550     initEvents : function()
37551     {
37552         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37553         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37554         
37555         if(!this.fieldLabel.length){
37556             this.labelEl.hide();
37557         }
37558         
37559         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37560         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37561         
37562         this.indicator = this.indicatorEl();
37563         
37564         if(this.indicator){
37565             this.indicator.addClass('invisible');
37566         }
37567         
37568         this.originalValue = this.getValue();
37569         
37570     },
37571     
37572     inputEl: function ()
37573     {
37574         return this.el.select('.roo-radio-set-input', true).first();
37575     },
37576     
37577     getChildContainer : function()
37578     {
37579         return this.itemsEl;
37580     },
37581     
37582     register : function(item)
37583     {
37584         this.radioes.push(item);
37585         
37586     },
37587     
37588     validate : function()
37589     {   
37590         if(this.getVisibilityEl().hasClass('hidden')){
37591             return true;
37592         }
37593         
37594         var valid = false;
37595         
37596         Roo.each(this.radioes, function(i){
37597             if(!i.checked){
37598                 return;
37599             }
37600             
37601             valid = true;
37602             return false;
37603         });
37604         
37605         if(this.allowBlank) {
37606             return true;
37607         }
37608         
37609         if(this.disabled || valid){
37610             this.markValid();
37611             return true;
37612         }
37613         
37614         this.markInvalid();
37615         return false;
37616         
37617     },
37618     
37619     markValid : function()
37620     {
37621         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37622             this.indicatorEl().removeClass('visible');
37623             this.indicatorEl().addClass('invisible');
37624         }
37625         
37626         
37627         if (Roo.bootstrap.version == 3) {
37628             this.el.removeClass([this.invalidClass, this.validClass]);
37629             this.el.addClass(this.validClass);
37630         } else {
37631             this.el.removeClass(['is-invalid','is-valid']);
37632             this.el.addClass(['is-valid']);
37633         }
37634         this.fireEvent('valid', this);
37635     },
37636     
37637     markInvalid : function(msg)
37638     {
37639         if(this.allowBlank || this.disabled){
37640             return;
37641         }
37642         
37643         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37644             this.indicatorEl().removeClass('invisible');
37645             this.indicatorEl().addClass('visible');
37646         }
37647         if (Roo.bootstrap.version == 3) {
37648             this.el.removeClass([this.invalidClass, this.validClass]);
37649             this.el.addClass(this.invalidClass);
37650         } else {
37651             this.el.removeClass(['is-invalid','is-valid']);
37652             this.el.addClass(['is-invalid']);
37653         }
37654         
37655         this.fireEvent('invalid', this, msg);
37656         
37657     },
37658     
37659     setValue : function(v, suppressEvent)
37660     {   
37661         if(this.value === v){
37662             return;
37663         }
37664         
37665         this.value = v;
37666         
37667         if(this.rendered){
37668             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37669         }
37670         
37671         Roo.each(this.radioes, function(i){
37672             i.checked = false;
37673             i.el.removeClass('checked');
37674         });
37675         
37676         Roo.each(this.radioes, function(i){
37677             
37678             if(i.value === v || i.value.toString() === v.toString()){
37679                 i.checked = true;
37680                 i.el.addClass('checked');
37681                 
37682                 if(suppressEvent !== true){
37683                     this.fireEvent('check', this, i);
37684                 }
37685                 
37686                 return false;
37687             }
37688             
37689         }, this);
37690         
37691         this.validate();
37692     },
37693     
37694     clearInvalid : function(){
37695         
37696         if(!this.el || this.preventMark){
37697             return;
37698         }
37699         
37700         this.el.removeClass([this.invalidClass]);
37701         
37702         this.fireEvent('valid', this);
37703     }
37704     
37705 });
37706
37707 Roo.apply(Roo.bootstrap.RadioSet, {
37708     
37709     groups: {},
37710     
37711     register : function(set)
37712     {
37713         this.groups[set.name] = set;
37714     },
37715     
37716     get: function(name) 
37717     {
37718         if (typeof(this.groups[name]) == 'undefined') {
37719             return false;
37720         }
37721         
37722         return this.groups[name] ;
37723     }
37724     
37725 });
37726 /*
37727  * Based on:
37728  * Ext JS Library 1.1.1
37729  * Copyright(c) 2006-2007, Ext JS, LLC.
37730  *
37731  * Originally Released Under LGPL - original licence link has changed is not relivant.
37732  *
37733  * Fork - LGPL
37734  * <script type="text/javascript">
37735  */
37736
37737
37738 /**
37739  * @class Roo.bootstrap.SplitBar
37740  * @extends Roo.util.Observable
37741  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37742  * <br><br>
37743  * Usage:
37744  * <pre><code>
37745 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37746                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37747 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37748 split.minSize = 100;
37749 split.maxSize = 600;
37750 split.animate = true;
37751 split.on('moved', splitterMoved);
37752 </code></pre>
37753  * @constructor
37754  * Create a new SplitBar
37755  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37756  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37757  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37758  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37759                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37760                         position of the SplitBar).
37761  */
37762 Roo.bootstrap.SplitBar = function(cfg){
37763     
37764     /** @private */
37765     
37766     //{
37767     //  dragElement : elm
37768     //  resizingElement: el,
37769         // optional..
37770     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37771     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37772         // existingProxy ???
37773     //}
37774     
37775     this.el = Roo.get(cfg.dragElement, true);
37776     this.el.dom.unselectable = "on";
37777     /** @private */
37778     this.resizingEl = Roo.get(cfg.resizingElement, true);
37779
37780     /**
37781      * @private
37782      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37783      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37784      * @type Number
37785      */
37786     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37787     
37788     /**
37789      * The minimum size of the resizing element. (Defaults to 0)
37790      * @type Number
37791      */
37792     this.minSize = 0;
37793     
37794     /**
37795      * The maximum size of the resizing element. (Defaults to 2000)
37796      * @type Number
37797      */
37798     this.maxSize = 2000;
37799     
37800     /**
37801      * Whether to animate the transition to the new size
37802      * @type Boolean
37803      */
37804     this.animate = false;
37805     
37806     /**
37807      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37808      * @type Boolean
37809      */
37810     this.useShim = false;
37811     
37812     /** @private */
37813     this.shim = null;
37814     
37815     if(!cfg.existingProxy){
37816         /** @private */
37817         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37818     }else{
37819         this.proxy = Roo.get(cfg.existingProxy).dom;
37820     }
37821     /** @private */
37822     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37823     
37824     /** @private */
37825     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37826     
37827     /** @private */
37828     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37829     
37830     /** @private */
37831     this.dragSpecs = {};
37832     
37833     /**
37834      * @private The adapter to use to positon and resize elements
37835      */
37836     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37837     this.adapter.init(this);
37838     
37839     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37840         /** @private */
37841         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37842         this.el.addClass("roo-splitbar-h");
37843     }else{
37844         /** @private */
37845         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37846         this.el.addClass("roo-splitbar-v");
37847     }
37848     
37849     this.addEvents({
37850         /**
37851          * @event resize
37852          * Fires when the splitter is moved (alias for {@link #event-moved})
37853          * @param {Roo.bootstrap.SplitBar} this
37854          * @param {Number} newSize the new width or height
37855          */
37856         "resize" : true,
37857         /**
37858          * @event moved
37859          * Fires when the splitter is moved
37860          * @param {Roo.bootstrap.SplitBar} this
37861          * @param {Number} newSize the new width or height
37862          */
37863         "moved" : true,
37864         /**
37865          * @event beforeresize
37866          * Fires before the splitter is dragged
37867          * @param {Roo.bootstrap.SplitBar} this
37868          */
37869         "beforeresize" : true,
37870
37871         "beforeapply" : true
37872     });
37873
37874     Roo.util.Observable.call(this);
37875 };
37876
37877 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37878     onStartProxyDrag : function(x, y){
37879         this.fireEvent("beforeresize", this);
37880         if(!this.overlay){
37881             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37882             o.unselectable();
37883             o.enableDisplayMode("block");
37884             // all splitbars share the same overlay
37885             Roo.bootstrap.SplitBar.prototype.overlay = o;
37886         }
37887         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37888         this.overlay.show();
37889         Roo.get(this.proxy).setDisplayed("block");
37890         var size = this.adapter.getElementSize(this);
37891         this.activeMinSize = this.getMinimumSize();;
37892         this.activeMaxSize = this.getMaximumSize();;
37893         var c1 = size - this.activeMinSize;
37894         var c2 = Math.max(this.activeMaxSize - size, 0);
37895         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37896             this.dd.resetConstraints();
37897             this.dd.setXConstraint(
37898                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37899                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37900             );
37901             this.dd.setYConstraint(0, 0);
37902         }else{
37903             this.dd.resetConstraints();
37904             this.dd.setXConstraint(0, 0);
37905             this.dd.setYConstraint(
37906                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37907                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37908             );
37909          }
37910         this.dragSpecs.startSize = size;
37911         this.dragSpecs.startPoint = [x, y];
37912         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37913     },
37914     
37915     /** 
37916      * @private Called after the drag operation by the DDProxy
37917      */
37918     onEndProxyDrag : function(e){
37919         Roo.get(this.proxy).setDisplayed(false);
37920         var endPoint = Roo.lib.Event.getXY(e);
37921         if(this.overlay){
37922             this.overlay.hide();
37923         }
37924         var newSize;
37925         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37926             newSize = this.dragSpecs.startSize + 
37927                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37928                     endPoint[0] - this.dragSpecs.startPoint[0] :
37929                     this.dragSpecs.startPoint[0] - endPoint[0]
37930                 );
37931         }else{
37932             newSize = this.dragSpecs.startSize + 
37933                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37934                     endPoint[1] - this.dragSpecs.startPoint[1] :
37935                     this.dragSpecs.startPoint[1] - endPoint[1]
37936                 );
37937         }
37938         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37939         if(newSize != this.dragSpecs.startSize){
37940             if(this.fireEvent('beforeapply', this, newSize) !== false){
37941                 this.adapter.setElementSize(this, newSize);
37942                 this.fireEvent("moved", this, newSize);
37943                 this.fireEvent("resize", this, newSize);
37944             }
37945         }
37946     },
37947     
37948     /**
37949      * Get the adapter this SplitBar uses
37950      * @return The adapter object
37951      */
37952     getAdapter : function(){
37953         return this.adapter;
37954     },
37955     
37956     /**
37957      * Set the adapter this SplitBar uses
37958      * @param {Object} adapter A SplitBar adapter object
37959      */
37960     setAdapter : function(adapter){
37961         this.adapter = adapter;
37962         this.adapter.init(this);
37963     },
37964     
37965     /**
37966      * Gets the minimum size for the resizing element
37967      * @return {Number} The minimum size
37968      */
37969     getMinimumSize : function(){
37970         return this.minSize;
37971     },
37972     
37973     /**
37974      * Sets the minimum size for the resizing element
37975      * @param {Number} minSize The minimum size
37976      */
37977     setMinimumSize : function(minSize){
37978         this.minSize = minSize;
37979     },
37980     
37981     /**
37982      * Gets the maximum size for the resizing element
37983      * @return {Number} The maximum size
37984      */
37985     getMaximumSize : function(){
37986         return this.maxSize;
37987     },
37988     
37989     /**
37990      * Sets the maximum size for the resizing element
37991      * @param {Number} maxSize The maximum size
37992      */
37993     setMaximumSize : function(maxSize){
37994         this.maxSize = maxSize;
37995     },
37996     
37997     /**
37998      * Sets the initialize size for the resizing element
37999      * @param {Number} size The initial size
38000      */
38001     setCurrentSize : function(size){
38002         var oldAnimate = this.animate;
38003         this.animate = false;
38004         this.adapter.setElementSize(this, size);
38005         this.animate = oldAnimate;
38006     },
38007     
38008     /**
38009      * Destroy this splitbar. 
38010      * @param {Boolean} removeEl True to remove the element
38011      */
38012     destroy : function(removeEl){
38013         if(this.shim){
38014             this.shim.remove();
38015         }
38016         this.dd.unreg();
38017         this.proxy.parentNode.removeChild(this.proxy);
38018         if(removeEl){
38019             this.el.remove();
38020         }
38021     }
38022 });
38023
38024 /**
38025  * @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.
38026  */
38027 Roo.bootstrap.SplitBar.createProxy = function(dir){
38028     var proxy = new Roo.Element(document.createElement("div"));
38029     proxy.unselectable();
38030     var cls = 'roo-splitbar-proxy';
38031     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38032     document.body.appendChild(proxy.dom);
38033     return proxy.dom;
38034 };
38035
38036 /** 
38037  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38038  * Default Adapter. It assumes the splitter and resizing element are not positioned
38039  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38040  */
38041 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38042 };
38043
38044 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38045     // do nothing for now
38046     init : function(s){
38047     
38048     },
38049     /**
38050      * Called before drag operations to get the current size of the resizing element. 
38051      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38052      */
38053      getElementSize : function(s){
38054         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38055             return s.resizingEl.getWidth();
38056         }else{
38057             return s.resizingEl.getHeight();
38058         }
38059     },
38060     
38061     /**
38062      * Called after drag operations to set the size of the resizing element.
38063      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38064      * @param {Number} newSize The new size to set
38065      * @param {Function} onComplete A function to be invoked when resizing is complete
38066      */
38067     setElementSize : function(s, newSize, onComplete){
38068         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38069             if(!s.animate){
38070                 s.resizingEl.setWidth(newSize);
38071                 if(onComplete){
38072                     onComplete(s, newSize);
38073                 }
38074             }else{
38075                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38076             }
38077         }else{
38078             
38079             if(!s.animate){
38080                 s.resizingEl.setHeight(newSize);
38081                 if(onComplete){
38082                     onComplete(s, newSize);
38083                 }
38084             }else{
38085                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38086             }
38087         }
38088     }
38089 };
38090
38091 /** 
38092  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38093  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38094  * Adapter that  moves the splitter element to align with the resized sizing element. 
38095  * Used with an absolute positioned SplitBar.
38096  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38097  * document.body, make sure you assign an id to the body element.
38098  */
38099 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38100     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38101     this.container = Roo.get(container);
38102 };
38103
38104 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38105     init : function(s){
38106         this.basic.init(s);
38107     },
38108     
38109     getElementSize : function(s){
38110         return this.basic.getElementSize(s);
38111     },
38112     
38113     setElementSize : function(s, newSize, onComplete){
38114         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38115     },
38116     
38117     moveSplitter : function(s){
38118         var yes = Roo.bootstrap.SplitBar;
38119         switch(s.placement){
38120             case yes.LEFT:
38121                 s.el.setX(s.resizingEl.getRight());
38122                 break;
38123             case yes.RIGHT:
38124                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38125                 break;
38126             case yes.TOP:
38127                 s.el.setY(s.resizingEl.getBottom());
38128                 break;
38129             case yes.BOTTOM:
38130                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38131                 break;
38132         }
38133     }
38134 };
38135
38136 /**
38137  * Orientation constant - Create a vertical SplitBar
38138  * @static
38139  * @type Number
38140  */
38141 Roo.bootstrap.SplitBar.VERTICAL = 1;
38142
38143 /**
38144  * Orientation constant - Create a horizontal SplitBar
38145  * @static
38146  * @type Number
38147  */
38148 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38149
38150 /**
38151  * Placement constant - The resizing element is to the left of the splitter element
38152  * @static
38153  * @type Number
38154  */
38155 Roo.bootstrap.SplitBar.LEFT = 1;
38156
38157 /**
38158  * Placement constant - The resizing element is to the right of the splitter element
38159  * @static
38160  * @type Number
38161  */
38162 Roo.bootstrap.SplitBar.RIGHT = 2;
38163
38164 /**
38165  * Placement constant - The resizing element is positioned above the splitter element
38166  * @static
38167  * @type Number
38168  */
38169 Roo.bootstrap.SplitBar.TOP = 3;
38170
38171 /**
38172  * Placement constant - The resizing element is positioned under splitter element
38173  * @static
38174  * @type Number
38175  */
38176 Roo.bootstrap.SplitBar.BOTTOM = 4;
38177 Roo.namespace("Roo.bootstrap.layout");/*
38178  * Based on:
38179  * Ext JS Library 1.1.1
38180  * Copyright(c) 2006-2007, Ext JS, LLC.
38181  *
38182  * Originally Released Under LGPL - original licence link has changed is not relivant.
38183  *
38184  * Fork - LGPL
38185  * <script type="text/javascript">
38186  */
38187
38188 /**
38189  * @class Roo.bootstrap.layout.Manager
38190  * @extends Roo.bootstrap.Component
38191  * Base class for layout managers.
38192  */
38193 Roo.bootstrap.layout.Manager = function(config)
38194 {
38195     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38196
38197
38198
38199
38200
38201     /** false to disable window resize monitoring @type Boolean */
38202     this.monitorWindowResize = true;
38203     this.regions = {};
38204     this.addEvents({
38205         /**
38206          * @event layout
38207          * Fires when a layout is performed.
38208          * @param {Roo.LayoutManager} this
38209          */
38210         "layout" : true,
38211         /**
38212          * @event regionresized
38213          * Fires when the user resizes a region.
38214          * @param {Roo.LayoutRegion} region The resized region
38215          * @param {Number} newSize The new size (width for east/west, height for north/south)
38216          */
38217         "regionresized" : true,
38218         /**
38219          * @event regioncollapsed
38220          * Fires when a region is collapsed.
38221          * @param {Roo.LayoutRegion} region The collapsed region
38222          */
38223         "regioncollapsed" : true,
38224         /**
38225          * @event regionexpanded
38226          * Fires when a region is expanded.
38227          * @param {Roo.LayoutRegion} region The expanded region
38228          */
38229         "regionexpanded" : true
38230     });
38231     this.updating = false;
38232
38233     if (config.el) {
38234         this.el = Roo.get(config.el);
38235         this.initEvents();
38236     }
38237
38238 };
38239
38240 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38241
38242
38243     regions : null,
38244
38245     monitorWindowResize : true,
38246
38247
38248     updating : false,
38249
38250
38251     onRender : function(ct, position)
38252     {
38253         if(!this.el){
38254             this.el = Roo.get(ct);
38255             this.initEvents();
38256         }
38257         //this.fireEvent('render',this);
38258     },
38259
38260
38261     initEvents: function()
38262     {
38263
38264
38265         // ie scrollbar fix
38266         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38267             document.body.scroll = "no";
38268         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38269             this.el.position('relative');
38270         }
38271         this.id = this.el.id;
38272         this.el.addClass("roo-layout-container");
38273         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38274         if(this.el.dom != document.body ) {
38275             this.el.on('resize', this.layout,this);
38276             this.el.on('show', this.layout,this);
38277         }
38278
38279     },
38280
38281     /**
38282      * Returns true if this layout is currently being updated
38283      * @return {Boolean}
38284      */
38285     isUpdating : function(){
38286         return this.updating;
38287     },
38288
38289     /**
38290      * Suspend the LayoutManager from doing auto-layouts while
38291      * making multiple add or remove calls
38292      */
38293     beginUpdate : function(){
38294         this.updating = true;
38295     },
38296
38297     /**
38298      * Restore auto-layouts and optionally disable the manager from performing a layout
38299      * @param {Boolean} noLayout true to disable a layout update
38300      */
38301     endUpdate : function(noLayout){
38302         this.updating = false;
38303         if(!noLayout){
38304             this.layout();
38305         }
38306     },
38307
38308     layout: function(){
38309         // abstract...
38310     },
38311
38312     onRegionResized : function(region, newSize){
38313         this.fireEvent("regionresized", region, newSize);
38314         this.layout();
38315     },
38316
38317     onRegionCollapsed : function(region){
38318         this.fireEvent("regioncollapsed", region);
38319     },
38320
38321     onRegionExpanded : function(region){
38322         this.fireEvent("regionexpanded", region);
38323     },
38324
38325     /**
38326      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38327      * performs box-model adjustments.
38328      * @return {Object} The size as an object {width: (the width), height: (the height)}
38329      */
38330     getViewSize : function()
38331     {
38332         var size;
38333         if(this.el.dom != document.body){
38334             size = this.el.getSize();
38335         }else{
38336             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38337         }
38338         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38339         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38340         return size;
38341     },
38342
38343     /**
38344      * Returns the Element this layout is bound to.
38345      * @return {Roo.Element}
38346      */
38347     getEl : function(){
38348         return this.el;
38349     },
38350
38351     /**
38352      * Returns the specified region.
38353      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38354      * @return {Roo.LayoutRegion}
38355      */
38356     getRegion : function(target){
38357         return this.regions[target.toLowerCase()];
38358     },
38359
38360     onWindowResize : function(){
38361         if(this.monitorWindowResize){
38362             this.layout();
38363         }
38364     }
38365 });
38366 /*
38367  * Based on:
38368  * Ext JS Library 1.1.1
38369  * Copyright(c) 2006-2007, Ext JS, LLC.
38370  *
38371  * Originally Released Under LGPL - original licence link has changed is not relivant.
38372  *
38373  * Fork - LGPL
38374  * <script type="text/javascript">
38375  */
38376 /**
38377  * @class Roo.bootstrap.layout.Border
38378  * @extends Roo.bootstrap.layout.Manager
38379  * @builder-top
38380  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38381  * please see: examples/bootstrap/nested.html<br><br>
38382  
38383 <b>The container the layout is rendered into can be either the body element or any other element.
38384 If it is not the body element, the container needs to either be an absolute positioned element,
38385 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38386 the container size if it is not the body element.</b>
38387
38388 * @constructor
38389 * Create a new Border
38390 * @param {Object} config Configuration options
38391  */
38392 Roo.bootstrap.layout.Border = function(config){
38393     config = config || {};
38394     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38395     
38396     
38397     
38398     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38399         if(config[region]){
38400             config[region].region = region;
38401             this.addRegion(config[region]);
38402         }
38403     },this);
38404     
38405 };
38406
38407 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38408
38409 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38410     
38411     parent : false, // this might point to a 'nest' or a ???
38412     
38413     /**
38414      * Creates and adds a new region if it doesn't already exist.
38415      * @param {String} target The target region key (north, south, east, west or center).
38416      * @param {Object} config The regions config object
38417      * @return {BorderLayoutRegion} The new region
38418      */
38419     addRegion : function(config)
38420     {
38421         if(!this.regions[config.region]){
38422             var r = this.factory(config);
38423             this.bindRegion(r);
38424         }
38425         return this.regions[config.region];
38426     },
38427
38428     // private (kinda)
38429     bindRegion : function(r){
38430         this.regions[r.config.region] = r;
38431         
38432         r.on("visibilitychange",    this.layout, this);
38433         r.on("paneladded",          this.layout, this);
38434         r.on("panelremoved",        this.layout, this);
38435         r.on("invalidated",         this.layout, this);
38436         r.on("resized",             this.onRegionResized, this);
38437         r.on("collapsed",           this.onRegionCollapsed, this);
38438         r.on("expanded",            this.onRegionExpanded, this);
38439     },
38440
38441     /**
38442      * Performs a layout update.
38443      */
38444     layout : function()
38445     {
38446         if(this.updating) {
38447             return;
38448         }
38449         
38450         // render all the rebions if they have not been done alreayd?
38451         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38452             if(this.regions[region] && !this.regions[region].bodyEl){
38453                 this.regions[region].onRender(this.el)
38454             }
38455         },this);
38456         
38457         var size = this.getViewSize();
38458         var w = size.width;
38459         var h = size.height;
38460         var centerW = w;
38461         var centerH = h;
38462         var centerY = 0;
38463         var centerX = 0;
38464         //var x = 0, y = 0;
38465
38466         var rs = this.regions;
38467         var north = rs["north"];
38468         var south = rs["south"]; 
38469         var west = rs["west"];
38470         var east = rs["east"];
38471         var center = rs["center"];
38472         //if(this.hideOnLayout){ // not supported anymore
38473             //c.el.setStyle("display", "none");
38474         //}
38475         if(north && north.isVisible()){
38476             var b = north.getBox();
38477             var m = north.getMargins();
38478             b.width = w - (m.left+m.right);
38479             b.x = m.left;
38480             b.y = m.top;
38481             centerY = b.height + b.y + m.bottom;
38482             centerH -= centerY;
38483             north.updateBox(this.safeBox(b));
38484         }
38485         if(south && south.isVisible()){
38486             var b = south.getBox();
38487             var m = south.getMargins();
38488             b.width = w - (m.left+m.right);
38489             b.x = m.left;
38490             var totalHeight = (b.height + m.top + m.bottom);
38491             b.y = h - totalHeight + m.top;
38492             centerH -= totalHeight;
38493             south.updateBox(this.safeBox(b));
38494         }
38495         if(west && west.isVisible()){
38496             var b = west.getBox();
38497             var m = west.getMargins();
38498             b.height = centerH - (m.top+m.bottom);
38499             b.x = m.left;
38500             b.y = centerY + m.top;
38501             var totalWidth = (b.width + m.left + m.right);
38502             centerX += totalWidth;
38503             centerW -= totalWidth;
38504             west.updateBox(this.safeBox(b));
38505         }
38506         if(east && east.isVisible()){
38507             var b = east.getBox();
38508             var m = east.getMargins();
38509             b.height = centerH - (m.top+m.bottom);
38510             var totalWidth = (b.width + m.left + m.right);
38511             b.x = w - totalWidth + m.left;
38512             b.y = centerY + m.top;
38513             centerW -= totalWidth;
38514             east.updateBox(this.safeBox(b));
38515         }
38516         if(center){
38517             var m = center.getMargins();
38518             var centerBox = {
38519                 x: centerX + m.left,
38520                 y: centerY + m.top,
38521                 width: centerW - (m.left+m.right),
38522                 height: centerH - (m.top+m.bottom)
38523             };
38524             //if(this.hideOnLayout){
38525                 //center.el.setStyle("display", "block");
38526             //}
38527             center.updateBox(this.safeBox(centerBox));
38528         }
38529         this.el.repaint();
38530         this.fireEvent("layout", this);
38531     },
38532
38533     // private
38534     safeBox : function(box){
38535         box.width = Math.max(0, box.width);
38536         box.height = Math.max(0, box.height);
38537         return box;
38538     },
38539
38540     /**
38541      * Adds a ContentPanel (or subclass) to this layout.
38542      * @param {String} target The target region key (north, south, east, west or center).
38543      * @param {Roo.ContentPanel} panel The panel to add
38544      * @return {Roo.ContentPanel} The added panel
38545      */
38546     add : function(target, panel){
38547          
38548         target = target.toLowerCase();
38549         return this.regions[target].add(panel);
38550     },
38551
38552     /**
38553      * Remove a ContentPanel (or subclass) to this layout.
38554      * @param {String} target The target region key (north, south, east, west or center).
38555      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38556      * @return {Roo.ContentPanel} The removed panel
38557      */
38558     remove : function(target, panel){
38559         target = target.toLowerCase();
38560         return this.regions[target].remove(panel);
38561     },
38562
38563     /**
38564      * Searches all regions for a panel with the specified id
38565      * @param {String} panelId
38566      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38567      */
38568     findPanel : function(panelId){
38569         var rs = this.regions;
38570         for(var target in rs){
38571             if(typeof rs[target] != "function"){
38572                 var p = rs[target].getPanel(panelId);
38573                 if(p){
38574                     return p;
38575                 }
38576             }
38577         }
38578         return null;
38579     },
38580
38581     /**
38582      * Searches all regions for a panel with the specified id and activates (shows) it.
38583      * @param {String/ContentPanel} panelId The panels id or the panel itself
38584      * @return {Roo.ContentPanel} The shown panel or null
38585      */
38586     showPanel : function(panelId) {
38587       var rs = this.regions;
38588       for(var target in rs){
38589          var r = rs[target];
38590          if(typeof r != "function"){
38591             if(r.hasPanel(panelId)){
38592                return r.showPanel(panelId);
38593             }
38594          }
38595       }
38596       return null;
38597    },
38598
38599    /**
38600      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38601      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38602      */
38603    /*
38604     restoreState : function(provider){
38605         if(!provider){
38606             provider = Roo.state.Manager;
38607         }
38608         var sm = new Roo.LayoutStateManager();
38609         sm.init(this, provider);
38610     },
38611 */
38612  
38613  
38614     /**
38615      * Adds a xtype elements to the layout.
38616      * <pre><code>
38617
38618 layout.addxtype({
38619        xtype : 'ContentPanel',
38620        region: 'west',
38621        items: [ .... ]
38622    }
38623 );
38624
38625 layout.addxtype({
38626         xtype : 'NestedLayoutPanel',
38627         region: 'west',
38628         layout: {
38629            center: { },
38630            west: { }   
38631         },
38632         items : [ ... list of content panels or nested layout panels.. ]
38633    }
38634 );
38635 </code></pre>
38636      * @param {Object} cfg Xtype definition of item to add.
38637      */
38638     addxtype : function(cfg)
38639     {
38640         // basically accepts a pannel...
38641         // can accept a layout region..!?!?
38642         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38643         
38644         
38645         // theory?  children can only be panels??
38646         
38647         //if (!cfg.xtype.match(/Panel$/)) {
38648         //    return false;
38649         //}
38650         var ret = false;
38651         
38652         if (typeof(cfg.region) == 'undefined') {
38653             Roo.log("Failed to add Panel, region was not set");
38654             Roo.log(cfg);
38655             return false;
38656         }
38657         var region = cfg.region;
38658         delete cfg.region;
38659         
38660           
38661         var xitems = [];
38662         if (cfg.items) {
38663             xitems = cfg.items;
38664             delete cfg.items;
38665         }
38666         var nb = false;
38667         
38668         if ( region == 'center') {
38669             Roo.log("Center: " + cfg.title);
38670         }
38671         
38672         
38673         switch(cfg.xtype) 
38674         {
38675             case 'Content':  // ContentPanel (el, cfg)
38676             case 'Scroll':  // ContentPanel (el, cfg)
38677             case 'View': 
38678                 cfg.autoCreate = cfg.autoCreate || true;
38679                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38680                 //} else {
38681                 //    var el = this.el.createChild();
38682                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38683                 //}
38684                 
38685                 this.add(region, ret);
38686                 break;
38687             
38688             /*
38689             case 'TreePanel': // our new panel!
38690                 cfg.el = this.el.createChild();
38691                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38692                 this.add(region, ret);
38693                 break;
38694             */
38695             
38696             case 'Nest': 
38697                 // create a new Layout (which is  a Border Layout...
38698                 
38699                 var clayout = cfg.layout;
38700                 clayout.el  = this.el.createChild();
38701                 clayout.items   = clayout.items  || [];
38702                 
38703                 delete cfg.layout;
38704                 
38705                 // replace this exitems with the clayout ones..
38706                 xitems = clayout.items;
38707                  
38708                 // force background off if it's in center...
38709                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38710                     cfg.background = false;
38711                 }
38712                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38713                 
38714                 
38715                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38716                 //console.log('adding nested layout panel '  + cfg.toSource());
38717                 this.add(region, ret);
38718                 nb = {}; /// find first...
38719                 break;
38720             
38721             case 'Grid':
38722                 
38723                 // needs grid and region
38724                 
38725                 //var el = this.getRegion(region).el.createChild();
38726                 /*
38727                  *var el = this.el.createChild();
38728                 // create the grid first...
38729                 cfg.grid.container = el;
38730                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38731                 */
38732                 
38733                 if (region == 'center' && this.active ) {
38734                     cfg.background = false;
38735                 }
38736                 
38737                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38738                 
38739                 this.add(region, ret);
38740                 /*
38741                 if (cfg.background) {
38742                     // render grid on panel activation (if panel background)
38743                     ret.on('activate', function(gp) {
38744                         if (!gp.grid.rendered) {
38745                     //        gp.grid.render(el);
38746                         }
38747                     });
38748                 } else {
38749                   //  cfg.grid.render(el);
38750                 }
38751                 */
38752                 break;
38753            
38754            
38755             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38756                 // it was the old xcomponent building that caused this before.
38757                 // espeically if border is the top element in the tree.
38758                 ret = this;
38759                 break; 
38760                 
38761                     
38762                 
38763                 
38764                 
38765             default:
38766                 /*
38767                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38768                     
38769                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38770                     this.add(region, ret);
38771                 } else {
38772                 */
38773                     Roo.log(cfg);
38774                     throw "Can not add '" + cfg.xtype + "' to Border";
38775                     return null;
38776              
38777                                 
38778              
38779         }
38780         this.beginUpdate();
38781         // add children..
38782         var region = '';
38783         var abn = {};
38784         Roo.each(xitems, function(i)  {
38785             region = nb && i.region ? i.region : false;
38786             
38787             var add = ret.addxtype(i);
38788            
38789             if (region) {
38790                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38791                 if (!i.background) {
38792                     abn[region] = nb[region] ;
38793                 }
38794             }
38795             
38796         });
38797         this.endUpdate();
38798
38799         // make the last non-background panel active..
38800         //if (nb) { Roo.log(abn); }
38801         if (nb) {
38802             
38803             for(var r in abn) {
38804                 region = this.getRegion(r);
38805                 if (region) {
38806                     // tried using nb[r], but it does not work..
38807                      
38808                     region.showPanel(abn[r]);
38809                    
38810                 }
38811             }
38812         }
38813         return ret;
38814         
38815     },
38816     
38817     
38818 // private
38819     factory : function(cfg)
38820     {
38821         
38822         var validRegions = Roo.bootstrap.layout.Border.regions;
38823
38824         var target = cfg.region;
38825         cfg.mgr = this;
38826         
38827         var r = Roo.bootstrap.layout;
38828         Roo.log(target);
38829         switch(target){
38830             case "north":
38831                 return new r.North(cfg);
38832             case "south":
38833                 return new r.South(cfg);
38834             case "east":
38835                 return new r.East(cfg);
38836             case "west":
38837                 return new r.West(cfg);
38838             case "center":
38839                 return new r.Center(cfg);
38840         }
38841         throw 'Layout region "'+target+'" not supported.';
38842     }
38843     
38844     
38845 });
38846  /*
38847  * Based on:
38848  * Ext JS Library 1.1.1
38849  * Copyright(c) 2006-2007, Ext JS, LLC.
38850  *
38851  * Originally Released Under LGPL - original licence link has changed is not relivant.
38852  *
38853  * Fork - LGPL
38854  * <script type="text/javascript">
38855  */
38856  
38857 /**
38858  * @class Roo.bootstrap.layout.Basic
38859  * @extends Roo.util.Observable
38860  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38861  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38862  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38863  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38864  * @cfg {string}   region  the region that it inhabits..
38865  * @cfg {bool}   skipConfig skip config?
38866  * 
38867
38868  */
38869 Roo.bootstrap.layout.Basic = function(config){
38870     
38871     this.mgr = config.mgr;
38872     
38873     this.position = config.region;
38874     
38875     var skipConfig = config.skipConfig;
38876     
38877     this.events = {
38878         /**
38879          * @scope Roo.BasicLayoutRegion
38880          */
38881         
38882         /**
38883          * @event beforeremove
38884          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38885          * @param {Roo.LayoutRegion} this
38886          * @param {Roo.ContentPanel} panel The panel
38887          * @param {Object} e The cancel event object
38888          */
38889         "beforeremove" : true,
38890         /**
38891          * @event invalidated
38892          * Fires when the layout for this region is changed.
38893          * @param {Roo.LayoutRegion} this
38894          */
38895         "invalidated" : true,
38896         /**
38897          * @event visibilitychange
38898          * Fires when this region is shown or hidden 
38899          * @param {Roo.LayoutRegion} this
38900          * @param {Boolean} visibility true or false
38901          */
38902         "visibilitychange" : true,
38903         /**
38904          * @event paneladded
38905          * Fires when a panel is added. 
38906          * @param {Roo.LayoutRegion} this
38907          * @param {Roo.ContentPanel} panel The panel
38908          */
38909         "paneladded" : true,
38910         /**
38911          * @event panelremoved
38912          * Fires when a panel is removed. 
38913          * @param {Roo.LayoutRegion} this
38914          * @param {Roo.ContentPanel} panel The panel
38915          */
38916         "panelremoved" : true,
38917         /**
38918          * @event beforecollapse
38919          * Fires when this region before collapse.
38920          * @param {Roo.LayoutRegion} this
38921          */
38922         "beforecollapse" : true,
38923         /**
38924          * @event collapsed
38925          * Fires when this region is collapsed.
38926          * @param {Roo.LayoutRegion} this
38927          */
38928         "collapsed" : true,
38929         /**
38930          * @event expanded
38931          * Fires when this region is expanded.
38932          * @param {Roo.LayoutRegion} this
38933          */
38934         "expanded" : true,
38935         /**
38936          * @event slideshow
38937          * Fires when this region is slid into view.
38938          * @param {Roo.LayoutRegion} this
38939          */
38940         "slideshow" : true,
38941         /**
38942          * @event slidehide
38943          * Fires when this region slides out of view. 
38944          * @param {Roo.LayoutRegion} this
38945          */
38946         "slidehide" : true,
38947         /**
38948          * @event panelactivated
38949          * Fires when a panel is activated. 
38950          * @param {Roo.LayoutRegion} this
38951          * @param {Roo.ContentPanel} panel The activated panel
38952          */
38953         "panelactivated" : true,
38954         /**
38955          * @event resized
38956          * Fires when the user resizes this region. 
38957          * @param {Roo.LayoutRegion} this
38958          * @param {Number} newSize The new size (width for east/west, height for north/south)
38959          */
38960         "resized" : true
38961     };
38962     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38963     this.panels = new Roo.util.MixedCollection();
38964     this.panels.getKey = this.getPanelId.createDelegate(this);
38965     this.box = null;
38966     this.activePanel = null;
38967     // ensure listeners are added...
38968     
38969     if (config.listeners || config.events) {
38970         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38971             listeners : config.listeners || {},
38972             events : config.events || {}
38973         });
38974     }
38975     
38976     if(skipConfig !== true){
38977         this.applyConfig(config);
38978     }
38979 };
38980
38981 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38982 {
38983     getPanelId : function(p){
38984         return p.getId();
38985     },
38986     
38987     applyConfig : function(config){
38988         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38989         this.config = config;
38990         
38991     },
38992     
38993     /**
38994      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38995      * the width, for horizontal (north, south) the height.
38996      * @param {Number} newSize The new width or height
38997      */
38998     resizeTo : function(newSize){
38999         var el = this.el ? this.el :
39000                  (this.activePanel ? this.activePanel.getEl() : null);
39001         if(el){
39002             switch(this.position){
39003                 case "east":
39004                 case "west":
39005                     el.setWidth(newSize);
39006                     this.fireEvent("resized", this, newSize);
39007                 break;
39008                 case "north":
39009                 case "south":
39010                     el.setHeight(newSize);
39011                     this.fireEvent("resized", this, newSize);
39012                 break;                
39013             }
39014         }
39015     },
39016     
39017     getBox : function(){
39018         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39019     },
39020     
39021     getMargins : function(){
39022         return this.margins;
39023     },
39024     
39025     updateBox : function(box){
39026         this.box = box;
39027         var el = this.activePanel.getEl();
39028         el.dom.style.left = box.x + "px";
39029         el.dom.style.top = box.y + "px";
39030         this.activePanel.setSize(box.width, box.height);
39031     },
39032     
39033     /**
39034      * Returns the container element for this region.
39035      * @return {Roo.Element}
39036      */
39037     getEl : function(){
39038         return this.activePanel;
39039     },
39040     
39041     /**
39042      * Returns true if this region is currently visible.
39043      * @return {Boolean}
39044      */
39045     isVisible : function(){
39046         return this.activePanel ? true : false;
39047     },
39048     
39049     setActivePanel : function(panel){
39050         panel = this.getPanel(panel);
39051         if(this.activePanel && this.activePanel != panel){
39052             this.activePanel.setActiveState(false);
39053             this.activePanel.getEl().setLeftTop(-10000,-10000);
39054         }
39055         this.activePanel = panel;
39056         panel.setActiveState(true);
39057         if(this.box){
39058             panel.setSize(this.box.width, this.box.height);
39059         }
39060         this.fireEvent("panelactivated", this, panel);
39061         this.fireEvent("invalidated");
39062     },
39063     
39064     /**
39065      * Show the specified panel.
39066      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39067      * @return {Roo.ContentPanel} The shown panel or null
39068      */
39069     showPanel : function(panel){
39070         panel = this.getPanel(panel);
39071         if(panel){
39072             this.setActivePanel(panel);
39073         }
39074         return panel;
39075     },
39076     
39077     /**
39078      * Get the active panel for this region.
39079      * @return {Roo.ContentPanel} The active panel or null
39080      */
39081     getActivePanel : function(){
39082         return this.activePanel;
39083     },
39084     
39085     /**
39086      * Add the passed ContentPanel(s)
39087      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39088      * @return {Roo.ContentPanel} The panel added (if only one was added)
39089      */
39090     add : function(panel){
39091         if(arguments.length > 1){
39092             for(var i = 0, len = arguments.length; i < len; i++) {
39093                 this.add(arguments[i]);
39094             }
39095             return null;
39096         }
39097         if(this.hasPanel(panel)){
39098             this.showPanel(panel);
39099             return panel;
39100         }
39101         var el = panel.getEl();
39102         if(el.dom.parentNode != this.mgr.el.dom){
39103             this.mgr.el.dom.appendChild(el.dom);
39104         }
39105         if(panel.setRegion){
39106             panel.setRegion(this);
39107         }
39108         this.panels.add(panel);
39109         el.setStyle("position", "absolute");
39110         if(!panel.background){
39111             this.setActivePanel(panel);
39112             if(this.config.initialSize && this.panels.getCount()==1){
39113                 this.resizeTo(this.config.initialSize);
39114             }
39115         }
39116         this.fireEvent("paneladded", this, panel);
39117         return panel;
39118     },
39119     
39120     /**
39121      * Returns true if the panel is in this region.
39122      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39123      * @return {Boolean}
39124      */
39125     hasPanel : function(panel){
39126         if(typeof panel == "object"){ // must be panel obj
39127             panel = panel.getId();
39128         }
39129         return this.getPanel(panel) ? true : false;
39130     },
39131     
39132     /**
39133      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39134      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39135      * @param {Boolean} preservePanel Overrides the config preservePanel option
39136      * @return {Roo.ContentPanel} The panel that was removed
39137      */
39138     remove : function(panel, preservePanel){
39139         panel = this.getPanel(panel);
39140         if(!panel){
39141             return null;
39142         }
39143         var e = {};
39144         this.fireEvent("beforeremove", this, panel, e);
39145         if(e.cancel === true){
39146             return null;
39147         }
39148         var panelId = panel.getId();
39149         this.panels.removeKey(panelId);
39150         return panel;
39151     },
39152     
39153     /**
39154      * Returns the panel specified or null if it's not in this region.
39155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39156      * @return {Roo.ContentPanel}
39157      */
39158     getPanel : function(id){
39159         if(typeof id == "object"){ // must be panel obj
39160             return id;
39161         }
39162         return this.panels.get(id);
39163     },
39164     
39165     /**
39166      * Returns this regions position (north/south/east/west/center).
39167      * @return {String} 
39168      */
39169     getPosition: function(){
39170         return this.position;    
39171     }
39172 });/*
39173  * Based on:
39174  * Ext JS Library 1.1.1
39175  * Copyright(c) 2006-2007, Ext JS, LLC.
39176  *
39177  * Originally Released Under LGPL - original licence link has changed is not relivant.
39178  *
39179  * Fork - LGPL
39180  * <script type="text/javascript">
39181  */
39182  
39183 /**
39184  * @class Roo.bootstrap.layout.Region
39185  * @extends Roo.bootstrap.layout.Basic
39186  * This class represents a region in a layout manager.
39187  
39188  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39189  * @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})
39190  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39191  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39192  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39193  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39194  * @cfg {String}    title           The title for the region (overrides panel titles)
39195  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39196  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39197  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39198  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39199  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39200  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39201  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39202  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39203  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39204  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39205
39206  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39207  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39208  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39209  * @cfg {Number}    width           For East/West panels
39210  * @cfg {Number}    height          For North/South panels
39211  * @cfg {Boolean}   split           To show the splitter
39212  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39213  * 
39214  * @cfg {string}   cls             Extra CSS classes to add to region
39215  * 
39216  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39217  * @cfg {string}   region  the region that it inhabits..
39218  *
39219
39220  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39221  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39222
39223  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39224  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39225  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39226  */
39227 Roo.bootstrap.layout.Region = function(config)
39228 {
39229     this.applyConfig(config);
39230
39231     var mgr = config.mgr;
39232     var pos = config.region;
39233     config.skipConfig = true;
39234     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39235     
39236     if (mgr.el) {
39237         this.onRender(mgr.el);   
39238     }
39239      
39240     this.visible = true;
39241     this.collapsed = false;
39242     this.unrendered_panels = [];
39243 };
39244
39245 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39246
39247     position: '', // set by wrapper (eg. north/south etc..)
39248     unrendered_panels : null,  // unrendered panels.
39249     
39250     tabPosition : false,
39251     
39252     mgr: false, // points to 'Border'
39253     
39254     
39255     createBody : function(){
39256         /** This region's body element 
39257         * @type Roo.Element */
39258         this.bodyEl = this.el.createChild({
39259                 tag: "div",
39260                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39261         });
39262     },
39263
39264     onRender: function(ctr, pos)
39265     {
39266         var dh = Roo.DomHelper;
39267         /** This region's container element 
39268         * @type Roo.Element */
39269         this.el = dh.append(ctr.dom, {
39270                 tag: "div",
39271                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39272             }, true);
39273         /** This region's title element 
39274         * @type Roo.Element */
39275     
39276         this.titleEl = dh.append(this.el.dom,  {
39277                 tag: "div",
39278                 unselectable: "on",
39279                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39280                 children:[
39281                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39282                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39283                 ]
39284             }, true);
39285         
39286         this.titleEl.enableDisplayMode();
39287         /** This region's title text element 
39288         * @type HTMLElement */
39289         this.titleTextEl = this.titleEl.dom.firstChild;
39290         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39291         /*
39292         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39293         this.closeBtn.enableDisplayMode();
39294         this.closeBtn.on("click", this.closeClicked, this);
39295         this.closeBtn.hide();
39296     */
39297         this.createBody(this.config);
39298         if(this.config.hideWhenEmpty){
39299             this.hide();
39300             this.on("paneladded", this.validateVisibility, this);
39301             this.on("panelremoved", this.validateVisibility, this);
39302         }
39303         if(this.autoScroll){
39304             this.bodyEl.setStyle("overflow", "auto");
39305         }else{
39306             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39307         }
39308         //if(c.titlebar !== false){
39309             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39310                 this.titleEl.hide();
39311             }else{
39312                 this.titleEl.show();
39313                 if(this.config.title){
39314                     this.titleTextEl.innerHTML = this.config.title;
39315                 }
39316             }
39317         //}
39318         if(this.config.collapsed){
39319             this.collapse(true);
39320         }
39321         if(this.config.hidden){
39322             this.hide();
39323         }
39324         
39325         if (this.unrendered_panels && this.unrendered_panels.length) {
39326             for (var i =0;i< this.unrendered_panels.length; i++) {
39327                 this.add(this.unrendered_panels[i]);
39328             }
39329             this.unrendered_panels = null;
39330             
39331         }
39332         
39333     },
39334     
39335     applyConfig : function(c)
39336     {
39337         /*
39338          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39339             var dh = Roo.DomHelper;
39340             if(c.titlebar !== false){
39341                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39342                 this.collapseBtn.on("click", this.collapse, this);
39343                 this.collapseBtn.enableDisplayMode();
39344                 /*
39345                 if(c.showPin === true || this.showPin){
39346                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39347                     this.stickBtn.enableDisplayMode();
39348                     this.stickBtn.on("click", this.expand, this);
39349                     this.stickBtn.hide();
39350                 }
39351                 
39352             }
39353             */
39354             /** This region's collapsed element
39355             * @type Roo.Element */
39356             /*
39357              *
39358             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39359                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39360             ]}, true);
39361             
39362             if(c.floatable !== false){
39363                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39364                this.collapsedEl.on("click", this.collapseClick, this);
39365             }
39366
39367             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39368                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39369                    id: "message", unselectable: "on", style:{"float":"left"}});
39370                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39371              }
39372             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39373             this.expandBtn.on("click", this.expand, this);
39374             
39375         }
39376         
39377         if(this.collapseBtn){
39378             this.collapseBtn.setVisible(c.collapsible == true);
39379         }
39380         
39381         this.cmargins = c.cmargins || this.cmargins ||
39382                          (this.position == "west" || this.position == "east" ?
39383                              {top: 0, left: 2, right:2, bottom: 0} :
39384                              {top: 2, left: 0, right:0, bottom: 2});
39385         */
39386         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39387         
39388         
39389         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39390         
39391         this.autoScroll = c.autoScroll || false;
39392         
39393         
39394        
39395         
39396         this.duration = c.duration || .30;
39397         this.slideDuration = c.slideDuration || .45;
39398         this.config = c;
39399        
39400     },
39401     /**
39402      * Returns true if this region is currently visible.
39403      * @return {Boolean}
39404      */
39405     isVisible : function(){
39406         return this.visible;
39407     },
39408
39409     /**
39410      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39411      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39412      */
39413     //setCollapsedTitle : function(title){
39414     //    title = title || "&#160;";
39415      //   if(this.collapsedTitleTextEl){
39416       //      this.collapsedTitleTextEl.innerHTML = title;
39417        // }
39418     //},
39419
39420     getBox : function(){
39421         var b;
39422       //  if(!this.collapsed){
39423             b = this.el.getBox(false, true);
39424        // }else{
39425           //  b = this.collapsedEl.getBox(false, true);
39426         //}
39427         return b;
39428     },
39429
39430     getMargins : function(){
39431         return this.margins;
39432         //return this.collapsed ? this.cmargins : this.margins;
39433     },
39434 /*
39435     highlight : function(){
39436         this.el.addClass("x-layout-panel-dragover");
39437     },
39438
39439     unhighlight : function(){
39440         this.el.removeClass("x-layout-panel-dragover");
39441     },
39442 */
39443     updateBox : function(box)
39444     {
39445         if (!this.bodyEl) {
39446             return; // not rendered yet..
39447         }
39448         
39449         this.box = box;
39450         if(!this.collapsed){
39451             this.el.dom.style.left = box.x + "px";
39452             this.el.dom.style.top = box.y + "px";
39453             this.updateBody(box.width, box.height);
39454         }else{
39455             this.collapsedEl.dom.style.left = box.x + "px";
39456             this.collapsedEl.dom.style.top = box.y + "px";
39457             this.collapsedEl.setSize(box.width, box.height);
39458         }
39459         if(this.tabs){
39460             this.tabs.autoSizeTabs();
39461         }
39462     },
39463
39464     updateBody : function(w, h)
39465     {
39466         if(w !== null){
39467             this.el.setWidth(w);
39468             w -= this.el.getBorderWidth("rl");
39469             if(this.config.adjustments){
39470                 w += this.config.adjustments[0];
39471             }
39472         }
39473         if(h !== null && h > 0){
39474             this.el.setHeight(h);
39475             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39476             h -= this.el.getBorderWidth("tb");
39477             if(this.config.adjustments){
39478                 h += this.config.adjustments[1];
39479             }
39480             this.bodyEl.setHeight(h);
39481             if(this.tabs){
39482                 h = this.tabs.syncHeight(h);
39483             }
39484         }
39485         if(this.panelSize){
39486             w = w !== null ? w : this.panelSize.width;
39487             h = h !== null ? h : this.panelSize.height;
39488         }
39489         if(this.activePanel){
39490             var el = this.activePanel.getEl();
39491             w = w !== null ? w : el.getWidth();
39492             h = h !== null ? h : el.getHeight();
39493             this.panelSize = {width: w, height: h};
39494             this.activePanel.setSize(w, h);
39495         }
39496         if(Roo.isIE && this.tabs){
39497             this.tabs.el.repaint();
39498         }
39499     },
39500
39501     /**
39502      * Returns the container element for this region.
39503      * @return {Roo.Element}
39504      */
39505     getEl : function(){
39506         return this.el;
39507     },
39508
39509     /**
39510      * Hides this region.
39511      */
39512     hide : function(){
39513         //if(!this.collapsed){
39514             this.el.dom.style.left = "-2000px";
39515             this.el.hide();
39516         //}else{
39517          //   this.collapsedEl.dom.style.left = "-2000px";
39518          //   this.collapsedEl.hide();
39519        // }
39520         this.visible = false;
39521         this.fireEvent("visibilitychange", this, false);
39522     },
39523
39524     /**
39525      * Shows this region if it was previously hidden.
39526      */
39527     show : function(){
39528         //if(!this.collapsed){
39529             this.el.show();
39530         //}else{
39531         //    this.collapsedEl.show();
39532        // }
39533         this.visible = true;
39534         this.fireEvent("visibilitychange", this, true);
39535     },
39536 /*
39537     closeClicked : function(){
39538         if(this.activePanel){
39539             this.remove(this.activePanel);
39540         }
39541     },
39542
39543     collapseClick : function(e){
39544         if(this.isSlid){
39545            e.stopPropagation();
39546            this.slideIn();
39547         }else{
39548            e.stopPropagation();
39549            this.slideOut();
39550         }
39551     },
39552 */
39553     /**
39554      * Collapses this region.
39555      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39556      */
39557     /*
39558     collapse : function(skipAnim, skipCheck = false){
39559         if(this.collapsed) {
39560             return;
39561         }
39562         
39563         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39564             
39565             this.collapsed = true;
39566             if(this.split){
39567                 this.split.el.hide();
39568             }
39569             if(this.config.animate && skipAnim !== true){
39570                 this.fireEvent("invalidated", this);
39571                 this.animateCollapse();
39572             }else{
39573                 this.el.setLocation(-20000,-20000);
39574                 this.el.hide();
39575                 this.collapsedEl.show();
39576                 this.fireEvent("collapsed", this);
39577                 this.fireEvent("invalidated", this);
39578             }
39579         }
39580         
39581     },
39582 */
39583     animateCollapse : function(){
39584         // overridden
39585     },
39586
39587     /**
39588      * Expands this region if it was previously collapsed.
39589      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39590      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39591      */
39592     /*
39593     expand : function(e, skipAnim){
39594         if(e) {
39595             e.stopPropagation();
39596         }
39597         if(!this.collapsed || this.el.hasActiveFx()) {
39598             return;
39599         }
39600         if(this.isSlid){
39601             this.afterSlideIn();
39602             skipAnim = true;
39603         }
39604         this.collapsed = false;
39605         if(this.config.animate && skipAnim !== true){
39606             this.animateExpand();
39607         }else{
39608             this.el.show();
39609             if(this.split){
39610                 this.split.el.show();
39611             }
39612             this.collapsedEl.setLocation(-2000,-2000);
39613             this.collapsedEl.hide();
39614             this.fireEvent("invalidated", this);
39615             this.fireEvent("expanded", this);
39616         }
39617     },
39618 */
39619     animateExpand : function(){
39620         // overridden
39621     },
39622
39623     initTabs : function()
39624     {
39625         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39626         
39627         var ts = new Roo.bootstrap.panel.Tabs({
39628             el: this.bodyEl.dom,
39629             region : this,
39630             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39631             disableTooltips: this.config.disableTabTips,
39632             toolbar : this.config.toolbar
39633         });
39634         
39635         if(this.config.hideTabs){
39636             ts.stripWrap.setDisplayed(false);
39637         }
39638         this.tabs = ts;
39639         ts.resizeTabs = this.config.resizeTabs === true;
39640         ts.minTabWidth = this.config.minTabWidth || 40;
39641         ts.maxTabWidth = this.config.maxTabWidth || 250;
39642         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39643         ts.monitorResize = false;
39644         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39645         ts.bodyEl.addClass('roo-layout-tabs-body');
39646         this.panels.each(this.initPanelAsTab, this);
39647     },
39648
39649     initPanelAsTab : function(panel){
39650         var ti = this.tabs.addTab(
39651             panel.getEl().id,
39652             panel.getTitle(),
39653             null,
39654             this.config.closeOnTab && panel.isClosable(),
39655             panel.tpl
39656         );
39657         if(panel.tabTip !== undefined){
39658             ti.setTooltip(panel.tabTip);
39659         }
39660         ti.on("activate", function(){
39661               this.setActivePanel(panel);
39662         }, this);
39663         
39664         if(this.config.closeOnTab){
39665             ti.on("beforeclose", function(t, e){
39666                 e.cancel = true;
39667                 this.remove(panel);
39668             }, this);
39669         }
39670         
39671         panel.tabItem = ti;
39672         
39673         return ti;
39674     },
39675
39676     updatePanelTitle : function(panel, title)
39677     {
39678         if(this.activePanel == panel){
39679             this.updateTitle(title);
39680         }
39681         if(this.tabs){
39682             var ti = this.tabs.getTab(panel.getEl().id);
39683             ti.setText(title);
39684             if(panel.tabTip !== undefined){
39685                 ti.setTooltip(panel.tabTip);
39686             }
39687         }
39688     },
39689
39690     updateTitle : function(title){
39691         if(this.titleTextEl && !this.config.title){
39692             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39693         }
39694     },
39695
39696     setActivePanel : function(panel)
39697     {
39698         panel = this.getPanel(panel);
39699         if(this.activePanel && this.activePanel != panel){
39700             if(this.activePanel.setActiveState(false) === false){
39701                 return;
39702             }
39703         }
39704         this.activePanel = panel;
39705         panel.setActiveState(true);
39706         if(this.panelSize){
39707             panel.setSize(this.panelSize.width, this.panelSize.height);
39708         }
39709         if(this.closeBtn){
39710             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39711         }
39712         this.updateTitle(panel.getTitle());
39713         if(this.tabs){
39714             this.fireEvent("invalidated", this);
39715         }
39716         this.fireEvent("panelactivated", this, panel);
39717     },
39718
39719     /**
39720      * Shows the specified panel.
39721      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39722      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39723      */
39724     showPanel : function(panel)
39725     {
39726         panel = this.getPanel(panel);
39727         if(panel){
39728             if(this.tabs){
39729                 var tab = this.tabs.getTab(panel.getEl().id);
39730                 if(tab.isHidden()){
39731                     this.tabs.unhideTab(tab.id);
39732                 }
39733                 tab.activate();
39734             }else{
39735                 this.setActivePanel(panel);
39736             }
39737         }
39738         return panel;
39739     },
39740
39741     /**
39742      * Get the active panel for this region.
39743      * @return {Roo.ContentPanel} The active panel or null
39744      */
39745     getActivePanel : function(){
39746         return this.activePanel;
39747     },
39748
39749     validateVisibility : function(){
39750         if(this.panels.getCount() < 1){
39751             this.updateTitle("&#160;");
39752             this.closeBtn.hide();
39753             this.hide();
39754         }else{
39755             if(!this.isVisible()){
39756                 this.show();
39757             }
39758         }
39759     },
39760
39761     /**
39762      * Adds the passed ContentPanel(s) to this region.
39763      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39764      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39765      */
39766     add : function(panel)
39767     {
39768         if(arguments.length > 1){
39769             for(var i = 0, len = arguments.length; i < len; i++) {
39770                 this.add(arguments[i]);
39771             }
39772             return null;
39773         }
39774         
39775         // if we have not been rendered yet, then we can not really do much of this..
39776         if (!this.bodyEl) {
39777             this.unrendered_panels.push(panel);
39778             return panel;
39779         }
39780         
39781         
39782         
39783         
39784         if(this.hasPanel(panel)){
39785             this.showPanel(panel);
39786             return panel;
39787         }
39788         panel.setRegion(this);
39789         this.panels.add(panel);
39790        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39791             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39792             // and hide them... ???
39793             this.bodyEl.dom.appendChild(panel.getEl().dom);
39794             if(panel.background !== true){
39795                 this.setActivePanel(panel);
39796             }
39797             this.fireEvent("paneladded", this, panel);
39798             return panel;
39799         }
39800         */
39801         if(!this.tabs){
39802             this.initTabs();
39803         }else{
39804             this.initPanelAsTab(panel);
39805         }
39806         
39807         
39808         if(panel.background !== true){
39809             this.tabs.activate(panel.getEl().id);
39810         }
39811         this.fireEvent("paneladded", this, panel);
39812         return panel;
39813     },
39814
39815     /**
39816      * Hides the tab for the specified panel.
39817      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39818      */
39819     hidePanel : function(panel){
39820         if(this.tabs && (panel = this.getPanel(panel))){
39821             this.tabs.hideTab(panel.getEl().id);
39822         }
39823     },
39824
39825     /**
39826      * Unhides the tab for a previously hidden panel.
39827      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39828      */
39829     unhidePanel : function(panel){
39830         if(this.tabs && (panel = this.getPanel(panel))){
39831             this.tabs.unhideTab(panel.getEl().id);
39832         }
39833     },
39834
39835     clearPanels : function(){
39836         while(this.panels.getCount() > 0){
39837              this.remove(this.panels.first());
39838         }
39839     },
39840
39841     /**
39842      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39843      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39844      * @param {Boolean} preservePanel Overrides the config preservePanel option
39845      * @return {Roo.ContentPanel} The panel that was removed
39846      */
39847     remove : function(panel, preservePanel)
39848     {
39849         panel = this.getPanel(panel);
39850         if(!panel){
39851             return null;
39852         }
39853         var e = {};
39854         this.fireEvent("beforeremove", this, panel, e);
39855         if(e.cancel === true){
39856             return null;
39857         }
39858         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39859         var panelId = panel.getId();
39860         this.panels.removeKey(panelId);
39861         if(preservePanel){
39862             document.body.appendChild(panel.getEl().dom);
39863         }
39864         if(this.tabs){
39865             this.tabs.removeTab(panel.getEl().id);
39866         }else if (!preservePanel){
39867             this.bodyEl.dom.removeChild(panel.getEl().dom);
39868         }
39869         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39870             var p = this.panels.first();
39871             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39872             tempEl.appendChild(p.getEl().dom);
39873             this.bodyEl.update("");
39874             this.bodyEl.dom.appendChild(p.getEl().dom);
39875             tempEl = null;
39876             this.updateTitle(p.getTitle());
39877             this.tabs = null;
39878             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39879             this.setActivePanel(p);
39880         }
39881         panel.setRegion(null);
39882         if(this.activePanel == panel){
39883             this.activePanel = null;
39884         }
39885         if(this.config.autoDestroy !== false && preservePanel !== true){
39886             try{panel.destroy();}catch(e){}
39887         }
39888         this.fireEvent("panelremoved", this, panel);
39889         return panel;
39890     },
39891
39892     /**
39893      * Returns the TabPanel component used by this region
39894      * @return {Roo.TabPanel}
39895      */
39896     getTabs : function(){
39897         return this.tabs;
39898     },
39899
39900     createTool : function(parentEl, className){
39901         var btn = Roo.DomHelper.append(parentEl, {
39902             tag: "div",
39903             cls: "x-layout-tools-button",
39904             children: [ {
39905                 tag: "div",
39906                 cls: "roo-layout-tools-button-inner " + className,
39907                 html: "&#160;"
39908             }]
39909         }, true);
39910         btn.addClassOnOver("roo-layout-tools-button-over");
39911         return btn;
39912     }
39913 });/*
39914  * Based on:
39915  * Ext JS Library 1.1.1
39916  * Copyright(c) 2006-2007, Ext JS, LLC.
39917  *
39918  * Originally Released Under LGPL - original licence link has changed is not relivant.
39919  *
39920  * Fork - LGPL
39921  * <script type="text/javascript">
39922  */
39923  
39924
39925
39926 /**
39927  * @class Roo.SplitLayoutRegion
39928  * @extends Roo.LayoutRegion
39929  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39930  */
39931 Roo.bootstrap.layout.Split = function(config){
39932     this.cursor = config.cursor;
39933     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39934 };
39935
39936 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39937 {
39938     splitTip : "Drag to resize.",
39939     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39940     useSplitTips : false,
39941
39942     applyConfig : function(config){
39943         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39944     },
39945     
39946     onRender : function(ctr,pos) {
39947         
39948         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39949         if(!this.config.split){
39950             return;
39951         }
39952         if(!this.split){
39953             
39954             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39955                             tag: "div",
39956                             id: this.el.id + "-split",
39957                             cls: "roo-layout-split roo-layout-split-"+this.position,
39958                             html: "&#160;"
39959             });
39960             /** The SplitBar for this region 
39961             * @type Roo.SplitBar */
39962             // does not exist yet...
39963             Roo.log([this.position, this.orientation]);
39964             
39965             this.split = new Roo.bootstrap.SplitBar({
39966                 dragElement : splitEl,
39967                 resizingElement: this.el,
39968                 orientation : this.orientation
39969             });
39970             
39971             this.split.on("moved", this.onSplitMove, this);
39972             this.split.useShim = this.config.useShim === true;
39973             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39974             if(this.useSplitTips){
39975                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39976             }
39977             //if(config.collapsible){
39978             //    this.split.el.on("dblclick", this.collapse,  this);
39979             //}
39980         }
39981         if(typeof this.config.minSize != "undefined"){
39982             this.split.minSize = this.config.minSize;
39983         }
39984         if(typeof this.config.maxSize != "undefined"){
39985             this.split.maxSize = this.config.maxSize;
39986         }
39987         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39988             this.hideSplitter();
39989         }
39990         
39991     },
39992
39993     getHMaxSize : function(){
39994          var cmax = this.config.maxSize || 10000;
39995          var center = this.mgr.getRegion("center");
39996          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39997     },
39998
39999     getVMaxSize : function(){
40000          var cmax = this.config.maxSize || 10000;
40001          var center = this.mgr.getRegion("center");
40002          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40003     },
40004
40005     onSplitMove : function(split, newSize){
40006         this.fireEvent("resized", this, newSize);
40007     },
40008     
40009     /** 
40010      * Returns the {@link Roo.SplitBar} for this region.
40011      * @return {Roo.SplitBar}
40012      */
40013     getSplitBar : function(){
40014         return this.split;
40015     },
40016     
40017     hide : function(){
40018         this.hideSplitter();
40019         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40020     },
40021
40022     hideSplitter : function(){
40023         if(this.split){
40024             this.split.el.setLocation(-2000,-2000);
40025             this.split.el.hide();
40026         }
40027     },
40028
40029     show : function(){
40030         if(this.split){
40031             this.split.el.show();
40032         }
40033         Roo.bootstrap.layout.Split.superclass.show.call(this);
40034     },
40035     
40036     beforeSlide: function(){
40037         if(Roo.isGecko){// firefox overflow auto bug workaround
40038             this.bodyEl.clip();
40039             if(this.tabs) {
40040                 this.tabs.bodyEl.clip();
40041             }
40042             if(this.activePanel){
40043                 this.activePanel.getEl().clip();
40044                 
40045                 if(this.activePanel.beforeSlide){
40046                     this.activePanel.beforeSlide();
40047                 }
40048             }
40049         }
40050     },
40051     
40052     afterSlide : function(){
40053         if(Roo.isGecko){// firefox overflow auto bug workaround
40054             this.bodyEl.unclip();
40055             if(this.tabs) {
40056                 this.tabs.bodyEl.unclip();
40057             }
40058             if(this.activePanel){
40059                 this.activePanel.getEl().unclip();
40060                 if(this.activePanel.afterSlide){
40061                     this.activePanel.afterSlide();
40062                 }
40063             }
40064         }
40065     },
40066
40067     initAutoHide : function(){
40068         if(this.autoHide !== false){
40069             if(!this.autoHideHd){
40070                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40071                 this.autoHideHd = {
40072                     "mouseout": function(e){
40073                         if(!e.within(this.el, true)){
40074                             st.delay(500);
40075                         }
40076                     },
40077                     "mouseover" : function(e){
40078                         st.cancel();
40079                     },
40080                     scope : this
40081                 };
40082             }
40083             this.el.on(this.autoHideHd);
40084         }
40085     },
40086
40087     clearAutoHide : function(){
40088         if(this.autoHide !== false){
40089             this.el.un("mouseout", this.autoHideHd.mouseout);
40090             this.el.un("mouseover", this.autoHideHd.mouseover);
40091         }
40092     },
40093
40094     clearMonitor : function(){
40095         Roo.get(document).un("click", this.slideInIf, this);
40096     },
40097
40098     // these names are backwards but not changed for compat
40099     slideOut : function(){
40100         if(this.isSlid || this.el.hasActiveFx()){
40101             return;
40102         }
40103         this.isSlid = true;
40104         if(this.collapseBtn){
40105             this.collapseBtn.hide();
40106         }
40107         this.closeBtnState = this.closeBtn.getStyle('display');
40108         this.closeBtn.hide();
40109         if(this.stickBtn){
40110             this.stickBtn.show();
40111         }
40112         this.el.show();
40113         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40114         this.beforeSlide();
40115         this.el.setStyle("z-index", 10001);
40116         this.el.slideIn(this.getSlideAnchor(), {
40117             callback: function(){
40118                 this.afterSlide();
40119                 this.initAutoHide();
40120                 Roo.get(document).on("click", this.slideInIf, this);
40121                 this.fireEvent("slideshow", this);
40122             },
40123             scope: this,
40124             block: true
40125         });
40126     },
40127
40128     afterSlideIn : function(){
40129         this.clearAutoHide();
40130         this.isSlid = false;
40131         this.clearMonitor();
40132         this.el.setStyle("z-index", "");
40133         if(this.collapseBtn){
40134             this.collapseBtn.show();
40135         }
40136         this.closeBtn.setStyle('display', this.closeBtnState);
40137         if(this.stickBtn){
40138             this.stickBtn.hide();
40139         }
40140         this.fireEvent("slidehide", this);
40141     },
40142
40143     slideIn : function(cb){
40144         if(!this.isSlid || this.el.hasActiveFx()){
40145             Roo.callback(cb);
40146             return;
40147         }
40148         this.isSlid = false;
40149         this.beforeSlide();
40150         this.el.slideOut(this.getSlideAnchor(), {
40151             callback: function(){
40152                 this.el.setLeftTop(-10000, -10000);
40153                 this.afterSlide();
40154                 this.afterSlideIn();
40155                 Roo.callback(cb);
40156             },
40157             scope: this,
40158             block: true
40159         });
40160     },
40161     
40162     slideInIf : function(e){
40163         if(!e.within(this.el)){
40164             this.slideIn();
40165         }
40166     },
40167
40168     animateCollapse : function(){
40169         this.beforeSlide();
40170         this.el.setStyle("z-index", 20000);
40171         var anchor = this.getSlideAnchor();
40172         this.el.slideOut(anchor, {
40173             callback : function(){
40174                 this.el.setStyle("z-index", "");
40175                 this.collapsedEl.slideIn(anchor, {duration:.3});
40176                 this.afterSlide();
40177                 this.el.setLocation(-10000,-10000);
40178                 this.el.hide();
40179                 this.fireEvent("collapsed", this);
40180             },
40181             scope: this,
40182             block: true
40183         });
40184     },
40185
40186     animateExpand : function(){
40187         this.beforeSlide();
40188         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40189         this.el.setStyle("z-index", 20000);
40190         this.collapsedEl.hide({
40191             duration:.1
40192         });
40193         this.el.slideIn(this.getSlideAnchor(), {
40194             callback : function(){
40195                 this.el.setStyle("z-index", "");
40196                 this.afterSlide();
40197                 if(this.split){
40198                     this.split.el.show();
40199                 }
40200                 this.fireEvent("invalidated", this);
40201                 this.fireEvent("expanded", this);
40202             },
40203             scope: this,
40204             block: true
40205         });
40206     },
40207
40208     anchors : {
40209         "west" : "left",
40210         "east" : "right",
40211         "north" : "top",
40212         "south" : "bottom"
40213     },
40214
40215     sanchors : {
40216         "west" : "l",
40217         "east" : "r",
40218         "north" : "t",
40219         "south" : "b"
40220     },
40221
40222     canchors : {
40223         "west" : "tl-tr",
40224         "east" : "tr-tl",
40225         "north" : "tl-bl",
40226         "south" : "bl-tl"
40227     },
40228
40229     getAnchor : function(){
40230         return this.anchors[this.position];
40231     },
40232
40233     getCollapseAnchor : function(){
40234         return this.canchors[this.position];
40235     },
40236
40237     getSlideAnchor : function(){
40238         return this.sanchors[this.position];
40239     },
40240
40241     getAlignAdj : function(){
40242         var cm = this.cmargins;
40243         switch(this.position){
40244             case "west":
40245                 return [0, 0];
40246             break;
40247             case "east":
40248                 return [0, 0];
40249             break;
40250             case "north":
40251                 return [0, 0];
40252             break;
40253             case "south":
40254                 return [0, 0];
40255             break;
40256         }
40257     },
40258
40259     getExpandAdj : function(){
40260         var c = this.collapsedEl, cm = this.cmargins;
40261         switch(this.position){
40262             case "west":
40263                 return [-(cm.right+c.getWidth()+cm.left), 0];
40264             break;
40265             case "east":
40266                 return [cm.right+c.getWidth()+cm.left, 0];
40267             break;
40268             case "north":
40269                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40270             break;
40271             case "south":
40272                 return [0, cm.top+cm.bottom+c.getHeight()];
40273             break;
40274         }
40275     }
40276 });/*
40277  * Based on:
40278  * Ext JS Library 1.1.1
40279  * Copyright(c) 2006-2007, Ext JS, LLC.
40280  *
40281  * Originally Released Under LGPL - original licence link has changed is not relivant.
40282  *
40283  * Fork - LGPL
40284  * <script type="text/javascript">
40285  */
40286 /*
40287  * These classes are private internal classes
40288  */
40289 Roo.bootstrap.layout.Center = function(config){
40290     config.region = "center";
40291     Roo.bootstrap.layout.Region.call(this, config);
40292     this.visible = true;
40293     this.minWidth = config.minWidth || 20;
40294     this.minHeight = config.minHeight || 20;
40295 };
40296
40297 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40298     hide : function(){
40299         // center panel can't be hidden
40300     },
40301     
40302     show : function(){
40303         // center panel can't be hidden
40304     },
40305     
40306     getMinWidth: function(){
40307         return this.minWidth;
40308     },
40309     
40310     getMinHeight: function(){
40311         return this.minHeight;
40312     }
40313 });
40314
40315
40316
40317
40318  
40319
40320
40321
40322
40323
40324
40325 Roo.bootstrap.layout.North = function(config)
40326 {
40327     config.region = 'north';
40328     config.cursor = 'n-resize';
40329     
40330     Roo.bootstrap.layout.Split.call(this, config);
40331     
40332     
40333     if(this.split){
40334         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40335         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40336         this.split.el.addClass("roo-layout-split-v");
40337     }
40338     //var size = config.initialSize || config.height;
40339     //if(this.el && typeof size != "undefined"){
40340     //    this.el.setHeight(size);
40341     //}
40342 };
40343 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40344 {
40345     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40346      
40347      
40348     onRender : function(ctr, pos)
40349     {
40350         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40351         var size = this.config.initialSize || this.config.height;
40352         if(this.el && typeof size != "undefined"){
40353             this.el.setHeight(size);
40354         }
40355     
40356     },
40357     
40358     getBox : function(){
40359         if(this.collapsed){
40360             return this.collapsedEl.getBox();
40361         }
40362         var box = this.el.getBox();
40363         if(this.split){
40364             box.height += this.split.el.getHeight();
40365         }
40366         return box;
40367     },
40368     
40369     updateBox : function(box){
40370         if(this.split && !this.collapsed){
40371             box.height -= this.split.el.getHeight();
40372             this.split.el.setLeft(box.x);
40373             this.split.el.setTop(box.y+box.height);
40374             this.split.el.setWidth(box.width);
40375         }
40376         if(this.collapsed){
40377             this.updateBody(box.width, null);
40378         }
40379         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40380     }
40381 });
40382
40383
40384
40385
40386
40387 Roo.bootstrap.layout.South = function(config){
40388     config.region = 'south';
40389     config.cursor = 's-resize';
40390     Roo.bootstrap.layout.Split.call(this, config);
40391     if(this.split){
40392         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40393         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40394         this.split.el.addClass("roo-layout-split-v");
40395     }
40396     
40397 };
40398
40399 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40400     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40401     
40402     onRender : function(ctr, pos)
40403     {
40404         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40405         var size = this.config.initialSize || this.config.height;
40406         if(this.el && typeof size != "undefined"){
40407             this.el.setHeight(size);
40408         }
40409     
40410     },
40411     
40412     getBox : function(){
40413         if(this.collapsed){
40414             return this.collapsedEl.getBox();
40415         }
40416         var box = this.el.getBox();
40417         if(this.split){
40418             var sh = this.split.el.getHeight();
40419             box.height += sh;
40420             box.y -= sh;
40421         }
40422         return box;
40423     },
40424     
40425     updateBox : function(box){
40426         if(this.split && !this.collapsed){
40427             var sh = this.split.el.getHeight();
40428             box.height -= sh;
40429             box.y += sh;
40430             this.split.el.setLeft(box.x);
40431             this.split.el.setTop(box.y-sh);
40432             this.split.el.setWidth(box.width);
40433         }
40434         if(this.collapsed){
40435             this.updateBody(box.width, null);
40436         }
40437         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40438     }
40439 });
40440
40441 Roo.bootstrap.layout.East = function(config){
40442     config.region = "east";
40443     config.cursor = "e-resize";
40444     Roo.bootstrap.layout.Split.call(this, config);
40445     if(this.split){
40446         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40447         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40448         this.split.el.addClass("roo-layout-split-h");
40449     }
40450     
40451 };
40452 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40453     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40454     
40455     onRender : function(ctr, pos)
40456     {
40457         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40458         var size = this.config.initialSize || this.config.width;
40459         if(this.el && typeof size != "undefined"){
40460             this.el.setWidth(size);
40461         }
40462     
40463     },
40464     
40465     getBox : function(){
40466         if(this.collapsed){
40467             return this.collapsedEl.getBox();
40468         }
40469         var box = this.el.getBox();
40470         if(this.split){
40471             var sw = this.split.el.getWidth();
40472             box.width += sw;
40473             box.x -= sw;
40474         }
40475         return box;
40476     },
40477
40478     updateBox : function(box){
40479         if(this.split && !this.collapsed){
40480             var sw = this.split.el.getWidth();
40481             box.width -= sw;
40482             this.split.el.setLeft(box.x);
40483             this.split.el.setTop(box.y);
40484             this.split.el.setHeight(box.height);
40485             box.x += sw;
40486         }
40487         if(this.collapsed){
40488             this.updateBody(null, box.height);
40489         }
40490         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40491     }
40492 });
40493
40494 Roo.bootstrap.layout.West = function(config){
40495     config.region = "west";
40496     config.cursor = "w-resize";
40497     
40498     Roo.bootstrap.layout.Split.call(this, config);
40499     if(this.split){
40500         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40501         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40502         this.split.el.addClass("roo-layout-split-h");
40503     }
40504     
40505 };
40506 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40507     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40508     
40509     onRender: function(ctr, pos)
40510     {
40511         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40512         var size = this.config.initialSize || this.config.width;
40513         if(typeof size != "undefined"){
40514             this.el.setWidth(size);
40515         }
40516     },
40517     
40518     getBox : function(){
40519         if(this.collapsed){
40520             return this.collapsedEl.getBox();
40521         }
40522         var box = this.el.getBox();
40523         if (box.width == 0) {
40524             box.width = this.config.width; // kludge?
40525         }
40526         if(this.split){
40527             box.width += this.split.el.getWidth();
40528         }
40529         return box;
40530     },
40531     
40532     updateBox : function(box){
40533         if(this.split && !this.collapsed){
40534             var sw = this.split.el.getWidth();
40535             box.width -= sw;
40536             this.split.el.setLeft(box.x+box.width);
40537             this.split.el.setTop(box.y);
40538             this.split.el.setHeight(box.height);
40539         }
40540         if(this.collapsed){
40541             this.updateBody(null, box.height);
40542         }
40543         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40544     }
40545 });Roo.namespace("Roo.bootstrap.panel");/*
40546  * Based on:
40547  * Ext JS Library 1.1.1
40548  * Copyright(c) 2006-2007, Ext JS, LLC.
40549  *
40550  * Originally Released Under LGPL - original licence link has changed is not relivant.
40551  *
40552  * Fork - LGPL
40553  * <script type="text/javascript">
40554  */
40555 /**
40556  * @class Roo.ContentPanel
40557  * @extends Roo.util.Observable
40558  * @builder-top
40559  * A basic ContentPanel element.
40560  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40561  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40562  * @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
40563  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40564  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40565  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40566  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40567  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40568  * @cfg {String} title          The title for this panel
40569  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40570  * @cfg {String} url            Calls {@link #setUrl} with this value
40571  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40572  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40573  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40574  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40575  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40576  * @cfg {Boolean} badges render the badges
40577  * @cfg {String} cls  extra classes to use  
40578  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40579
40580  * @constructor
40581  * Create a new ContentPanel.
40582  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40583  * @param {String/Object} config A string to set only the title or a config object
40584  * @param {String} content (optional) Set the HTML content for this panel
40585  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40586  */
40587 Roo.bootstrap.panel.Content = function( config){
40588     
40589     this.tpl = config.tpl || false;
40590     
40591     var el = config.el;
40592     var content = config.content;
40593
40594     if(config.autoCreate){ // xtype is available if this is called from factory
40595         el = Roo.id();
40596     }
40597     this.el = Roo.get(el);
40598     if(!this.el && config && config.autoCreate){
40599         if(typeof config.autoCreate == "object"){
40600             if(!config.autoCreate.id){
40601                 config.autoCreate.id = config.id||el;
40602             }
40603             this.el = Roo.DomHelper.append(document.body,
40604                         config.autoCreate, true);
40605         }else{
40606             var elcfg =  {
40607                 tag: "div",
40608                 cls: (config.cls || '') +
40609                     (config.background ? ' bg-' + config.background : '') +
40610                     " roo-layout-inactive-content",
40611                 id: config.id||el
40612             };
40613             if (config.iframe) {
40614                 elcfg.cn = [
40615                     {
40616                         tag : 'iframe',
40617                         style : 'border: 0px',
40618                         src : 'about:blank'
40619                     }
40620                 ];
40621             }
40622               
40623             if (config.html) {
40624                 elcfg.html = config.html;
40625                 
40626             }
40627                         
40628             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40629             if (config.iframe) {
40630                 this.iframeEl = this.el.select('iframe',true).first();
40631             }
40632             
40633         }
40634     } 
40635     this.closable = false;
40636     this.loaded = false;
40637     this.active = false;
40638    
40639       
40640     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40641         
40642         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40643         
40644         this.wrapEl = this.el; //this.el.wrap();
40645         var ti = [];
40646         if (config.toolbar.items) {
40647             ti = config.toolbar.items ;
40648             delete config.toolbar.items ;
40649         }
40650         
40651         var nitems = [];
40652         this.toolbar.render(this.wrapEl, 'before');
40653         for(var i =0;i < ti.length;i++) {
40654           //  Roo.log(['add child', items[i]]);
40655             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40656         }
40657         this.toolbar.items = nitems;
40658         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40659         delete config.toolbar;
40660         
40661     }
40662     /*
40663     // xtype created footer. - not sure if will work as we normally have to render first..
40664     if (this.footer && !this.footer.el && this.footer.xtype) {
40665         if (!this.wrapEl) {
40666             this.wrapEl = this.el.wrap();
40667         }
40668     
40669         this.footer.container = this.wrapEl.createChild();
40670          
40671         this.footer = Roo.factory(this.footer, Roo);
40672         
40673     }
40674     */
40675     
40676      if(typeof config == "string"){
40677         this.title = config;
40678     }else{
40679         Roo.apply(this, config);
40680     }
40681     
40682     if(this.resizeEl){
40683         this.resizeEl = Roo.get(this.resizeEl, true);
40684     }else{
40685         this.resizeEl = this.el;
40686     }
40687     // handle view.xtype
40688     
40689  
40690     
40691     
40692     this.addEvents({
40693         /**
40694          * @event activate
40695          * Fires when this panel is activated. 
40696          * @param {Roo.ContentPanel} this
40697          */
40698         "activate" : true,
40699         /**
40700          * @event deactivate
40701          * Fires when this panel is activated. 
40702          * @param {Roo.ContentPanel} this
40703          */
40704         "deactivate" : true,
40705
40706         /**
40707          * @event resize
40708          * Fires when this panel is resized if fitToFrame is true.
40709          * @param {Roo.ContentPanel} this
40710          * @param {Number} width The width after any component adjustments
40711          * @param {Number} height The height after any component adjustments
40712          */
40713         "resize" : true,
40714         
40715          /**
40716          * @event render
40717          * Fires when this tab is created
40718          * @param {Roo.ContentPanel} this
40719          */
40720         "render" : true,
40721         
40722           /**
40723          * @event scroll
40724          * Fires when this content is scrolled
40725          * @param {Roo.ContentPanel} this
40726          * @param {Event} scrollEvent
40727          */
40728         "scroll" : true
40729         
40730         
40731         
40732     });
40733     
40734
40735     
40736     
40737     if(this.autoScroll && !this.iframe){
40738         this.resizeEl.setStyle("overflow", "auto");
40739         this.resizeEl.on('scroll', this.onScroll, this);
40740     } else {
40741         // fix randome scrolling
40742         //this.el.on('scroll', function() {
40743         //    Roo.log('fix random scolling');
40744         //    this.scrollTo('top',0); 
40745         //});
40746     }
40747     content = content || this.content;
40748     if(content){
40749         this.setContent(content);
40750     }
40751     if(config && config.url){
40752         this.setUrl(this.url, this.params, this.loadOnce);
40753     }
40754     
40755     
40756     
40757     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40758     
40759     if (this.view && typeof(this.view.xtype) != 'undefined') {
40760         this.view.el = this.el.appendChild(document.createElement("div"));
40761         this.view = Roo.factory(this.view); 
40762         this.view.render  &&  this.view.render(false, '');  
40763     }
40764     
40765     
40766     this.fireEvent('render', this);
40767 };
40768
40769 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40770     
40771     cls : '',
40772     background : '',
40773     
40774     tabTip : '',
40775     
40776     iframe : false,
40777     iframeEl : false,
40778     
40779     /* Resize Element - use this to work out scroll etc. */
40780     resizeEl : false,
40781     
40782     setRegion : function(region){
40783         this.region = region;
40784         this.setActiveClass(region && !this.background);
40785     },
40786     
40787     
40788     setActiveClass: function(state)
40789     {
40790         if(state){
40791            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40792            this.el.setStyle('position','relative');
40793         }else{
40794            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40795            this.el.setStyle('position', 'absolute');
40796         } 
40797     },
40798     
40799     /**
40800      * Returns the toolbar for this Panel if one was configured. 
40801      * @return {Roo.Toolbar} 
40802      */
40803     getToolbar : function(){
40804         return this.toolbar;
40805     },
40806     
40807     setActiveState : function(active)
40808     {
40809         this.active = active;
40810         this.setActiveClass(active);
40811         if(!active){
40812             if(this.fireEvent("deactivate", this) === false){
40813                 return false;
40814             }
40815             return true;
40816         }
40817         this.fireEvent("activate", this);
40818         return true;
40819     },
40820     /**
40821      * Updates this panel's element (not for iframe)
40822      * @param {String} content The new content
40823      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40824     */
40825     setContent : function(content, loadScripts){
40826         if (this.iframe) {
40827             return;
40828         }
40829         
40830         this.el.update(content, loadScripts);
40831     },
40832
40833     ignoreResize : function(w, h){
40834         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40835             return true;
40836         }else{
40837             this.lastSize = {width: w, height: h};
40838             return false;
40839         }
40840     },
40841     /**
40842      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40843      * @return {Roo.UpdateManager} The UpdateManager
40844      */
40845     getUpdateManager : function(){
40846         if (this.iframe) {
40847             return false;
40848         }
40849         return this.el.getUpdateManager();
40850     },
40851      /**
40852      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40853      * Does not work with IFRAME contents
40854      * @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:
40855 <pre><code>
40856 panel.load({
40857     url: "your-url.php",
40858     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40859     callback: yourFunction,
40860     scope: yourObject, //(optional scope)
40861     discardUrl: false,
40862     nocache: false,
40863     text: "Loading...",
40864     timeout: 30,
40865     scripts: false
40866 });
40867 </code></pre>
40868      
40869      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40870      * 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.
40871      * @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}
40872      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40873      * @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.
40874      * @return {Roo.ContentPanel} this
40875      */
40876     load : function(){
40877         
40878         if (this.iframe) {
40879             return this;
40880         }
40881         
40882         var um = this.el.getUpdateManager();
40883         um.update.apply(um, arguments);
40884         return this;
40885     },
40886
40887
40888     /**
40889      * 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.
40890      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40891      * @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)
40892      * @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)
40893      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40894      */
40895     setUrl : function(url, params, loadOnce){
40896         if (this.iframe) {
40897             this.iframeEl.dom.src = url;
40898             return false;
40899         }
40900         
40901         if(this.refreshDelegate){
40902             this.removeListener("activate", this.refreshDelegate);
40903         }
40904         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40905         this.on("activate", this.refreshDelegate);
40906         return this.el.getUpdateManager();
40907     },
40908     
40909     _handleRefresh : function(url, params, loadOnce){
40910         if(!loadOnce || !this.loaded){
40911             var updater = this.el.getUpdateManager();
40912             updater.update(url, params, this._setLoaded.createDelegate(this));
40913         }
40914     },
40915     
40916     _setLoaded : function(){
40917         this.loaded = true;
40918     }, 
40919     
40920     /**
40921      * Returns this panel's id
40922      * @return {String} 
40923      */
40924     getId : function(){
40925         return this.el.id;
40926     },
40927     
40928     /** 
40929      * Returns this panel's element - used by regiosn to add.
40930      * @return {Roo.Element} 
40931      */
40932     getEl : function(){
40933         return this.wrapEl || this.el;
40934     },
40935     
40936    
40937     
40938     adjustForComponents : function(width, height)
40939     {
40940         //Roo.log('adjustForComponents ');
40941         if(this.resizeEl != this.el){
40942             width -= this.el.getFrameWidth('lr');
40943             height -= this.el.getFrameWidth('tb');
40944         }
40945         if(this.toolbar){
40946             var te = this.toolbar.getEl();
40947             te.setWidth(width);
40948             height -= te.getHeight();
40949         }
40950         if(this.footer){
40951             var te = this.footer.getEl();
40952             te.setWidth(width);
40953             height -= te.getHeight();
40954         }
40955         
40956         
40957         if(this.adjustments){
40958             width += this.adjustments[0];
40959             height += this.adjustments[1];
40960         }
40961         return {"width": width, "height": height};
40962     },
40963     
40964     setSize : function(width, height){
40965         if(this.fitToFrame && !this.ignoreResize(width, height)){
40966             if(this.fitContainer && this.resizeEl != this.el){
40967                 this.el.setSize(width, height);
40968             }
40969             var size = this.adjustForComponents(width, height);
40970             if (this.iframe) {
40971                 this.iframeEl.setSize(width,height);
40972             }
40973             
40974             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40975             this.fireEvent('resize', this, size.width, size.height);
40976             
40977             
40978         }
40979     },
40980     
40981     /**
40982      * Returns this panel's title
40983      * @return {String} 
40984      */
40985     getTitle : function(){
40986         
40987         if (typeof(this.title) != 'object') {
40988             return this.title;
40989         }
40990         
40991         var t = '';
40992         for (var k in this.title) {
40993             if (!this.title.hasOwnProperty(k)) {
40994                 continue;
40995             }
40996             
40997             if (k.indexOf('-') >= 0) {
40998                 var s = k.split('-');
40999                 for (var i = 0; i<s.length; i++) {
41000                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41001                 }
41002             } else {
41003                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41004             }
41005         }
41006         return t;
41007     },
41008     
41009     /**
41010      * Set this panel's title
41011      * @param {String} title
41012      */
41013     setTitle : function(title){
41014         this.title = title;
41015         if(this.region){
41016             this.region.updatePanelTitle(this, title);
41017         }
41018     },
41019     
41020     /**
41021      * Returns true is this panel was configured to be closable
41022      * @return {Boolean} 
41023      */
41024     isClosable : function(){
41025         return this.closable;
41026     },
41027     
41028     beforeSlide : function(){
41029         this.el.clip();
41030         this.resizeEl.clip();
41031     },
41032     
41033     afterSlide : function(){
41034         this.el.unclip();
41035         this.resizeEl.unclip();
41036     },
41037     
41038     /**
41039      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41040      *   Will fail silently if the {@link #setUrl} method has not been called.
41041      *   This does not activate the panel, just updates its content.
41042      */
41043     refresh : function(){
41044         if(this.refreshDelegate){
41045            this.loaded = false;
41046            this.refreshDelegate();
41047         }
41048     },
41049     
41050     /**
41051      * Destroys this panel
41052      */
41053     destroy : function(){
41054         this.el.removeAllListeners();
41055         var tempEl = document.createElement("span");
41056         tempEl.appendChild(this.el.dom);
41057         tempEl.innerHTML = "";
41058         this.el.remove();
41059         this.el = null;
41060     },
41061     
41062     /**
41063      * form - if the content panel contains a form - this is a reference to it.
41064      * @type {Roo.form.Form}
41065      */
41066     form : false,
41067     /**
41068      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41069      *    This contains a reference to it.
41070      * @type {Roo.View}
41071      */
41072     view : false,
41073     
41074       /**
41075      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41076      * <pre><code>
41077
41078 layout.addxtype({
41079        xtype : 'Form',
41080        items: [ .... ]
41081    }
41082 );
41083
41084 </code></pre>
41085      * @param {Object} cfg Xtype definition of item to add.
41086      */
41087     
41088     
41089     getChildContainer: function () {
41090         return this.getEl();
41091     },
41092     
41093     
41094     onScroll : function(e)
41095     {
41096         this.fireEvent('scroll', this, e);
41097     }
41098     
41099     
41100     /*
41101         var  ret = new Roo.factory(cfg);
41102         return ret;
41103         
41104         
41105         // add form..
41106         if (cfg.xtype.match(/^Form$/)) {
41107             
41108             var el;
41109             //if (this.footer) {
41110             //    el = this.footer.container.insertSibling(false, 'before');
41111             //} else {
41112                 el = this.el.createChild();
41113             //}
41114
41115             this.form = new  Roo.form.Form(cfg);
41116             
41117             
41118             if ( this.form.allItems.length) {
41119                 this.form.render(el.dom);
41120             }
41121             return this.form;
41122         }
41123         // should only have one of theses..
41124         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41125             // views.. should not be just added - used named prop 'view''
41126             
41127             cfg.el = this.el.appendChild(document.createElement("div"));
41128             // factory?
41129             
41130             var ret = new Roo.factory(cfg);
41131              
41132              ret.render && ret.render(false, ''); // render blank..
41133             this.view = ret;
41134             return ret;
41135         }
41136         return false;
41137     }
41138     \*/
41139 });
41140  
41141 /**
41142  * @class Roo.bootstrap.panel.Grid
41143  * @extends Roo.bootstrap.panel.Content
41144  * @constructor
41145  * Create a new GridPanel.
41146  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41147  * @param {Object} config A the config object
41148   
41149  */
41150
41151
41152
41153 Roo.bootstrap.panel.Grid = function(config)
41154 {
41155     
41156       
41157     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41158         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41159
41160     config.el = this.wrapper;
41161     //this.el = this.wrapper;
41162     
41163       if (config.container) {
41164         // ctor'ed from a Border/panel.grid
41165         
41166         
41167         this.wrapper.setStyle("overflow", "hidden");
41168         this.wrapper.addClass('roo-grid-container');
41169
41170     }
41171     
41172     
41173     if(config.toolbar){
41174         var tool_el = this.wrapper.createChild();    
41175         this.toolbar = Roo.factory(config.toolbar);
41176         var ti = [];
41177         if (config.toolbar.items) {
41178             ti = config.toolbar.items ;
41179             delete config.toolbar.items ;
41180         }
41181         
41182         var nitems = [];
41183         this.toolbar.render(tool_el);
41184         for(var i =0;i < ti.length;i++) {
41185           //  Roo.log(['add child', items[i]]);
41186             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41187         }
41188         this.toolbar.items = nitems;
41189         
41190         delete config.toolbar;
41191     }
41192     
41193     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41194     config.grid.scrollBody = true;;
41195     config.grid.monitorWindowResize = false; // turn off autosizing
41196     config.grid.autoHeight = false;
41197     config.grid.autoWidth = false;
41198     
41199     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41200     
41201     if (config.background) {
41202         // render grid on panel activation (if panel background)
41203         this.on('activate', function(gp) {
41204             if (!gp.grid.rendered) {
41205                 gp.grid.render(this.wrapper);
41206                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41207             }
41208         });
41209             
41210     } else {
41211         this.grid.render(this.wrapper);
41212         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41213
41214     }
41215     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41216     // ??? needed ??? config.el = this.wrapper;
41217     
41218     
41219     
41220   
41221     // xtype created footer. - not sure if will work as we normally have to render first..
41222     if (this.footer && !this.footer.el && this.footer.xtype) {
41223         
41224         var ctr = this.grid.getView().getFooterPanel(true);
41225         this.footer.dataSource = this.grid.dataSource;
41226         this.footer = Roo.factory(this.footer, Roo);
41227         this.footer.render(ctr);
41228         
41229     }
41230     
41231     
41232     
41233     
41234      
41235 };
41236
41237 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41238     getId : function(){
41239         return this.grid.id;
41240     },
41241     
41242     /**
41243      * Returns the grid for this panel
41244      * @return {Roo.bootstrap.Table} 
41245      */
41246     getGrid : function(){
41247         return this.grid;    
41248     },
41249     
41250     setSize : function(width, height){
41251         if(!this.ignoreResize(width, height)){
41252             var grid = this.grid;
41253             var size = this.adjustForComponents(width, height);
41254             // tfoot is not a footer?
41255           
41256             
41257             var gridel = grid.getGridEl();
41258             gridel.setSize(size.width, size.height);
41259             
41260             var tbd = grid.getGridEl().select('tbody', true).first();
41261             var thd = grid.getGridEl().select('thead',true).first();
41262             var tbf= grid.getGridEl().select('tfoot', true).first();
41263
41264             if (tbf) {
41265                 size.height -= tbf.getHeight();
41266             }
41267             if (thd) {
41268                 size.height -= thd.getHeight();
41269             }
41270             
41271             tbd.setSize(size.width, size.height );
41272             // this is for the account management tab -seems to work there.
41273             var thd = grid.getGridEl().select('thead',true).first();
41274             //if (tbd) {
41275             //    tbd.setSize(size.width, size.height - thd.getHeight());
41276             //}
41277              
41278             grid.autoSize();
41279         }
41280     },
41281      
41282     
41283     
41284     beforeSlide : function(){
41285         this.grid.getView().scroller.clip();
41286     },
41287     
41288     afterSlide : function(){
41289         this.grid.getView().scroller.unclip();
41290     },
41291     
41292     destroy : function(){
41293         this.grid.destroy();
41294         delete this.grid;
41295         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41296     }
41297 });
41298
41299 /**
41300  * @class Roo.bootstrap.panel.Nest
41301  * @extends Roo.bootstrap.panel.Content
41302  * @constructor
41303  * Create a new Panel, that can contain a layout.Border.
41304  * 
41305  * 
41306  * @param {Roo.BorderLayout} layout The layout for this panel
41307  * @param {String/Object} config A string to set only the title or a config object
41308  */
41309 Roo.bootstrap.panel.Nest = function(config)
41310 {
41311     // construct with only one argument..
41312     /* FIXME - implement nicer consturctors
41313     if (layout.layout) {
41314         config = layout;
41315         layout = config.layout;
41316         delete config.layout;
41317     }
41318     if (layout.xtype && !layout.getEl) {
41319         // then layout needs constructing..
41320         layout = Roo.factory(layout, Roo);
41321     }
41322     */
41323     
41324     config.el =  config.layout.getEl();
41325     
41326     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41327     
41328     config.layout.monitorWindowResize = false; // turn off autosizing
41329     this.layout = config.layout;
41330     this.layout.getEl().addClass("roo-layout-nested-layout");
41331     this.layout.parent = this;
41332     
41333     
41334     
41335     
41336 };
41337
41338 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41339
41340     setSize : function(width, height){
41341         if(!this.ignoreResize(width, height)){
41342             var size = this.adjustForComponents(width, height);
41343             var el = this.layout.getEl();
41344             if (size.height < 1) {
41345                 el.setWidth(size.width);   
41346             } else {
41347                 el.setSize(size.width, size.height);
41348             }
41349             var touch = el.dom.offsetWidth;
41350             this.layout.layout();
41351             // ie requires a double layout on the first pass
41352             if(Roo.isIE && !this.initialized){
41353                 this.initialized = true;
41354                 this.layout.layout();
41355             }
41356         }
41357     },
41358     
41359     // activate all subpanels if not currently active..
41360     
41361     setActiveState : function(active){
41362         this.active = active;
41363         this.setActiveClass(active);
41364         
41365         if(!active){
41366             this.fireEvent("deactivate", this);
41367             return;
41368         }
41369         
41370         this.fireEvent("activate", this);
41371         // not sure if this should happen before or after..
41372         if (!this.layout) {
41373             return; // should not happen..
41374         }
41375         var reg = false;
41376         for (var r in this.layout.regions) {
41377             reg = this.layout.getRegion(r);
41378             if (reg.getActivePanel()) {
41379                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41380                 reg.setActivePanel(reg.getActivePanel());
41381                 continue;
41382             }
41383             if (!reg.panels.length) {
41384                 continue;
41385             }
41386             reg.showPanel(reg.getPanel(0));
41387         }
41388         
41389         
41390         
41391         
41392     },
41393     
41394     /**
41395      * Returns the nested BorderLayout for this panel
41396      * @return {Roo.BorderLayout} 
41397      */
41398     getLayout : function(){
41399         return this.layout;
41400     },
41401     
41402      /**
41403      * Adds a xtype elements to the layout of the nested panel
41404      * <pre><code>
41405
41406 panel.addxtype({
41407        xtype : 'ContentPanel',
41408        region: 'west',
41409        items: [ .... ]
41410    }
41411 );
41412
41413 panel.addxtype({
41414         xtype : 'NestedLayoutPanel',
41415         region: 'west',
41416         layout: {
41417            center: { },
41418            west: { }   
41419         },
41420         items : [ ... list of content panels or nested layout panels.. ]
41421    }
41422 );
41423 </code></pre>
41424      * @param {Object} cfg Xtype definition of item to add.
41425      */
41426     addxtype : function(cfg) {
41427         return this.layout.addxtype(cfg);
41428     
41429     }
41430 });/*
41431  * Based on:
41432  * Ext JS Library 1.1.1
41433  * Copyright(c) 2006-2007, Ext JS, LLC.
41434  *
41435  * Originally Released Under LGPL - original licence link has changed is not relivant.
41436  *
41437  * Fork - LGPL
41438  * <script type="text/javascript">
41439  */
41440 /**
41441  * @class Roo.TabPanel
41442  * @extends Roo.util.Observable
41443  * A lightweight tab container.
41444  * <br><br>
41445  * Usage:
41446  * <pre><code>
41447 // basic tabs 1, built from existing content
41448 var tabs = new Roo.TabPanel("tabs1");
41449 tabs.addTab("script", "View Script");
41450 tabs.addTab("markup", "View Markup");
41451 tabs.activate("script");
41452
41453 // more advanced tabs, built from javascript
41454 var jtabs = new Roo.TabPanel("jtabs");
41455 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41456
41457 // set up the UpdateManager
41458 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41459 var updater = tab2.getUpdateManager();
41460 updater.setDefaultUrl("ajax1.htm");
41461 tab2.on('activate', updater.refresh, updater, true);
41462
41463 // Use setUrl for Ajax loading
41464 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41465 tab3.setUrl("ajax2.htm", null, true);
41466
41467 // Disabled tab
41468 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41469 tab4.disable();
41470
41471 jtabs.activate("jtabs-1");
41472  * </code></pre>
41473  * @constructor
41474  * Create a new TabPanel.
41475  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41476  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41477  */
41478 Roo.bootstrap.panel.Tabs = function(config){
41479     /**
41480     * The container element for this TabPanel.
41481     * @type Roo.Element
41482     */
41483     this.el = Roo.get(config.el);
41484     delete config.el;
41485     if(config){
41486         if(typeof config == "boolean"){
41487             this.tabPosition = config ? "bottom" : "top";
41488         }else{
41489             Roo.apply(this, config);
41490         }
41491     }
41492     
41493     if(this.tabPosition == "bottom"){
41494         // if tabs are at the bottom = create the body first.
41495         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41496         this.el.addClass("roo-tabs-bottom");
41497     }
41498     // next create the tabs holders
41499     
41500     if (this.tabPosition == "west"){
41501         
41502         var reg = this.region; // fake it..
41503         while (reg) {
41504             if (!reg.mgr.parent) {
41505                 break;
41506             }
41507             reg = reg.mgr.parent.region;
41508         }
41509         Roo.log("got nest?");
41510         Roo.log(reg);
41511         if (reg.mgr.getRegion('west')) {
41512             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41513             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41514             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41515             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41516             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41517         
41518             
41519         }
41520         
41521         
41522     } else {
41523      
41524         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41525         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41526         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41527         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41528     }
41529     
41530     
41531     if(Roo.isIE){
41532         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41533     }
41534     
41535     // finally - if tabs are at the top, then create the body last..
41536     if(this.tabPosition != "bottom"){
41537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41538          * @type Roo.Element
41539          */
41540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41541         this.el.addClass("roo-tabs-top");
41542     }
41543     this.items = [];
41544
41545     this.bodyEl.setStyle("position", "relative");
41546
41547     this.active = null;
41548     this.activateDelegate = this.activate.createDelegate(this);
41549
41550     this.addEvents({
41551         /**
41552          * @event tabchange
41553          * Fires when the active tab changes
41554          * @param {Roo.TabPanel} this
41555          * @param {Roo.TabPanelItem} activePanel The new active tab
41556          */
41557         "tabchange": true,
41558         /**
41559          * @event beforetabchange
41560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41561          * @param {Roo.TabPanel} this
41562          * @param {Object} e Set cancel to true on this object to cancel the tab change
41563          * @param {Roo.TabPanelItem} tab The tab being changed to
41564          */
41565         "beforetabchange" : true
41566     });
41567
41568     Roo.EventManager.onWindowResize(this.onResize, this);
41569     this.cpad = this.el.getPadding("lr");
41570     this.hiddenCount = 0;
41571
41572
41573     // toolbar on the tabbar support...
41574     if (this.toolbar) {
41575         alert("no toolbar support yet");
41576         this.toolbar  = false;
41577         /*
41578         var tcfg = this.toolbar;
41579         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41580         this.toolbar = new Roo.Toolbar(tcfg);
41581         if (Roo.isSafari) {
41582             var tbl = tcfg.container.child('table', true);
41583             tbl.setAttribute('width', '100%');
41584         }
41585         */
41586         
41587     }
41588    
41589
41590
41591     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41592 };
41593
41594 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41595     /*
41596      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41597      */
41598     tabPosition : "top",
41599     /*
41600      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41601      */
41602     currentTabWidth : 0,
41603     /*
41604      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41605      */
41606     minTabWidth : 40,
41607     /*
41608      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41609      */
41610     maxTabWidth : 250,
41611     /*
41612      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41613      */
41614     preferredTabWidth : 175,
41615     /*
41616      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41617      */
41618     resizeTabs : false,
41619     /*
41620      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41621      */
41622     monitorResize : true,
41623     /*
41624      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41625      */
41626     toolbar : false,  // set by caller..
41627     
41628     region : false, /// set by caller
41629     
41630     disableTooltips : true, // not used yet...
41631
41632     /**
41633      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41634      * @param {String} id The id of the div to use <b>or create</b>
41635      * @param {String} text The text for the tab
41636      * @param {String} content (optional) Content to put in the TabPanelItem body
41637      * @param {Boolean} closable (optional) True to create a close icon on the tab
41638      * @return {Roo.TabPanelItem} The created TabPanelItem
41639      */
41640     addTab : function(id, text, content, closable, tpl)
41641     {
41642         var item = new Roo.bootstrap.panel.TabItem({
41643             panel: this,
41644             id : id,
41645             text : text,
41646             closable : closable,
41647             tpl : tpl
41648         });
41649         this.addTabItem(item);
41650         if(content){
41651             item.setContent(content);
41652         }
41653         return item;
41654     },
41655
41656     /**
41657      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41658      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41659      * @return {Roo.TabPanelItem}
41660      */
41661     getTab : function(id){
41662         return this.items[id];
41663     },
41664
41665     /**
41666      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41667      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41668      */
41669     hideTab : function(id){
41670         var t = this.items[id];
41671         if(!t.isHidden()){
41672            t.setHidden(true);
41673            this.hiddenCount++;
41674            this.autoSizeTabs();
41675         }
41676     },
41677
41678     /**
41679      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41680      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41681      */
41682     unhideTab : function(id){
41683         var t = this.items[id];
41684         if(t.isHidden()){
41685            t.setHidden(false);
41686            this.hiddenCount--;
41687            this.autoSizeTabs();
41688         }
41689     },
41690
41691     /**
41692      * Adds an existing {@link Roo.TabPanelItem}.
41693      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41694      */
41695     addTabItem : function(item)
41696     {
41697         this.items[item.id] = item;
41698         this.items.push(item);
41699         this.autoSizeTabs();
41700       //  if(this.resizeTabs){
41701     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41702   //         this.autoSizeTabs();
41703 //        }else{
41704 //            item.autoSize();
41705        // }
41706     },
41707
41708     /**
41709      * Removes a {@link Roo.TabPanelItem}.
41710      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41711      */
41712     removeTab : function(id){
41713         var items = this.items;
41714         var tab = items[id];
41715         if(!tab) { return; }
41716         var index = items.indexOf(tab);
41717         if(this.active == tab && items.length > 1){
41718             var newTab = this.getNextAvailable(index);
41719             if(newTab) {
41720                 newTab.activate();
41721             }
41722         }
41723         this.stripEl.dom.removeChild(tab.pnode.dom);
41724         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41725             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41726         }
41727         items.splice(index, 1);
41728         delete this.items[tab.id];
41729         tab.fireEvent("close", tab);
41730         tab.purgeListeners();
41731         this.autoSizeTabs();
41732     },
41733
41734     getNextAvailable : function(start){
41735         var items = this.items;
41736         var index = start;
41737         // look for a next tab that will slide over to
41738         // replace the one being removed
41739         while(index < items.length){
41740             var item = items[++index];
41741             if(item && !item.isHidden()){
41742                 return item;
41743             }
41744         }
41745         // if one isn't found select the previous tab (on the left)
41746         index = start;
41747         while(index >= 0){
41748             var item = items[--index];
41749             if(item && !item.isHidden()){
41750                 return item;
41751             }
41752         }
41753         return null;
41754     },
41755
41756     /**
41757      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41758      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41759      */
41760     disableTab : function(id){
41761         var tab = this.items[id];
41762         if(tab && this.active != tab){
41763             tab.disable();
41764         }
41765     },
41766
41767     /**
41768      * Enables a {@link Roo.TabPanelItem} that is disabled.
41769      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41770      */
41771     enableTab : function(id){
41772         var tab = this.items[id];
41773         tab.enable();
41774     },
41775
41776     /**
41777      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41778      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41779      * @return {Roo.TabPanelItem} The TabPanelItem.
41780      */
41781     activate : function(id)
41782     {
41783         //Roo.log('activite:'  + id);
41784         
41785         var tab = this.items[id];
41786         if(!tab){
41787             return null;
41788         }
41789         if(tab == this.active || tab.disabled){
41790             return tab;
41791         }
41792         var e = {};
41793         this.fireEvent("beforetabchange", this, e, tab);
41794         if(e.cancel !== true && !tab.disabled){
41795             if(this.active){
41796                 this.active.hide();
41797             }
41798             this.active = this.items[id];
41799             this.active.show();
41800             this.fireEvent("tabchange", this, this.active);
41801         }
41802         return tab;
41803     },
41804
41805     /**
41806      * Gets the active {@link Roo.TabPanelItem}.
41807      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41808      */
41809     getActiveTab : function(){
41810         return this.active;
41811     },
41812
41813     /**
41814      * Updates the tab body element to fit the height of the container element
41815      * for overflow scrolling
41816      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41817      */
41818     syncHeight : function(targetHeight){
41819         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41820         var bm = this.bodyEl.getMargins();
41821         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41822         this.bodyEl.setHeight(newHeight);
41823         return newHeight;
41824     },
41825
41826     onResize : function(){
41827         if(this.monitorResize){
41828             this.autoSizeTabs();
41829         }
41830     },
41831
41832     /**
41833      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41834      */
41835     beginUpdate : function(){
41836         this.updating = true;
41837     },
41838
41839     /**
41840      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41841      */
41842     endUpdate : function(){
41843         this.updating = false;
41844         this.autoSizeTabs();
41845     },
41846
41847     /**
41848      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41849      */
41850     autoSizeTabs : function()
41851     {
41852         var count = this.items.length;
41853         var vcount = count - this.hiddenCount;
41854         
41855         if (vcount < 2) {
41856             this.stripEl.hide();
41857         } else {
41858             this.stripEl.show();
41859         }
41860         
41861         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41862             return;
41863         }
41864         
41865         
41866         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41867         var availWidth = Math.floor(w / vcount);
41868         var b = this.stripBody;
41869         if(b.getWidth() > w){
41870             var tabs = this.items;
41871             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41872             if(availWidth < this.minTabWidth){
41873                 /*if(!this.sleft){    // incomplete scrolling code
41874                     this.createScrollButtons();
41875                 }
41876                 this.showScroll();
41877                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41878             }
41879         }else{
41880             if(this.currentTabWidth < this.preferredTabWidth){
41881                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41882             }
41883         }
41884     },
41885
41886     /**
41887      * Returns the number of tabs in this TabPanel.
41888      * @return {Number}
41889      */
41890      getCount : function(){
41891          return this.items.length;
41892      },
41893
41894     /**
41895      * Resizes all the tabs to the passed width
41896      * @param {Number} The new width
41897      */
41898     setTabWidth : function(width){
41899         this.currentTabWidth = width;
41900         for(var i = 0, len = this.items.length; i < len; i++) {
41901                 if(!this.items[i].isHidden()) {
41902                 this.items[i].setWidth(width);
41903             }
41904         }
41905     },
41906
41907     /**
41908      * Destroys this TabPanel
41909      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41910      */
41911     destroy : function(removeEl){
41912         Roo.EventManager.removeResizeListener(this.onResize, this);
41913         for(var i = 0, len = this.items.length; i < len; i++){
41914             this.items[i].purgeListeners();
41915         }
41916         if(removeEl === true){
41917             this.el.update("");
41918             this.el.remove();
41919         }
41920     },
41921     
41922     createStrip : function(container)
41923     {
41924         var strip = document.createElement("nav");
41925         strip.className = Roo.bootstrap.version == 4 ?
41926             "navbar-light bg-light" : 
41927             "navbar navbar-default"; //"x-tabs-wrap";
41928         container.appendChild(strip);
41929         return strip;
41930     },
41931     
41932     createStripList : function(strip)
41933     {
41934         // div wrapper for retard IE
41935         // returns the "tr" element.
41936         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41937         //'<div class="x-tabs-strip-wrap">'+
41938           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41939           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41940         return strip.firstChild; //.firstChild.firstChild.firstChild;
41941     },
41942     createBody : function(container)
41943     {
41944         var body = document.createElement("div");
41945         Roo.id(body, "tab-body");
41946         //Roo.fly(body).addClass("x-tabs-body");
41947         Roo.fly(body).addClass("tab-content");
41948         container.appendChild(body);
41949         return body;
41950     },
41951     createItemBody :function(bodyEl, id){
41952         var body = Roo.getDom(id);
41953         if(!body){
41954             body = document.createElement("div");
41955             body.id = id;
41956         }
41957         //Roo.fly(body).addClass("x-tabs-item-body");
41958         Roo.fly(body).addClass("tab-pane");
41959          bodyEl.insertBefore(body, bodyEl.firstChild);
41960         return body;
41961     },
41962     /** @private */
41963     createStripElements :  function(stripEl, text, closable, tpl)
41964     {
41965         var td = document.createElement("li"); // was td..
41966         td.className = 'nav-item';
41967         
41968         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41969         
41970         
41971         stripEl.appendChild(td);
41972         /*if(closable){
41973             td.className = "x-tabs-closable";
41974             if(!this.closeTpl){
41975                 this.closeTpl = new Roo.Template(
41976                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41977                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41978                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41979                 );
41980             }
41981             var el = this.closeTpl.overwrite(td, {"text": text});
41982             var close = el.getElementsByTagName("div")[0];
41983             var inner = el.getElementsByTagName("em")[0];
41984             return {"el": el, "close": close, "inner": inner};
41985         } else {
41986         */
41987         // not sure what this is..
41988 //            if(!this.tabTpl){
41989                 //this.tabTpl = new Roo.Template(
41990                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41991                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41992                 //);
41993 //                this.tabTpl = new Roo.Template(
41994 //                   '<a href="#">' +
41995 //                   '<span unselectable="on"' +
41996 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41997 //                            ' >{text}</span></a>'
41998 //                );
41999 //                
42000 //            }
42001
42002
42003             var template = tpl || this.tabTpl || false;
42004             
42005             if(!template){
42006                 template =  new Roo.Template(
42007                         Roo.bootstrap.version == 4 ? 
42008                             (
42009                                 '<a class="nav-link" href="#" unselectable="on"' +
42010                                      (this.disableTooltips ? '' : ' title="{text}"') +
42011                                      ' >{text}</a>'
42012                             ) : (
42013                                 '<a class="nav-link" href="#">' +
42014                                 '<span unselectable="on"' +
42015                                          (this.disableTooltips ? '' : ' title="{text}"') +
42016                                     ' >{text}</span></a>'
42017                             )
42018                 );
42019             }
42020             
42021             switch (typeof(template)) {
42022                 case 'object' :
42023                     break;
42024                 case 'string' :
42025                     template = new Roo.Template(template);
42026                     break;
42027                 default :
42028                     break;
42029             }
42030             
42031             var el = template.overwrite(td, {"text": text});
42032             
42033             var inner = el.getElementsByTagName("span")[0];
42034             
42035             return {"el": el, "inner": inner};
42036             
42037     }
42038         
42039     
42040 });
42041
42042 /**
42043  * @class Roo.TabPanelItem
42044  * @extends Roo.util.Observable
42045  * Represents an individual item (tab plus body) in a TabPanel.
42046  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42047  * @param {String} id The id of this TabPanelItem
42048  * @param {String} text The text for the tab of this TabPanelItem
42049  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42050  */
42051 Roo.bootstrap.panel.TabItem = function(config){
42052     /**
42053      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42054      * @type Roo.TabPanel
42055      */
42056     this.tabPanel = config.panel;
42057     /**
42058      * The id for this TabPanelItem
42059      * @type String
42060      */
42061     this.id = config.id;
42062     /** @private */
42063     this.disabled = false;
42064     /** @private */
42065     this.text = config.text;
42066     /** @private */
42067     this.loaded = false;
42068     this.closable = config.closable;
42069
42070     /**
42071      * The body element for this TabPanelItem.
42072      * @type Roo.Element
42073      */
42074     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42075     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42076     this.bodyEl.setStyle("display", "block");
42077     this.bodyEl.setStyle("zoom", "1");
42078     //this.hideAction();
42079
42080     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42081     /** @private */
42082     this.el = Roo.get(els.el);
42083     this.inner = Roo.get(els.inner, true);
42084      this.textEl = Roo.bootstrap.version == 4 ?
42085         this.el : Roo.get(this.el.dom.firstChild, true);
42086
42087     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42088     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42089
42090     
42091 //    this.el.on("mousedown", this.onTabMouseDown, this);
42092     this.el.on("click", this.onTabClick, this);
42093     /** @private */
42094     if(config.closable){
42095         var c = Roo.get(els.close, true);
42096         c.dom.title = this.closeText;
42097         c.addClassOnOver("close-over");
42098         c.on("click", this.closeClick, this);
42099      }
42100
42101     this.addEvents({
42102          /**
42103          * @event activate
42104          * Fires when this tab becomes the active tab.
42105          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42106          * @param {Roo.TabPanelItem} this
42107          */
42108         "activate": true,
42109         /**
42110          * @event beforeclose
42111          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42112          * @param {Roo.TabPanelItem} this
42113          * @param {Object} e Set cancel to true on this object to cancel the close.
42114          */
42115         "beforeclose": true,
42116         /**
42117          * @event close
42118          * Fires when this tab is closed.
42119          * @param {Roo.TabPanelItem} this
42120          */
42121          "close": true,
42122         /**
42123          * @event deactivate
42124          * Fires when this tab is no longer the active tab.
42125          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42126          * @param {Roo.TabPanelItem} this
42127          */
42128          "deactivate" : true
42129     });
42130     this.hidden = false;
42131
42132     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42133 };
42134
42135 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42136            {
42137     purgeListeners : function(){
42138        Roo.util.Observable.prototype.purgeListeners.call(this);
42139        this.el.removeAllListeners();
42140     },
42141     /**
42142      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42143      */
42144     show : function(){
42145         this.status_node.addClass("active");
42146         this.showAction();
42147         if(Roo.isOpera){
42148             this.tabPanel.stripWrap.repaint();
42149         }
42150         this.fireEvent("activate", this.tabPanel, this);
42151     },
42152
42153     /**
42154      * Returns true if this tab is the active tab.
42155      * @return {Boolean}
42156      */
42157     isActive : function(){
42158         return this.tabPanel.getActiveTab() == this;
42159     },
42160
42161     /**
42162      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42163      */
42164     hide : function(){
42165         this.status_node.removeClass("active");
42166         this.hideAction();
42167         this.fireEvent("deactivate", this.tabPanel, this);
42168     },
42169
42170     hideAction : function(){
42171         this.bodyEl.hide();
42172         this.bodyEl.setStyle("position", "absolute");
42173         this.bodyEl.setLeft("-20000px");
42174         this.bodyEl.setTop("-20000px");
42175     },
42176
42177     showAction : function(){
42178         this.bodyEl.setStyle("position", "relative");
42179         this.bodyEl.setTop("");
42180         this.bodyEl.setLeft("");
42181         this.bodyEl.show();
42182     },
42183
42184     /**
42185      * Set the tooltip for the tab.
42186      * @param {String} tooltip The tab's tooltip
42187      */
42188     setTooltip : function(text){
42189         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42190             this.textEl.dom.qtip = text;
42191             this.textEl.dom.removeAttribute('title');
42192         }else{
42193             this.textEl.dom.title = text;
42194         }
42195     },
42196
42197     onTabClick : function(e){
42198         e.preventDefault();
42199         this.tabPanel.activate(this.id);
42200     },
42201
42202     onTabMouseDown : function(e){
42203         e.preventDefault();
42204         this.tabPanel.activate(this.id);
42205     },
42206 /*
42207     getWidth : function(){
42208         return this.inner.getWidth();
42209     },
42210
42211     setWidth : function(width){
42212         var iwidth = width - this.linode.getPadding("lr");
42213         this.inner.setWidth(iwidth);
42214         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42215         this.linode.setWidth(width);
42216     },
42217 */
42218     /**
42219      * Show or hide the tab
42220      * @param {Boolean} hidden True to hide or false to show.
42221      */
42222     setHidden : function(hidden){
42223         this.hidden = hidden;
42224         this.linode.setStyle("display", hidden ? "none" : "");
42225     },
42226
42227     /**
42228      * Returns true if this tab is "hidden"
42229      * @return {Boolean}
42230      */
42231     isHidden : function(){
42232         return this.hidden;
42233     },
42234
42235     /**
42236      * Returns the text for this tab
42237      * @return {String}
42238      */
42239     getText : function(){
42240         return this.text;
42241     },
42242     /*
42243     autoSize : function(){
42244         //this.el.beginMeasure();
42245         this.textEl.setWidth(1);
42246         /*
42247          *  #2804 [new] Tabs in Roojs
42248          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42249          */
42250         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42251         //this.el.endMeasure();
42252     //},
42253
42254     /**
42255      * Sets the text for the tab (Note: this also sets the tooltip text)
42256      * @param {String} text The tab's text and tooltip
42257      */
42258     setText : function(text){
42259         this.text = text;
42260         this.textEl.update(text);
42261         this.setTooltip(text);
42262         //if(!this.tabPanel.resizeTabs){
42263         //    this.autoSize();
42264         //}
42265     },
42266     /**
42267      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42268      */
42269     activate : function(){
42270         this.tabPanel.activate(this.id);
42271     },
42272
42273     /**
42274      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42275      */
42276     disable : function(){
42277         if(this.tabPanel.active != this){
42278             this.disabled = true;
42279             this.status_node.addClass("disabled");
42280         }
42281     },
42282
42283     /**
42284      * Enables this TabPanelItem if it was previously disabled.
42285      */
42286     enable : function(){
42287         this.disabled = false;
42288         this.status_node.removeClass("disabled");
42289     },
42290
42291     /**
42292      * Sets the content for this TabPanelItem.
42293      * @param {String} content The content
42294      * @param {Boolean} loadScripts true to look for and load scripts
42295      */
42296     setContent : function(content, loadScripts){
42297         this.bodyEl.update(content, loadScripts);
42298     },
42299
42300     /**
42301      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42302      * @return {Roo.UpdateManager} The UpdateManager
42303      */
42304     getUpdateManager : function(){
42305         return this.bodyEl.getUpdateManager();
42306     },
42307
42308     /**
42309      * Set a URL to be used to load the content for this TabPanelItem.
42310      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42311      * @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)
42312      * @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)
42313      * @return {Roo.UpdateManager} The UpdateManager
42314      */
42315     setUrl : function(url, params, loadOnce){
42316         if(this.refreshDelegate){
42317             this.un('activate', this.refreshDelegate);
42318         }
42319         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42320         this.on("activate", this.refreshDelegate);
42321         return this.bodyEl.getUpdateManager();
42322     },
42323
42324     /** @private */
42325     _handleRefresh : function(url, params, loadOnce){
42326         if(!loadOnce || !this.loaded){
42327             var updater = this.bodyEl.getUpdateManager();
42328             updater.update(url, params, this._setLoaded.createDelegate(this));
42329         }
42330     },
42331
42332     /**
42333      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42334      *   Will fail silently if the setUrl method has not been called.
42335      *   This does not activate the panel, just updates its content.
42336      */
42337     refresh : function(){
42338         if(this.refreshDelegate){
42339            this.loaded = false;
42340            this.refreshDelegate();
42341         }
42342     },
42343
42344     /** @private */
42345     _setLoaded : function(){
42346         this.loaded = true;
42347     },
42348
42349     /** @private */
42350     closeClick : function(e){
42351         var o = {};
42352         e.stopEvent();
42353         this.fireEvent("beforeclose", this, o);
42354         if(o.cancel !== true){
42355             this.tabPanel.removeTab(this.id);
42356         }
42357     },
42358     /**
42359      * The text displayed in the tooltip for the close icon.
42360      * @type String
42361      */
42362     closeText : "Close this tab"
42363 });
42364 /**
42365 *    This script refer to:
42366 *    Title: International Telephone Input
42367 *    Author: Jack O'Connor
42368 *    Code version:  v12.1.12
42369 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42370 **/
42371
42372 Roo.bootstrap.PhoneInputData = function() {
42373     var d = [
42374       [
42375         "Afghanistan (‫افغانستان‬‎)",
42376         "af",
42377         "93"
42378       ],
42379       [
42380         "Albania (Shqipëri)",
42381         "al",
42382         "355"
42383       ],
42384       [
42385         "Algeria (‫الجزائر‬‎)",
42386         "dz",
42387         "213"
42388       ],
42389       [
42390         "American Samoa",
42391         "as",
42392         "1684"
42393       ],
42394       [
42395         "Andorra",
42396         "ad",
42397         "376"
42398       ],
42399       [
42400         "Angola",
42401         "ao",
42402         "244"
42403       ],
42404       [
42405         "Anguilla",
42406         "ai",
42407         "1264"
42408       ],
42409       [
42410         "Antigua and Barbuda",
42411         "ag",
42412         "1268"
42413       ],
42414       [
42415         "Argentina",
42416         "ar",
42417         "54"
42418       ],
42419       [
42420         "Armenia (Հայաստան)",
42421         "am",
42422         "374"
42423       ],
42424       [
42425         "Aruba",
42426         "aw",
42427         "297"
42428       ],
42429       [
42430         "Australia",
42431         "au",
42432         "61",
42433         0
42434       ],
42435       [
42436         "Austria (Österreich)",
42437         "at",
42438         "43"
42439       ],
42440       [
42441         "Azerbaijan (Azərbaycan)",
42442         "az",
42443         "994"
42444       ],
42445       [
42446         "Bahamas",
42447         "bs",
42448         "1242"
42449       ],
42450       [
42451         "Bahrain (‫البحرين‬‎)",
42452         "bh",
42453         "973"
42454       ],
42455       [
42456         "Bangladesh (বাংলাদেশ)",
42457         "bd",
42458         "880"
42459       ],
42460       [
42461         "Barbados",
42462         "bb",
42463         "1246"
42464       ],
42465       [
42466         "Belarus (Беларусь)",
42467         "by",
42468         "375"
42469       ],
42470       [
42471         "Belgium (België)",
42472         "be",
42473         "32"
42474       ],
42475       [
42476         "Belize",
42477         "bz",
42478         "501"
42479       ],
42480       [
42481         "Benin (Bénin)",
42482         "bj",
42483         "229"
42484       ],
42485       [
42486         "Bermuda",
42487         "bm",
42488         "1441"
42489       ],
42490       [
42491         "Bhutan (འབྲུག)",
42492         "bt",
42493         "975"
42494       ],
42495       [
42496         "Bolivia",
42497         "bo",
42498         "591"
42499       ],
42500       [
42501         "Bosnia and Herzegovina (Босна и Херцеговина)",
42502         "ba",
42503         "387"
42504       ],
42505       [
42506         "Botswana",
42507         "bw",
42508         "267"
42509       ],
42510       [
42511         "Brazil (Brasil)",
42512         "br",
42513         "55"
42514       ],
42515       [
42516         "British Indian Ocean Territory",
42517         "io",
42518         "246"
42519       ],
42520       [
42521         "British Virgin Islands",
42522         "vg",
42523         "1284"
42524       ],
42525       [
42526         "Brunei",
42527         "bn",
42528         "673"
42529       ],
42530       [
42531         "Bulgaria (България)",
42532         "bg",
42533         "359"
42534       ],
42535       [
42536         "Burkina Faso",
42537         "bf",
42538         "226"
42539       ],
42540       [
42541         "Burundi (Uburundi)",
42542         "bi",
42543         "257"
42544       ],
42545       [
42546         "Cambodia (កម្ពុជា)",
42547         "kh",
42548         "855"
42549       ],
42550       [
42551         "Cameroon (Cameroun)",
42552         "cm",
42553         "237"
42554       ],
42555       [
42556         "Canada",
42557         "ca",
42558         "1",
42559         1,
42560         ["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"]
42561       ],
42562       [
42563         "Cape Verde (Kabu Verdi)",
42564         "cv",
42565         "238"
42566       ],
42567       [
42568         "Caribbean Netherlands",
42569         "bq",
42570         "599",
42571         1
42572       ],
42573       [
42574         "Cayman Islands",
42575         "ky",
42576         "1345"
42577       ],
42578       [
42579         "Central African Republic (République centrafricaine)",
42580         "cf",
42581         "236"
42582       ],
42583       [
42584         "Chad (Tchad)",
42585         "td",
42586         "235"
42587       ],
42588       [
42589         "Chile",
42590         "cl",
42591         "56"
42592       ],
42593       [
42594         "China (中国)",
42595         "cn",
42596         "86"
42597       ],
42598       [
42599         "Christmas Island",
42600         "cx",
42601         "61",
42602         2
42603       ],
42604       [
42605         "Cocos (Keeling) Islands",
42606         "cc",
42607         "61",
42608         1
42609       ],
42610       [
42611         "Colombia",
42612         "co",
42613         "57"
42614       ],
42615       [
42616         "Comoros (‫جزر القمر‬‎)",
42617         "km",
42618         "269"
42619       ],
42620       [
42621         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42622         "cd",
42623         "243"
42624       ],
42625       [
42626         "Congo (Republic) (Congo-Brazzaville)",
42627         "cg",
42628         "242"
42629       ],
42630       [
42631         "Cook Islands",
42632         "ck",
42633         "682"
42634       ],
42635       [
42636         "Costa Rica",
42637         "cr",
42638         "506"
42639       ],
42640       [
42641         "Côte d’Ivoire",
42642         "ci",
42643         "225"
42644       ],
42645       [
42646         "Croatia (Hrvatska)",
42647         "hr",
42648         "385"
42649       ],
42650       [
42651         "Cuba",
42652         "cu",
42653         "53"
42654       ],
42655       [
42656         "Curaçao",
42657         "cw",
42658         "599",
42659         0
42660       ],
42661       [
42662         "Cyprus (Κύπρος)",
42663         "cy",
42664         "357"
42665       ],
42666       [
42667         "Czech Republic (Česká republika)",
42668         "cz",
42669         "420"
42670       ],
42671       [
42672         "Denmark (Danmark)",
42673         "dk",
42674         "45"
42675       ],
42676       [
42677         "Djibouti",
42678         "dj",
42679         "253"
42680       ],
42681       [
42682         "Dominica",
42683         "dm",
42684         "1767"
42685       ],
42686       [
42687         "Dominican Republic (República Dominicana)",
42688         "do",
42689         "1",
42690         2,
42691         ["809", "829", "849"]
42692       ],
42693       [
42694         "Ecuador",
42695         "ec",
42696         "593"
42697       ],
42698       [
42699         "Egypt (‫مصر‬‎)",
42700         "eg",
42701         "20"
42702       ],
42703       [
42704         "El Salvador",
42705         "sv",
42706         "503"
42707       ],
42708       [
42709         "Equatorial Guinea (Guinea Ecuatorial)",
42710         "gq",
42711         "240"
42712       ],
42713       [
42714         "Eritrea",
42715         "er",
42716         "291"
42717       ],
42718       [
42719         "Estonia (Eesti)",
42720         "ee",
42721         "372"
42722       ],
42723       [
42724         "Ethiopia",
42725         "et",
42726         "251"
42727       ],
42728       [
42729         "Falkland Islands (Islas Malvinas)",
42730         "fk",
42731         "500"
42732       ],
42733       [
42734         "Faroe Islands (Føroyar)",
42735         "fo",
42736         "298"
42737       ],
42738       [
42739         "Fiji",
42740         "fj",
42741         "679"
42742       ],
42743       [
42744         "Finland (Suomi)",
42745         "fi",
42746         "358",
42747         0
42748       ],
42749       [
42750         "France",
42751         "fr",
42752         "33"
42753       ],
42754       [
42755         "French Guiana (Guyane française)",
42756         "gf",
42757         "594"
42758       ],
42759       [
42760         "French Polynesia (Polynésie française)",
42761         "pf",
42762         "689"
42763       ],
42764       [
42765         "Gabon",
42766         "ga",
42767         "241"
42768       ],
42769       [
42770         "Gambia",
42771         "gm",
42772         "220"
42773       ],
42774       [
42775         "Georgia (საქართველო)",
42776         "ge",
42777         "995"
42778       ],
42779       [
42780         "Germany (Deutschland)",
42781         "de",
42782         "49"
42783       ],
42784       [
42785         "Ghana (Gaana)",
42786         "gh",
42787         "233"
42788       ],
42789       [
42790         "Gibraltar",
42791         "gi",
42792         "350"
42793       ],
42794       [
42795         "Greece (Ελλάδα)",
42796         "gr",
42797         "30"
42798       ],
42799       [
42800         "Greenland (Kalaallit Nunaat)",
42801         "gl",
42802         "299"
42803       ],
42804       [
42805         "Grenada",
42806         "gd",
42807         "1473"
42808       ],
42809       [
42810         "Guadeloupe",
42811         "gp",
42812         "590",
42813         0
42814       ],
42815       [
42816         "Guam",
42817         "gu",
42818         "1671"
42819       ],
42820       [
42821         "Guatemala",
42822         "gt",
42823         "502"
42824       ],
42825       [
42826         "Guernsey",
42827         "gg",
42828         "44",
42829         1
42830       ],
42831       [
42832         "Guinea (Guinée)",
42833         "gn",
42834         "224"
42835       ],
42836       [
42837         "Guinea-Bissau (Guiné Bissau)",
42838         "gw",
42839         "245"
42840       ],
42841       [
42842         "Guyana",
42843         "gy",
42844         "592"
42845       ],
42846       [
42847         "Haiti",
42848         "ht",
42849         "509"
42850       ],
42851       [
42852         "Honduras",
42853         "hn",
42854         "504"
42855       ],
42856       [
42857         "Hong Kong (香港)",
42858         "hk",
42859         "852"
42860       ],
42861       [
42862         "Hungary (Magyarország)",
42863         "hu",
42864         "36"
42865       ],
42866       [
42867         "Iceland (Ísland)",
42868         "is",
42869         "354"
42870       ],
42871       [
42872         "India (भारत)",
42873         "in",
42874         "91"
42875       ],
42876       [
42877         "Indonesia",
42878         "id",
42879         "62"
42880       ],
42881       [
42882         "Iran (‫ایران‬‎)",
42883         "ir",
42884         "98"
42885       ],
42886       [
42887         "Iraq (‫العراق‬‎)",
42888         "iq",
42889         "964"
42890       ],
42891       [
42892         "Ireland",
42893         "ie",
42894         "353"
42895       ],
42896       [
42897         "Isle of Man",
42898         "im",
42899         "44",
42900         2
42901       ],
42902       [
42903         "Israel (‫ישראל‬‎)",
42904         "il",
42905         "972"
42906       ],
42907       [
42908         "Italy (Italia)",
42909         "it",
42910         "39",
42911         0
42912       ],
42913       [
42914         "Jamaica",
42915         "jm",
42916         "1876"
42917       ],
42918       [
42919         "Japan (日本)",
42920         "jp",
42921         "81"
42922       ],
42923       [
42924         "Jersey",
42925         "je",
42926         "44",
42927         3
42928       ],
42929       [
42930         "Jordan (‫الأردن‬‎)",
42931         "jo",
42932         "962"
42933       ],
42934       [
42935         "Kazakhstan (Казахстан)",
42936         "kz",
42937         "7",
42938         1
42939       ],
42940       [
42941         "Kenya",
42942         "ke",
42943         "254"
42944       ],
42945       [
42946         "Kiribati",
42947         "ki",
42948         "686"
42949       ],
42950       [
42951         "Kosovo",
42952         "xk",
42953         "383"
42954       ],
42955       [
42956         "Kuwait (‫الكويت‬‎)",
42957         "kw",
42958         "965"
42959       ],
42960       [
42961         "Kyrgyzstan (Кыргызстан)",
42962         "kg",
42963         "996"
42964       ],
42965       [
42966         "Laos (ລາວ)",
42967         "la",
42968         "856"
42969       ],
42970       [
42971         "Latvia (Latvija)",
42972         "lv",
42973         "371"
42974       ],
42975       [
42976         "Lebanon (‫لبنان‬‎)",
42977         "lb",
42978         "961"
42979       ],
42980       [
42981         "Lesotho",
42982         "ls",
42983         "266"
42984       ],
42985       [
42986         "Liberia",
42987         "lr",
42988         "231"
42989       ],
42990       [
42991         "Libya (‫ليبيا‬‎)",
42992         "ly",
42993         "218"
42994       ],
42995       [
42996         "Liechtenstein",
42997         "li",
42998         "423"
42999       ],
43000       [
43001         "Lithuania (Lietuva)",
43002         "lt",
43003         "370"
43004       ],
43005       [
43006         "Luxembourg",
43007         "lu",
43008         "352"
43009       ],
43010       [
43011         "Macau (澳門)",
43012         "mo",
43013         "853"
43014       ],
43015       [
43016         "Macedonia (FYROM) (Македонија)",
43017         "mk",
43018         "389"
43019       ],
43020       [
43021         "Madagascar (Madagasikara)",
43022         "mg",
43023         "261"
43024       ],
43025       [
43026         "Malawi",
43027         "mw",
43028         "265"
43029       ],
43030       [
43031         "Malaysia",
43032         "my",
43033         "60"
43034       ],
43035       [
43036         "Maldives",
43037         "mv",
43038         "960"
43039       ],
43040       [
43041         "Mali",
43042         "ml",
43043         "223"
43044       ],
43045       [
43046         "Malta",
43047         "mt",
43048         "356"
43049       ],
43050       [
43051         "Marshall Islands",
43052         "mh",
43053         "692"
43054       ],
43055       [
43056         "Martinique",
43057         "mq",
43058         "596"
43059       ],
43060       [
43061         "Mauritania (‫موريتانيا‬‎)",
43062         "mr",
43063         "222"
43064       ],
43065       [
43066         "Mauritius (Moris)",
43067         "mu",
43068         "230"
43069       ],
43070       [
43071         "Mayotte",
43072         "yt",
43073         "262",
43074         1
43075       ],
43076       [
43077         "Mexico (México)",
43078         "mx",
43079         "52"
43080       ],
43081       [
43082         "Micronesia",
43083         "fm",
43084         "691"
43085       ],
43086       [
43087         "Moldova (Republica Moldova)",
43088         "md",
43089         "373"
43090       ],
43091       [
43092         "Monaco",
43093         "mc",
43094         "377"
43095       ],
43096       [
43097         "Mongolia (Монгол)",
43098         "mn",
43099         "976"
43100       ],
43101       [
43102         "Montenegro (Crna Gora)",
43103         "me",
43104         "382"
43105       ],
43106       [
43107         "Montserrat",
43108         "ms",
43109         "1664"
43110       ],
43111       [
43112         "Morocco (‫المغرب‬‎)",
43113         "ma",
43114         "212",
43115         0
43116       ],
43117       [
43118         "Mozambique (Moçambique)",
43119         "mz",
43120         "258"
43121       ],
43122       [
43123         "Myanmar (Burma) (မြန်မာ)",
43124         "mm",
43125         "95"
43126       ],
43127       [
43128         "Namibia (Namibië)",
43129         "na",
43130         "264"
43131       ],
43132       [
43133         "Nauru",
43134         "nr",
43135         "674"
43136       ],
43137       [
43138         "Nepal (नेपाल)",
43139         "np",
43140         "977"
43141       ],
43142       [
43143         "Netherlands (Nederland)",
43144         "nl",
43145         "31"
43146       ],
43147       [
43148         "New Caledonia (Nouvelle-Calédonie)",
43149         "nc",
43150         "687"
43151       ],
43152       [
43153         "New Zealand",
43154         "nz",
43155         "64"
43156       ],
43157       [
43158         "Nicaragua",
43159         "ni",
43160         "505"
43161       ],
43162       [
43163         "Niger (Nijar)",
43164         "ne",
43165         "227"
43166       ],
43167       [
43168         "Nigeria",
43169         "ng",
43170         "234"
43171       ],
43172       [
43173         "Niue",
43174         "nu",
43175         "683"
43176       ],
43177       [
43178         "Norfolk Island",
43179         "nf",
43180         "672"
43181       ],
43182       [
43183         "North Korea (조선 민주주의 인민 공화국)",
43184         "kp",
43185         "850"
43186       ],
43187       [
43188         "Northern Mariana Islands",
43189         "mp",
43190         "1670"
43191       ],
43192       [
43193         "Norway (Norge)",
43194         "no",
43195         "47",
43196         0
43197       ],
43198       [
43199         "Oman (‫عُمان‬‎)",
43200         "om",
43201         "968"
43202       ],
43203       [
43204         "Pakistan (‫پاکستان‬‎)",
43205         "pk",
43206         "92"
43207       ],
43208       [
43209         "Palau",
43210         "pw",
43211         "680"
43212       ],
43213       [
43214         "Palestine (‫فلسطين‬‎)",
43215         "ps",
43216         "970"
43217       ],
43218       [
43219         "Panama (Panamá)",
43220         "pa",
43221         "507"
43222       ],
43223       [
43224         "Papua New Guinea",
43225         "pg",
43226         "675"
43227       ],
43228       [
43229         "Paraguay",
43230         "py",
43231         "595"
43232       ],
43233       [
43234         "Peru (Perú)",
43235         "pe",
43236         "51"
43237       ],
43238       [
43239         "Philippines",
43240         "ph",
43241         "63"
43242       ],
43243       [
43244         "Poland (Polska)",
43245         "pl",
43246         "48"
43247       ],
43248       [
43249         "Portugal",
43250         "pt",
43251         "351"
43252       ],
43253       [
43254         "Puerto Rico",
43255         "pr",
43256         "1",
43257         3,
43258         ["787", "939"]
43259       ],
43260       [
43261         "Qatar (‫قطر‬‎)",
43262         "qa",
43263         "974"
43264       ],
43265       [
43266         "Réunion (La Réunion)",
43267         "re",
43268         "262",
43269         0
43270       ],
43271       [
43272         "Romania (România)",
43273         "ro",
43274         "40"
43275       ],
43276       [
43277         "Russia (Россия)",
43278         "ru",
43279         "7",
43280         0
43281       ],
43282       [
43283         "Rwanda",
43284         "rw",
43285         "250"
43286       ],
43287       [
43288         "Saint Barthélemy",
43289         "bl",
43290         "590",
43291         1
43292       ],
43293       [
43294         "Saint Helena",
43295         "sh",
43296         "290"
43297       ],
43298       [
43299         "Saint Kitts and Nevis",
43300         "kn",
43301         "1869"
43302       ],
43303       [
43304         "Saint Lucia",
43305         "lc",
43306         "1758"
43307       ],
43308       [
43309         "Saint Martin (Saint-Martin (partie française))",
43310         "mf",
43311         "590",
43312         2
43313       ],
43314       [
43315         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43316         "pm",
43317         "508"
43318       ],
43319       [
43320         "Saint Vincent and the Grenadines",
43321         "vc",
43322         "1784"
43323       ],
43324       [
43325         "Samoa",
43326         "ws",
43327         "685"
43328       ],
43329       [
43330         "San Marino",
43331         "sm",
43332         "378"
43333       ],
43334       [
43335         "São Tomé and Príncipe (São Tomé e Príncipe)",
43336         "st",
43337         "239"
43338       ],
43339       [
43340         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43341         "sa",
43342         "966"
43343       ],
43344       [
43345         "Senegal (Sénégal)",
43346         "sn",
43347         "221"
43348       ],
43349       [
43350         "Serbia (Србија)",
43351         "rs",
43352         "381"
43353       ],
43354       [
43355         "Seychelles",
43356         "sc",
43357         "248"
43358       ],
43359       [
43360         "Sierra Leone",
43361         "sl",
43362         "232"
43363       ],
43364       [
43365         "Singapore",
43366         "sg",
43367         "65"
43368       ],
43369       [
43370         "Sint Maarten",
43371         "sx",
43372         "1721"
43373       ],
43374       [
43375         "Slovakia (Slovensko)",
43376         "sk",
43377         "421"
43378       ],
43379       [
43380         "Slovenia (Slovenija)",
43381         "si",
43382         "386"
43383       ],
43384       [
43385         "Solomon Islands",
43386         "sb",
43387         "677"
43388       ],
43389       [
43390         "Somalia (Soomaaliya)",
43391         "so",
43392         "252"
43393       ],
43394       [
43395         "South Africa",
43396         "za",
43397         "27"
43398       ],
43399       [
43400         "South Korea (대한민국)",
43401         "kr",
43402         "82"
43403       ],
43404       [
43405         "South Sudan (‫جنوب السودان‬‎)",
43406         "ss",
43407         "211"
43408       ],
43409       [
43410         "Spain (España)",
43411         "es",
43412         "34"
43413       ],
43414       [
43415         "Sri Lanka (ශ්‍රී ලංකාව)",
43416         "lk",
43417         "94"
43418       ],
43419       [
43420         "Sudan (‫السودان‬‎)",
43421         "sd",
43422         "249"
43423       ],
43424       [
43425         "Suriname",
43426         "sr",
43427         "597"
43428       ],
43429       [
43430         "Svalbard and Jan Mayen",
43431         "sj",
43432         "47",
43433         1
43434       ],
43435       [
43436         "Swaziland",
43437         "sz",
43438         "268"
43439       ],
43440       [
43441         "Sweden (Sverige)",
43442         "se",
43443         "46"
43444       ],
43445       [
43446         "Switzerland (Schweiz)",
43447         "ch",
43448         "41"
43449       ],
43450       [
43451         "Syria (‫سوريا‬‎)",
43452         "sy",
43453         "963"
43454       ],
43455       [
43456         "Taiwan (台灣)",
43457         "tw",
43458         "886"
43459       ],
43460       [
43461         "Tajikistan",
43462         "tj",
43463         "992"
43464       ],
43465       [
43466         "Tanzania",
43467         "tz",
43468         "255"
43469       ],
43470       [
43471         "Thailand (ไทย)",
43472         "th",
43473         "66"
43474       ],
43475       [
43476         "Timor-Leste",
43477         "tl",
43478         "670"
43479       ],
43480       [
43481         "Togo",
43482         "tg",
43483         "228"
43484       ],
43485       [
43486         "Tokelau",
43487         "tk",
43488         "690"
43489       ],
43490       [
43491         "Tonga",
43492         "to",
43493         "676"
43494       ],
43495       [
43496         "Trinidad and Tobago",
43497         "tt",
43498         "1868"
43499       ],
43500       [
43501         "Tunisia (‫تونس‬‎)",
43502         "tn",
43503         "216"
43504       ],
43505       [
43506         "Turkey (Türkiye)",
43507         "tr",
43508         "90"
43509       ],
43510       [
43511         "Turkmenistan",
43512         "tm",
43513         "993"
43514       ],
43515       [
43516         "Turks and Caicos Islands",
43517         "tc",
43518         "1649"
43519       ],
43520       [
43521         "Tuvalu",
43522         "tv",
43523         "688"
43524       ],
43525       [
43526         "U.S. Virgin Islands",
43527         "vi",
43528         "1340"
43529       ],
43530       [
43531         "Uganda",
43532         "ug",
43533         "256"
43534       ],
43535       [
43536         "Ukraine (Україна)",
43537         "ua",
43538         "380"
43539       ],
43540       [
43541         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43542         "ae",
43543         "971"
43544       ],
43545       [
43546         "United Kingdom",
43547         "gb",
43548         "44",
43549         0
43550       ],
43551       [
43552         "United States",
43553         "us",
43554         "1",
43555         0
43556       ],
43557       [
43558         "Uruguay",
43559         "uy",
43560         "598"
43561       ],
43562       [
43563         "Uzbekistan (Oʻzbekiston)",
43564         "uz",
43565         "998"
43566       ],
43567       [
43568         "Vanuatu",
43569         "vu",
43570         "678"
43571       ],
43572       [
43573         "Vatican City (Città del Vaticano)",
43574         "va",
43575         "39",
43576         1
43577       ],
43578       [
43579         "Venezuela",
43580         "ve",
43581         "58"
43582       ],
43583       [
43584         "Vietnam (Việt Nam)",
43585         "vn",
43586         "84"
43587       ],
43588       [
43589         "Wallis and Futuna (Wallis-et-Futuna)",
43590         "wf",
43591         "681"
43592       ],
43593       [
43594         "Western Sahara (‫الصحراء الغربية‬‎)",
43595         "eh",
43596         "212",
43597         1
43598       ],
43599       [
43600         "Yemen (‫اليمن‬‎)",
43601         "ye",
43602         "967"
43603       ],
43604       [
43605         "Zambia",
43606         "zm",
43607         "260"
43608       ],
43609       [
43610         "Zimbabwe",
43611         "zw",
43612         "263"
43613       ],
43614       [
43615         "Åland Islands",
43616         "ax",
43617         "358",
43618         1
43619       ]
43620   ];
43621   
43622   return d;
43623 }/**
43624 *    This script refer to:
43625 *    Title: International Telephone Input
43626 *    Author: Jack O'Connor
43627 *    Code version:  v12.1.12
43628 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43629 **/
43630
43631 /**
43632  * @class Roo.bootstrap.PhoneInput
43633  * @extends Roo.bootstrap.TriggerField
43634  * An input with International dial-code selection
43635  
43636  * @cfg {String} defaultDialCode default '+852'
43637  * @cfg {Array} preferedCountries default []
43638   
43639  * @constructor
43640  * Create a new PhoneInput.
43641  * @param {Object} config Configuration options
43642  */
43643
43644 Roo.bootstrap.PhoneInput = function(config) {
43645     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43646 };
43647
43648 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43649         /**
43650         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43651         */
43652         listWidth: undefined,
43653         
43654         selectedClass: 'active',
43655         
43656         invalidClass : "has-warning",
43657         
43658         validClass: 'has-success',
43659         
43660         allowed: '0123456789',
43661         
43662         max_length: 15,
43663         
43664         /**
43665          * @cfg {String} defaultDialCode The default dial code when initializing the input
43666          */
43667         defaultDialCode: '+852',
43668         
43669         /**
43670          * @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
43671          */
43672         preferedCountries: false,
43673         
43674         getAutoCreate : function()
43675         {
43676             var data = Roo.bootstrap.PhoneInputData();
43677             var align = this.labelAlign || this.parentLabelAlign();
43678             var id = Roo.id();
43679             
43680             this.allCountries = [];
43681             this.dialCodeMapping = [];
43682             
43683             for (var i = 0; i < data.length; i++) {
43684               var c = data[i];
43685               this.allCountries[i] = {
43686                 name: c[0],
43687                 iso2: c[1],
43688                 dialCode: c[2],
43689                 priority: c[3] || 0,
43690                 areaCodes: c[4] || null
43691               };
43692               this.dialCodeMapping[c[2]] = {
43693                   name: c[0],
43694                   iso2: c[1],
43695                   priority: c[3] || 0,
43696                   areaCodes: c[4] || null
43697               };
43698             }
43699             
43700             var cfg = {
43701                 cls: 'form-group',
43702                 cn: []
43703             };
43704             
43705             var input =  {
43706                 tag: 'input',
43707                 id : id,
43708                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43709                 maxlength: this.max_length,
43710                 cls : 'form-control tel-input',
43711                 autocomplete: 'new-password'
43712             };
43713             
43714             var hiddenInput = {
43715                 tag: 'input',
43716                 type: 'hidden',
43717                 cls: 'hidden-tel-input'
43718             };
43719             
43720             if (this.name) {
43721                 hiddenInput.name = this.name;
43722             }
43723             
43724             if (this.disabled) {
43725                 input.disabled = true;
43726             }
43727             
43728             var flag_container = {
43729                 tag: 'div',
43730                 cls: 'flag-box',
43731                 cn: [
43732                     {
43733                         tag: 'div',
43734                         cls: 'flag'
43735                     },
43736                     {
43737                         tag: 'div',
43738                         cls: 'caret'
43739                     }
43740                 ]
43741             };
43742             
43743             var box = {
43744                 tag: 'div',
43745                 cls: this.hasFeedback ? 'has-feedback' : '',
43746                 cn: [
43747                     hiddenInput,
43748                     input,
43749                     {
43750                         tag: 'input',
43751                         cls: 'dial-code-holder',
43752                         disabled: true
43753                     }
43754                 ]
43755             };
43756             
43757             var container = {
43758                 cls: 'roo-select2-container input-group',
43759                 cn: [
43760                     flag_container,
43761                     box
43762                 ]
43763             };
43764             
43765             if (this.fieldLabel.length) {
43766                 var indicator = {
43767                     tag: 'i',
43768                     tooltip: 'This field is required'
43769                 };
43770                 
43771                 var label = {
43772                     tag: 'label',
43773                     'for':  id,
43774                     cls: 'control-label',
43775                     cn: []
43776                 };
43777                 
43778                 var label_text = {
43779                     tag: 'span',
43780                     html: this.fieldLabel
43781                 };
43782                 
43783                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43784                 label.cn = [
43785                     indicator,
43786                     label_text
43787                 ];
43788                 
43789                 if(this.indicatorpos == 'right') {
43790                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43791                     label.cn = [
43792                         label_text,
43793                         indicator
43794                     ];
43795                 }
43796                 
43797                 if(align == 'left') {
43798                     container = {
43799                         tag: 'div',
43800                         cn: [
43801                             container
43802                         ]
43803                     };
43804                     
43805                     if(this.labelWidth > 12){
43806                         label.style = "width: " + this.labelWidth + 'px';
43807                     }
43808                     if(this.labelWidth < 13 && this.labelmd == 0){
43809                         this.labelmd = this.labelWidth;
43810                     }
43811                     if(this.labellg > 0){
43812                         label.cls += ' col-lg-' + this.labellg;
43813                         input.cls += ' col-lg-' + (12 - this.labellg);
43814                     }
43815                     if(this.labelmd > 0){
43816                         label.cls += ' col-md-' + this.labelmd;
43817                         container.cls += ' col-md-' + (12 - this.labelmd);
43818                     }
43819                     if(this.labelsm > 0){
43820                         label.cls += ' col-sm-' + this.labelsm;
43821                         container.cls += ' col-sm-' + (12 - this.labelsm);
43822                     }
43823                     if(this.labelxs > 0){
43824                         label.cls += ' col-xs-' + this.labelxs;
43825                         container.cls += ' col-xs-' + (12 - this.labelxs);
43826                     }
43827                 }
43828             }
43829             
43830             cfg.cn = [
43831                 label,
43832                 container
43833             ];
43834             
43835             var settings = this;
43836             
43837             ['xs','sm','md','lg'].map(function(size){
43838                 if (settings[size]) {
43839                     cfg.cls += ' col-' + size + '-' + settings[size];
43840                 }
43841             });
43842             
43843             this.store = new Roo.data.Store({
43844                 proxy : new Roo.data.MemoryProxy({}),
43845                 reader : new Roo.data.JsonReader({
43846                     fields : [
43847                         {
43848                             'name' : 'name',
43849                             'type' : 'string'
43850                         },
43851                         {
43852                             'name' : 'iso2',
43853                             'type' : 'string'
43854                         },
43855                         {
43856                             'name' : 'dialCode',
43857                             'type' : 'string'
43858                         },
43859                         {
43860                             'name' : 'priority',
43861                             'type' : 'string'
43862                         },
43863                         {
43864                             'name' : 'areaCodes',
43865                             'type' : 'string'
43866                         }
43867                     ]
43868                 })
43869             });
43870             
43871             if(!this.preferedCountries) {
43872                 this.preferedCountries = [
43873                     'hk',
43874                     'gb',
43875                     'us'
43876                 ];
43877             }
43878             
43879             var p = this.preferedCountries.reverse();
43880             
43881             if(p) {
43882                 for (var i = 0; i < p.length; i++) {
43883                     for (var j = 0; j < this.allCountries.length; j++) {
43884                         if(this.allCountries[j].iso2 == p[i]) {
43885                             var t = this.allCountries[j];
43886                             this.allCountries.splice(j,1);
43887                             this.allCountries.unshift(t);
43888                         }
43889                     } 
43890                 }
43891             }
43892             
43893             this.store.proxy.data = {
43894                 success: true,
43895                 data: this.allCountries
43896             };
43897             
43898             return cfg;
43899         },
43900         
43901         initEvents : function()
43902         {
43903             this.createList();
43904             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43905             
43906             this.indicator = this.indicatorEl();
43907             this.flag = this.flagEl();
43908             this.dialCodeHolder = this.dialCodeHolderEl();
43909             
43910             this.trigger = this.el.select('div.flag-box',true).first();
43911             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43912             
43913             var _this = this;
43914             
43915             (function(){
43916                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43917                 _this.list.setWidth(lw);
43918             }).defer(100);
43919             
43920             this.list.on('mouseover', this.onViewOver, this);
43921             this.list.on('mousemove', this.onViewMove, this);
43922             this.inputEl().on("keyup", this.onKeyUp, this);
43923             this.inputEl().on("keypress", this.onKeyPress, this);
43924             
43925             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
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             this.setValue(this.defaultDialCode);
43933         },
43934         
43935         onTriggerClick : function(e)
43936         {
43937             Roo.log('trigger click');
43938             if(this.disabled){
43939                 return;
43940             }
43941             
43942             if(this.isExpanded()){
43943                 this.collapse();
43944                 this.hasFocus = false;
43945             }else {
43946                 this.store.load({});
43947                 this.hasFocus = true;
43948                 this.expand();
43949             }
43950         },
43951         
43952         isExpanded : function()
43953         {
43954             return this.list.isVisible();
43955         },
43956         
43957         collapse : function()
43958         {
43959             if(!this.isExpanded()){
43960                 return;
43961             }
43962             this.list.hide();
43963             Roo.get(document).un('mousedown', this.collapseIf, this);
43964             Roo.get(document).un('mousewheel', this.collapseIf, this);
43965             this.fireEvent('collapse', this);
43966             this.validate();
43967         },
43968         
43969         expand : function()
43970         {
43971             Roo.log('expand');
43972
43973             if(this.isExpanded() || !this.hasFocus){
43974                 return;
43975             }
43976             
43977             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43978             this.list.setWidth(lw);
43979             
43980             this.list.show();
43981             this.restrictHeight();
43982             
43983             Roo.get(document).on('mousedown', this.collapseIf, this);
43984             Roo.get(document).on('mousewheel', this.collapseIf, this);
43985             
43986             this.fireEvent('expand', this);
43987         },
43988         
43989         restrictHeight : function()
43990         {
43991             this.list.alignTo(this.inputEl(), this.listAlign);
43992             this.list.alignTo(this.inputEl(), this.listAlign);
43993         },
43994         
43995         onViewOver : function(e, t)
43996         {
43997             if(this.inKeyMode){
43998                 return;
43999             }
44000             var item = this.view.findItemFromChild(t);
44001             
44002             if(item){
44003                 var index = this.view.indexOf(item);
44004                 this.select(index, false);
44005             }
44006         },
44007
44008         // private
44009         onViewClick : function(view, doFocus, el, e)
44010         {
44011             var index = this.view.getSelectedIndexes()[0];
44012             
44013             var r = this.store.getAt(index);
44014             
44015             if(r){
44016                 this.onSelect(r, index);
44017             }
44018             if(doFocus !== false && !this.blockFocus){
44019                 this.inputEl().focus();
44020             }
44021         },
44022         
44023         onViewMove : function(e, t)
44024         {
44025             this.inKeyMode = false;
44026         },
44027         
44028         select : function(index, scrollIntoView)
44029         {
44030             this.selectedIndex = index;
44031             this.view.select(index);
44032             if(scrollIntoView !== false){
44033                 var el = this.view.getNode(index);
44034                 if(el){
44035                     this.list.scrollChildIntoView(el, false);
44036                 }
44037             }
44038         },
44039         
44040         createList : function()
44041         {
44042             this.list = Roo.get(document.body).createChild({
44043                 tag: 'ul',
44044                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44045                 style: 'display:none'
44046             });
44047             
44048             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44049         },
44050         
44051         collapseIf : function(e)
44052         {
44053             var in_combo  = e.within(this.el);
44054             var in_list =  e.within(this.list);
44055             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44056             
44057             if (in_combo || in_list || is_list) {
44058                 return;
44059             }
44060             this.collapse();
44061         },
44062         
44063         onSelect : function(record, index)
44064         {
44065             if(this.fireEvent('beforeselect', this, record, index) !== false){
44066                 
44067                 this.setFlagClass(record.data.iso2);
44068                 this.setDialCode(record.data.dialCode);
44069                 this.hasFocus = false;
44070                 this.collapse();
44071                 this.fireEvent('select', this, record, index);
44072             }
44073         },
44074         
44075         flagEl : function()
44076         {
44077             var flag = this.el.select('div.flag',true).first();
44078             if(!flag){
44079                 return false;
44080             }
44081             return flag;
44082         },
44083         
44084         dialCodeHolderEl : function()
44085         {
44086             var d = this.el.select('input.dial-code-holder',true).first();
44087             if(!d){
44088                 return false;
44089             }
44090             return d;
44091         },
44092         
44093         setDialCode : function(v)
44094         {
44095             this.dialCodeHolder.dom.value = '+'+v;
44096         },
44097         
44098         setFlagClass : function(n)
44099         {
44100             this.flag.dom.className = 'flag '+n;
44101         },
44102         
44103         getValue : function()
44104         {
44105             var v = this.inputEl().getValue();
44106             if(this.dialCodeHolder) {
44107                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44108             }
44109             return v;
44110         },
44111         
44112         setValue : function(v)
44113         {
44114             var d = this.getDialCode(v);
44115             
44116             //invalid dial code
44117             if(v.length == 0 || !d || d.length == 0) {
44118                 if(this.rendered){
44119                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44120                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44121                 }
44122                 return;
44123             }
44124             
44125             //valid dial code
44126             this.setFlagClass(this.dialCodeMapping[d].iso2);
44127             this.setDialCode(d);
44128             this.inputEl().dom.value = v.replace('+'+d,'');
44129             this.hiddenEl().dom.value = this.getValue();
44130             
44131             this.validate();
44132         },
44133         
44134         getDialCode : function(v)
44135         {
44136             v = v ||  '';
44137             
44138             if (v.length == 0) {
44139                 return this.dialCodeHolder.dom.value;
44140             }
44141             
44142             var dialCode = "";
44143             if (v.charAt(0) != "+") {
44144                 return false;
44145             }
44146             var numericChars = "";
44147             for (var i = 1; i < v.length; i++) {
44148               var c = v.charAt(i);
44149               if (!isNaN(c)) {
44150                 numericChars += c;
44151                 if (this.dialCodeMapping[numericChars]) {
44152                   dialCode = v.substr(1, i);
44153                 }
44154                 if (numericChars.length == 4) {
44155                   break;
44156                 }
44157               }
44158             }
44159             return dialCode;
44160         },
44161         
44162         reset : function()
44163         {
44164             this.setValue(this.defaultDialCode);
44165             this.validate();
44166         },
44167         
44168         hiddenEl : function()
44169         {
44170             return this.el.select('input.hidden-tel-input',true).first();
44171         },
44172         
44173         // after setting val
44174         onKeyUp : function(e){
44175             this.setValue(this.getValue());
44176         },
44177         
44178         onKeyPress : function(e){
44179             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44180                 e.stopEvent();
44181             }
44182         }
44183         
44184 });
44185 /**
44186  * @class Roo.bootstrap.MoneyField
44187  * @extends Roo.bootstrap.ComboBox
44188  * Bootstrap MoneyField class
44189  * 
44190  * @constructor
44191  * Create a new MoneyField.
44192  * @param {Object} config Configuration options
44193  */
44194
44195 Roo.bootstrap.MoneyField = function(config) {
44196     
44197     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44198     
44199 };
44200
44201 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44202     
44203     /**
44204      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44205      */
44206     allowDecimals : true,
44207     /**
44208      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44209      */
44210     decimalSeparator : ".",
44211     /**
44212      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44213      */
44214     decimalPrecision : 0,
44215     /**
44216      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44217      */
44218     allowNegative : true,
44219     /**
44220      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44221      */
44222     allowZero: true,
44223     /**
44224      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44225      */
44226     minValue : Number.NEGATIVE_INFINITY,
44227     /**
44228      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44229      */
44230     maxValue : Number.MAX_VALUE,
44231     /**
44232      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44233      */
44234     minText : "The minimum value for this field is {0}",
44235     /**
44236      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44237      */
44238     maxText : "The maximum value for this field is {0}",
44239     /**
44240      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44241      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44242      */
44243     nanText : "{0} is not a valid number",
44244     /**
44245      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44246      */
44247     castInt : true,
44248     /**
44249      * @cfg {String} defaults currency of the MoneyField
44250      * value should be in lkey
44251      */
44252     defaultCurrency : false,
44253     /**
44254      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44255      */
44256     thousandsDelimiter : false,
44257     /**
44258      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44259      */
44260     max_length: false,
44261     
44262     inputlg : 9,
44263     inputmd : 9,
44264     inputsm : 9,
44265     inputxs : 6,
44266     
44267     store : false,
44268     
44269     getAutoCreate : function()
44270     {
44271         var align = this.labelAlign || this.parentLabelAlign();
44272         
44273         var id = Roo.id();
44274
44275         var cfg = {
44276             cls: 'form-group',
44277             cn: []
44278         };
44279
44280         var input =  {
44281             tag: 'input',
44282             id : id,
44283             cls : 'form-control roo-money-amount-input',
44284             autocomplete: 'new-password'
44285         };
44286         
44287         var hiddenInput = {
44288             tag: 'input',
44289             type: 'hidden',
44290             id: Roo.id(),
44291             cls: 'hidden-number-input'
44292         };
44293         
44294         if(this.max_length) {
44295             input.maxlength = this.max_length; 
44296         }
44297         
44298         if (this.name) {
44299             hiddenInput.name = this.name;
44300         }
44301
44302         if (this.disabled) {
44303             input.disabled = true;
44304         }
44305
44306         var clg = 12 - this.inputlg;
44307         var cmd = 12 - this.inputmd;
44308         var csm = 12 - this.inputsm;
44309         var cxs = 12 - this.inputxs;
44310         
44311         var container = {
44312             tag : 'div',
44313             cls : 'row roo-money-field',
44314             cn : [
44315                 {
44316                     tag : 'div',
44317                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44318                     cn : [
44319                         {
44320                             tag : 'div',
44321                             cls: 'roo-select2-container input-group',
44322                             cn: [
44323                                 {
44324                                     tag : 'input',
44325                                     cls : 'form-control roo-money-currency-input',
44326                                     autocomplete: 'new-password',
44327                                     readOnly : 1,
44328                                     name : this.currencyName
44329                                 },
44330                                 {
44331                                     tag :'span',
44332                                     cls : 'input-group-addon',
44333                                     cn : [
44334                                         {
44335                                             tag: 'span',
44336                                             cls: 'caret'
44337                                         }
44338                                     ]
44339                                 }
44340                             ]
44341                         }
44342                     ]
44343                 },
44344                 {
44345                     tag : 'div',
44346                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44347                     cn : [
44348                         {
44349                             tag: 'div',
44350                             cls: this.hasFeedback ? 'has-feedback' : '',
44351                             cn: [
44352                                 input
44353                             ]
44354                         }
44355                     ]
44356                 }
44357             ]
44358             
44359         };
44360         
44361         if (this.fieldLabel.length) {
44362             var indicator = {
44363                 tag: 'i',
44364                 tooltip: 'This field is required'
44365             };
44366
44367             var label = {
44368                 tag: 'label',
44369                 'for':  id,
44370                 cls: 'control-label',
44371                 cn: []
44372             };
44373
44374             var label_text = {
44375                 tag: 'span',
44376                 html: this.fieldLabel
44377             };
44378
44379             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44380             label.cn = [
44381                 indicator,
44382                 label_text
44383             ];
44384
44385             if(this.indicatorpos == 'right') {
44386                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44387                 label.cn = [
44388                     label_text,
44389                     indicator
44390                 ];
44391             }
44392
44393             if(align == 'left') {
44394                 container = {
44395                     tag: 'div',
44396                     cn: [
44397                         container
44398                     ]
44399                 };
44400
44401                 if(this.labelWidth > 12){
44402                     label.style = "width: " + this.labelWidth + 'px';
44403                 }
44404                 if(this.labelWidth < 13 && this.labelmd == 0){
44405                     this.labelmd = this.labelWidth;
44406                 }
44407                 if(this.labellg > 0){
44408                     label.cls += ' col-lg-' + this.labellg;
44409                     input.cls += ' col-lg-' + (12 - this.labellg);
44410                 }
44411                 if(this.labelmd > 0){
44412                     label.cls += ' col-md-' + this.labelmd;
44413                     container.cls += ' col-md-' + (12 - this.labelmd);
44414                 }
44415                 if(this.labelsm > 0){
44416                     label.cls += ' col-sm-' + this.labelsm;
44417                     container.cls += ' col-sm-' + (12 - this.labelsm);
44418                 }
44419                 if(this.labelxs > 0){
44420                     label.cls += ' col-xs-' + this.labelxs;
44421                     container.cls += ' col-xs-' + (12 - this.labelxs);
44422                 }
44423             }
44424         }
44425
44426         cfg.cn = [
44427             label,
44428             container,
44429             hiddenInput
44430         ];
44431         
44432         var settings = this;
44433
44434         ['xs','sm','md','lg'].map(function(size){
44435             if (settings[size]) {
44436                 cfg.cls += ' col-' + size + '-' + settings[size];
44437             }
44438         });
44439         
44440         return cfg;
44441     },
44442     
44443     initEvents : function()
44444     {
44445         this.indicator = this.indicatorEl();
44446         
44447         this.initCurrencyEvent();
44448         
44449         this.initNumberEvent();
44450     },
44451     
44452     initCurrencyEvent : function()
44453     {
44454         if (!this.store) {
44455             throw "can not find store for combo";
44456         }
44457         
44458         this.store = Roo.factory(this.store, Roo.data);
44459         this.store.parent = this;
44460         
44461         this.createList();
44462         
44463         this.triggerEl = this.el.select('.input-group-addon', true).first();
44464         
44465         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44466         
44467         var _this = this;
44468         
44469         (function(){
44470             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44471             _this.list.setWidth(lw);
44472         }).defer(100);
44473         
44474         this.list.on('mouseover', this.onViewOver, this);
44475         this.list.on('mousemove', this.onViewMove, this);
44476         this.list.on('scroll', this.onViewScroll, this);
44477         
44478         if(!this.tpl){
44479             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44480         }
44481         
44482         this.view = new Roo.View(this.list, this.tpl, {
44483             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44484         });
44485         
44486         this.view.on('click', this.onViewClick, this);
44487         
44488         this.store.on('beforeload', this.onBeforeLoad, this);
44489         this.store.on('load', this.onLoad, this);
44490         this.store.on('loadexception', this.onLoadException, this);
44491         
44492         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44493             "up" : function(e){
44494                 this.inKeyMode = true;
44495                 this.selectPrev();
44496             },
44497
44498             "down" : function(e){
44499                 if(!this.isExpanded()){
44500                     this.onTriggerClick();
44501                 }else{
44502                     this.inKeyMode = true;
44503                     this.selectNext();
44504                 }
44505             },
44506
44507             "enter" : function(e){
44508                 this.collapse();
44509                 
44510                 if(this.fireEvent("specialkey", this, e)){
44511                     this.onViewClick(false);
44512                 }
44513                 
44514                 return true;
44515             },
44516
44517             "esc" : function(e){
44518                 this.collapse();
44519             },
44520
44521             "tab" : function(e){
44522                 this.collapse();
44523                 
44524                 if(this.fireEvent("specialkey", this, e)){
44525                     this.onViewClick(false);
44526                 }
44527                 
44528                 return true;
44529             },
44530
44531             scope : this,
44532
44533             doRelay : function(foo, bar, hname){
44534                 if(hname == 'down' || this.scope.isExpanded()){
44535                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44536                 }
44537                 return true;
44538             },
44539
44540             forceKeyDown: true
44541         });
44542         
44543         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44544         
44545     },
44546     
44547     initNumberEvent : function(e)
44548     {
44549         this.inputEl().on("keydown" , this.fireKey,  this);
44550         this.inputEl().on("focus", this.onFocus,  this);
44551         this.inputEl().on("blur", this.onBlur,  this);
44552         
44553         this.inputEl().relayEvent('keyup', this);
44554         
44555         if(this.indicator){
44556             this.indicator.addClass('invisible');
44557         }
44558  
44559         this.originalValue = this.getValue();
44560         
44561         if(this.validationEvent == 'keyup'){
44562             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44563             this.inputEl().on('keyup', this.filterValidation, this);
44564         }
44565         else if(this.validationEvent !== false){
44566             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44567         }
44568         
44569         if(this.selectOnFocus){
44570             this.on("focus", this.preFocus, this);
44571             
44572         }
44573         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44574             this.inputEl().on("keypress", this.filterKeys, this);
44575         } else {
44576             this.inputEl().relayEvent('keypress', this);
44577         }
44578         
44579         var allowed = "0123456789";
44580         
44581         if(this.allowDecimals){
44582             allowed += this.decimalSeparator;
44583         }
44584         
44585         if(this.allowNegative){
44586             allowed += "-";
44587         }
44588         
44589         if(this.thousandsDelimiter) {
44590             allowed += ",";
44591         }
44592         
44593         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44594         
44595         var keyPress = function(e){
44596             
44597             var k = e.getKey();
44598             
44599             var c = e.getCharCode();
44600             
44601             if(
44602                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44603                     allowed.indexOf(String.fromCharCode(c)) === -1
44604             ){
44605                 e.stopEvent();
44606                 return;
44607             }
44608             
44609             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44610                 return;
44611             }
44612             
44613             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44614                 e.stopEvent();
44615             }
44616         };
44617         
44618         this.inputEl().on("keypress", keyPress, this);
44619         
44620     },
44621     
44622     onTriggerClick : function(e)
44623     {   
44624         if(this.disabled){
44625             return;
44626         }
44627         
44628         this.page = 0;
44629         this.loadNext = false;
44630         
44631         if(this.isExpanded()){
44632             this.collapse();
44633             return;
44634         }
44635         
44636         this.hasFocus = true;
44637         
44638         if(this.triggerAction == 'all') {
44639             this.doQuery(this.allQuery, true);
44640             return;
44641         }
44642         
44643         this.doQuery(this.getRawValue());
44644     },
44645     
44646     getCurrency : function()
44647     {   
44648         var v = this.currencyEl().getValue();
44649         
44650         return v;
44651     },
44652     
44653     restrictHeight : function()
44654     {
44655         this.list.alignTo(this.currencyEl(), this.listAlign);
44656         this.list.alignTo(this.currencyEl(), this.listAlign);
44657     },
44658     
44659     onViewClick : function(view, doFocus, el, e)
44660     {
44661         var index = this.view.getSelectedIndexes()[0];
44662         
44663         var r = this.store.getAt(index);
44664         
44665         if(r){
44666             this.onSelect(r, index);
44667         }
44668     },
44669     
44670     onSelect : function(record, index){
44671         
44672         if(this.fireEvent('beforeselect', this, record, index) !== false){
44673         
44674             this.setFromCurrencyData(index > -1 ? record.data : false);
44675             
44676             this.collapse();
44677             
44678             this.fireEvent('select', this, record, index);
44679         }
44680     },
44681     
44682     setFromCurrencyData : function(o)
44683     {
44684         var currency = '';
44685         
44686         this.lastCurrency = o;
44687         
44688         if (this.currencyField) {
44689             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44690         } else {
44691             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44692         }
44693         
44694         this.lastSelectionText = currency;
44695         
44696         //setting default currency
44697         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44698             this.setCurrency(this.defaultCurrency);
44699             return;
44700         }
44701         
44702         this.setCurrency(currency);
44703     },
44704     
44705     setFromData : function(o)
44706     {
44707         var c = {};
44708         
44709         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44710         
44711         this.setFromCurrencyData(c);
44712         
44713         var value = '';
44714         
44715         if (this.name) {
44716             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44717         } else {
44718             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44719         }
44720         
44721         this.setValue(value);
44722         
44723     },
44724     
44725     setCurrency : function(v)
44726     {   
44727         this.currencyValue = v;
44728         
44729         if(this.rendered){
44730             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44731             this.validate();
44732         }
44733     },
44734     
44735     setValue : function(v)
44736     {
44737         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44738         
44739         this.value = v;
44740         
44741         if(this.rendered){
44742             
44743             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44744             
44745             this.inputEl().dom.value = (v == '') ? '' :
44746                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44747             
44748             if(!this.allowZero && v === '0') {
44749                 this.hiddenEl().dom.value = '';
44750                 this.inputEl().dom.value = '';
44751             }
44752             
44753             this.validate();
44754         }
44755     },
44756     
44757     getRawValue : function()
44758     {
44759         var v = this.inputEl().getValue();
44760         
44761         return v;
44762     },
44763     
44764     getValue : function()
44765     {
44766         return this.fixPrecision(this.parseValue(this.getRawValue()));
44767     },
44768     
44769     parseValue : function(value)
44770     {
44771         if(this.thousandsDelimiter) {
44772             value += "";
44773             r = new RegExp(",", "g");
44774             value = value.replace(r, "");
44775         }
44776         
44777         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44778         return isNaN(value) ? '' : value;
44779         
44780     },
44781     
44782     fixPrecision : function(value)
44783     {
44784         if(this.thousandsDelimiter) {
44785             value += "";
44786             r = new RegExp(",", "g");
44787             value = value.replace(r, "");
44788         }
44789         
44790         var nan = isNaN(value);
44791         
44792         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44793             return nan ? '' : value;
44794         }
44795         return parseFloat(value).toFixed(this.decimalPrecision);
44796     },
44797     
44798     decimalPrecisionFcn : function(v)
44799     {
44800         return Math.floor(v);
44801     },
44802     
44803     validateValue : function(value)
44804     {
44805         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44806             return false;
44807         }
44808         
44809         var num = this.parseValue(value);
44810         
44811         if(isNaN(num)){
44812             this.markInvalid(String.format(this.nanText, value));
44813             return false;
44814         }
44815         
44816         if(num < this.minValue){
44817             this.markInvalid(String.format(this.minText, this.minValue));
44818             return false;
44819         }
44820         
44821         if(num > this.maxValue){
44822             this.markInvalid(String.format(this.maxText, this.maxValue));
44823             return false;
44824         }
44825         
44826         return true;
44827     },
44828     
44829     validate : function()
44830     {
44831         if(this.disabled || this.allowBlank){
44832             this.markValid();
44833             return true;
44834         }
44835         
44836         var currency = this.getCurrency();
44837         
44838         if(this.validateValue(this.getRawValue()) && currency.length){
44839             this.markValid();
44840             return true;
44841         }
44842         
44843         this.markInvalid();
44844         return false;
44845     },
44846     
44847     getName: function()
44848     {
44849         return this.name;
44850     },
44851     
44852     beforeBlur : function()
44853     {
44854         if(!this.castInt){
44855             return;
44856         }
44857         
44858         var v = this.parseValue(this.getRawValue());
44859         
44860         if(v || v == 0){
44861             this.setValue(v);
44862         }
44863     },
44864     
44865     onBlur : function()
44866     {
44867         this.beforeBlur();
44868         
44869         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44870             //this.el.removeClass(this.focusClass);
44871         }
44872         
44873         this.hasFocus = false;
44874         
44875         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44876             this.validate();
44877         }
44878         
44879         var v = this.getValue();
44880         
44881         if(String(v) !== String(this.startValue)){
44882             this.fireEvent('change', this, v, this.startValue);
44883         }
44884         
44885         this.fireEvent("blur", this);
44886     },
44887     
44888     inputEl : function()
44889     {
44890         return this.el.select('.roo-money-amount-input', true).first();
44891     },
44892     
44893     currencyEl : function()
44894     {
44895         return this.el.select('.roo-money-currency-input', true).first();
44896     },
44897     
44898     hiddenEl : function()
44899     {
44900         return this.el.select('input.hidden-number-input',true).first();
44901     }
44902     
44903 });/**
44904  * @class Roo.bootstrap.BezierSignature
44905  * @extends Roo.bootstrap.Component
44906  * Bootstrap BezierSignature class
44907  * This script refer to:
44908  *    Title: Signature Pad
44909  *    Author: szimek
44910  *    Availability: https://github.com/szimek/signature_pad
44911  *
44912  * @constructor
44913  * Create a new BezierSignature
44914  * @param {Object} config The config object
44915  */
44916
44917 Roo.bootstrap.BezierSignature = function(config){
44918     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44919     this.addEvents({
44920         "resize" : true
44921     });
44922 };
44923
44924 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44925 {
44926      
44927     curve_data: [],
44928     
44929     is_empty: true,
44930     
44931     mouse_btn_down: true,
44932     
44933     /**
44934      * @cfg {int} canvas height
44935      */
44936     canvas_height: '200px',
44937     
44938     /**
44939      * @cfg {float|function} Radius of a single dot.
44940      */ 
44941     dot_size: false,
44942     
44943     /**
44944      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44945      */
44946     min_width: 0.5,
44947     
44948     /**
44949      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44950      */
44951     max_width: 2.5,
44952     
44953     /**
44954      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44955      */
44956     throttle: 16,
44957     
44958     /**
44959      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44960      */
44961     min_distance: 5,
44962     
44963     /**
44964      * @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.
44965      */
44966     bg_color: 'rgba(0, 0, 0, 0)',
44967     
44968     /**
44969      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44970      */
44971     dot_color: 'black',
44972     
44973     /**
44974      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44975      */ 
44976     velocity_filter_weight: 0.7,
44977     
44978     /**
44979      * @cfg {function} Callback when stroke begin. 
44980      */
44981     onBegin: false,
44982     
44983     /**
44984      * @cfg {function} Callback when stroke end.
44985      */
44986     onEnd: false,
44987     
44988     getAutoCreate : function()
44989     {
44990         var cls = 'roo-signature column';
44991         
44992         if(this.cls){
44993             cls += ' ' + this.cls;
44994         }
44995         
44996         var col_sizes = [
44997             'lg',
44998             'md',
44999             'sm',
45000             'xs'
45001         ];
45002         
45003         for(var i = 0; i < col_sizes.length; i++) {
45004             if(this[col_sizes[i]]) {
45005                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45006             }
45007         }
45008         
45009         var cfg = {
45010             tag: 'div',
45011             cls: cls,
45012             cn: [
45013                 {
45014                     tag: 'div',
45015                     cls: 'roo-signature-body',
45016                     cn: [
45017                         {
45018                             tag: 'canvas',
45019                             cls: 'roo-signature-body-canvas',
45020                             height: this.canvas_height,
45021                             width: this.canvas_width
45022                         }
45023                     ]
45024                 },
45025                 {
45026                     tag: 'input',
45027                     type: 'file',
45028                     style: 'display: none'
45029                 }
45030             ]
45031         };
45032         
45033         return cfg;
45034     },
45035     
45036     initEvents: function() 
45037     {
45038         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45039         
45040         var canvas = this.canvasEl();
45041         
45042         // mouse && touch event swapping...
45043         canvas.dom.style.touchAction = 'none';
45044         canvas.dom.style.msTouchAction = 'none';
45045         
45046         this.mouse_btn_down = false;
45047         canvas.on('mousedown', this._handleMouseDown, this);
45048         canvas.on('mousemove', this._handleMouseMove, this);
45049         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45050         
45051         if (window.PointerEvent) {
45052             canvas.on('pointerdown', this._handleMouseDown, this);
45053             canvas.on('pointermove', this._handleMouseMove, this);
45054             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45055         }
45056         
45057         if ('ontouchstart' in window) {
45058             canvas.on('touchstart', this._handleTouchStart, this);
45059             canvas.on('touchmove', this._handleTouchMove, this);
45060             canvas.on('touchend', this._handleTouchEnd, this);
45061         }
45062         
45063         Roo.EventManager.onWindowResize(this.resize, this, true);
45064         
45065         // file input event
45066         this.fileEl().on('change', this.uploadImage, this);
45067         
45068         this.clear();
45069         
45070         this.resize();
45071     },
45072     
45073     resize: function(){
45074         
45075         var canvas = this.canvasEl().dom;
45076         var ctx = this.canvasElCtx();
45077         var img_data = false;
45078         
45079         if(canvas.width > 0) {
45080             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45081         }
45082         // setting canvas width will clean img data
45083         canvas.width = 0;
45084         
45085         var style = window.getComputedStyle ? 
45086             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45087             
45088         var padding_left = parseInt(style.paddingLeft) || 0;
45089         var padding_right = parseInt(style.paddingRight) || 0;
45090         
45091         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45092         
45093         if(img_data) {
45094             ctx.putImageData(img_data, 0, 0);
45095         }
45096     },
45097     
45098     _handleMouseDown: function(e)
45099     {
45100         if (e.browserEvent.which === 1) {
45101             this.mouse_btn_down = true;
45102             this.strokeBegin(e);
45103         }
45104     },
45105     
45106     _handleMouseMove: function (e)
45107     {
45108         if (this.mouse_btn_down) {
45109             this.strokeMoveUpdate(e);
45110         }
45111     },
45112     
45113     _handleMouseUp: function (e)
45114     {
45115         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45116             this.mouse_btn_down = false;
45117             this.strokeEnd(e);
45118         }
45119     },
45120     
45121     _handleTouchStart: function (e) {
45122         
45123         e.preventDefault();
45124         if (e.browserEvent.targetTouches.length === 1) {
45125             // var touch = e.browserEvent.changedTouches[0];
45126             // this.strokeBegin(touch);
45127             
45128              this.strokeBegin(e); // assume e catching the correct xy...
45129         }
45130     },
45131     
45132     _handleTouchMove: function (e) {
45133         e.preventDefault();
45134         // var touch = event.targetTouches[0];
45135         // _this._strokeMoveUpdate(touch);
45136         this.strokeMoveUpdate(e);
45137     },
45138     
45139     _handleTouchEnd: function (e) {
45140         var wasCanvasTouched = e.target === this.canvasEl().dom;
45141         if (wasCanvasTouched) {
45142             e.preventDefault();
45143             // var touch = event.changedTouches[0];
45144             // _this._strokeEnd(touch);
45145             this.strokeEnd(e);
45146         }
45147     },
45148     
45149     reset: function () {
45150         this._lastPoints = [];
45151         this._lastVelocity = 0;
45152         this._lastWidth = (this.min_width + this.max_width) / 2;
45153         this.canvasElCtx().fillStyle = this.dot_color;
45154     },
45155     
45156     strokeMoveUpdate: function(e)
45157     {
45158         this.strokeUpdate(e);
45159         
45160         if (this.throttle) {
45161             this.throttleStroke(this.strokeUpdate, this.throttle);
45162         }
45163         else {
45164             this.strokeUpdate(e);
45165         }
45166     },
45167     
45168     strokeBegin: function(e)
45169     {
45170         var newPointGroup = {
45171             color: this.dot_color,
45172             points: []
45173         };
45174         
45175         if (typeof this.onBegin === 'function') {
45176             this.onBegin(e);
45177         }
45178         
45179         this.curve_data.push(newPointGroup);
45180         this.reset();
45181         this.strokeUpdate(e);
45182     },
45183     
45184     strokeUpdate: function(e)
45185     {
45186         var rect = this.canvasEl().dom.getBoundingClientRect();
45187         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45188         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45189         var lastPoints = lastPointGroup.points;
45190         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45191         var isLastPointTooClose = lastPoint
45192             ? point.distanceTo(lastPoint) <= this.min_distance
45193             : false;
45194         var color = lastPointGroup.color;
45195         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45196             var curve = this.addPoint(point);
45197             if (!lastPoint) {
45198                 this.drawDot({color: color, point: point});
45199             }
45200             else if (curve) {
45201                 this.drawCurve({color: color, curve: curve});
45202             }
45203             lastPoints.push({
45204                 time: point.time,
45205                 x: point.x,
45206                 y: point.y
45207             });
45208         }
45209     },
45210     
45211     strokeEnd: function(e)
45212     {
45213         this.strokeUpdate(e);
45214         if (typeof this.onEnd === 'function') {
45215             this.onEnd(e);
45216         }
45217     },
45218     
45219     addPoint:  function (point) {
45220         var _lastPoints = this._lastPoints;
45221         _lastPoints.push(point);
45222         if (_lastPoints.length > 2) {
45223             if (_lastPoints.length === 3) {
45224                 _lastPoints.unshift(_lastPoints[0]);
45225             }
45226             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45227             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45228             _lastPoints.shift();
45229             return curve;
45230         }
45231         return null;
45232     },
45233     
45234     calculateCurveWidths: function (startPoint, endPoint) {
45235         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45236             (1 - this.velocity_filter_weight) * this._lastVelocity;
45237
45238         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45239         var widths = {
45240             end: newWidth,
45241             start: this._lastWidth
45242         };
45243         
45244         this._lastVelocity = velocity;
45245         this._lastWidth = newWidth;
45246         return widths;
45247     },
45248     
45249     drawDot: function (_a) {
45250         var color = _a.color, point = _a.point;
45251         var ctx = this.canvasElCtx();
45252         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45253         ctx.beginPath();
45254         this.drawCurveSegment(point.x, point.y, width);
45255         ctx.closePath();
45256         ctx.fillStyle = color;
45257         ctx.fill();
45258     },
45259     
45260     drawCurve: function (_a) {
45261         var color = _a.color, curve = _a.curve;
45262         var ctx = this.canvasElCtx();
45263         var widthDelta = curve.endWidth - curve.startWidth;
45264         var drawSteps = Math.floor(curve.length()) * 2;
45265         ctx.beginPath();
45266         ctx.fillStyle = color;
45267         for (var i = 0; i < drawSteps; i += 1) {
45268         var t = i / drawSteps;
45269         var tt = t * t;
45270         var ttt = tt * t;
45271         var u = 1 - t;
45272         var uu = u * u;
45273         var uuu = uu * u;
45274         var x = uuu * curve.startPoint.x;
45275         x += 3 * uu * t * curve.control1.x;
45276         x += 3 * u * tt * curve.control2.x;
45277         x += ttt * curve.endPoint.x;
45278         var y = uuu * curve.startPoint.y;
45279         y += 3 * uu * t * curve.control1.y;
45280         y += 3 * u * tt * curve.control2.y;
45281         y += ttt * curve.endPoint.y;
45282         var width = curve.startWidth + ttt * widthDelta;
45283         this.drawCurveSegment(x, y, width);
45284         }
45285         ctx.closePath();
45286         ctx.fill();
45287     },
45288     
45289     drawCurveSegment: function (x, y, width) {
45290         var ctx = this.canvasElCtx();
45291         ctx.moveTo(x, y);
45292         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45293         this.is_empty = false;
45294     },
45295     
45296     clear: function()
45297     {
45298         var ctx = this.canvasElCtx();
45299         var canvas = this.canvasEl().dom;
45300         ctx.fillStyle = this.bg_color;
45301         ctx.clearRect(0, 0, canvas.width, canvas.height);
45302         ctx.fillRect(0, 0, canvas.width, canvas.height);
45303         this.curve_data = [];
45304         this.reset();
45305         this.is_empty = true;
45306     },
45307     
45308     fileEl: function()
45309     {
45310         return  this.el.select('input',true).first();
45311     },
45312     
45313     canvasEl: function()
45314     {
45315         return this.el.select('canvas',true).first();
45316     },
45317     
45318     canvasElCtx: function()
45319     {
45320         return this.el.select('canvas',true).first().dom.getContext('2d');
45321     },
45322     
45323     getImage: function(type)
45324     {
45325         if(this.is_empty) {
45326             return false;
45327         }
45328         
45329         // encryption ?
45330         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45331     },
45332     
45333     drawFromImage: function(img_src)
45334     {
45335         var img = new Image();
45336         
45337         img.onload = function(){
45338             this.canvasElCtx().drawImage(img, 0, 0);
45339         }.bind(this);
45340         
45341         img.src = img_src;
45342         
45343         this.is_empty = false;
45344     },
45345     
45346     selectImage: function()
45347     {
45348         this.fileEl().dom.click();
45349     },
45350     
45351     uploadImage: function(e)
45352     {
45353         var reader = new FileReader();
45354         
45355         reader.onload = function(e){
45356             var img = new Image();
45357             img.onload = function(){
45358                 this.reset();
45359                 this.canvasElCtx().drawImage(img, 0, 0);
45360             }.bind(this);
45361             img.src = e.target.result;
45362         }.bind(this);
45363         
45364         reader.readAsDataURL(e.target.files[0]);
45365     },
45366     
45367     // Bezier Point Constructor
45368     Point: (function () {
45369         function Point(x, y, time) {
45370             this.x = x;
45371             this.y = y;
45372             this.time = time || Date.now();
45373         }
45374         Point.prototype.distanceTo = function (start) {
45375             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45376         };
45377         Point.prototype.equals = function (other) {
45378             return this.x === other.x && this.y === other.y && this.time === other.time;
45379         };
45380         Point.prototype.velocityFrom = function (start) {
45381             return this.time !== start.time
45382             ? this.distanceTo(start) / (this.time - start.time)
45383             : 0;
45384         };
45385         return Point;
45386     }()),
45387     
45388     
45389     // Bezier Constructor
45390     Bezier: (function () {
45391         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45392             this.startPoint = startPoint;
45393             this.control2 = control2;
45394             this.control1 = control1;
45395             this.endPoint = endPoint;
45396             this.startWidth = startWidth;
45397             this.endWidth = endWidth;
45398         }
45399         Bezier.fromPoints = function (points, widths, scope) {
45400             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45401             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45402             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45403         };
45404         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45405             var dx1 = s1.x - s2.x;
45406             var dy1 = s1.y - s2.y;
45407             var dx2 = s2.x - s3.x;
45408             var dy2 = s2.y - s3.y;
45409             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45410             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45411             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45412             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45413             var dxm = m1.x - m2.x;
45414             var dym = m1.y - m2.y;
45415             var k = l2 / (l1 + l2);
45416             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45417             var tx = s2.x - cm.x;
45418             var ty = s2.y - cm.y;
45419             return {
45420                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45421                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45422             };
45423         };
45424         Bezier.prototype.length = function () {
45425             var steps = 10;
45426             var length = 0;
45427             var px;
45428             var py;
45429             for (var i = 0; i <= steps; i += 1) {
45430                 var t = i / steps;
45431                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45432                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45433                 if (i > 0) {
45434                     var xdiff = cx - px;
45435                     var ydiff = cy - py;
45436                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45437                 }
45438                 px = cx;
45439                 py = cy;
45440             }
45441             return length;
45442         };
45443         Bezier.prototype.point = function (t, start, c1, c2, end) {
45444             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45445             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45446             + (3.0 * c2 * (1.0 - t) * t * t)
45447             + (end * t * t * t);
45448         };
45449         return Bezier;
45450     }()),
45451     
45452     throttleStroke: function(fn, wait) {
45453       if (wait === void 0) { wait = 250; }
45454       var previous = 0;
45455       var timeout = null;
45456       var result;
45457       var storedContext;
45458       var storedArgs;
45459       var later = function () {
45460           previous = Date.now();
45461           timeout = null;
45462           result = fn.apply(storedContext, storedArgs);
45463           if (!timeout) {
45464               storedContext = null;
45465               storedArgs = [];
45466           }
45467       };
45468       return function wrapper() {
45469           var args = [];
45470           for (var _i = 0; _i < arguments.length; _i++) {
45471               args[_i] = arguments[_i];
45472           }
45473           var now = Date.now();
45474           var remaining = wait - (now - previous);
45475           storedContext = this;
45476           storedArgs = args;
45477           if (remaining <= 0 || remaining > wait) {
45478               if (timeout) {
45479                   clearTimeout(timeout);
45480                   timeout = null;
45481               }
45482               previous = now;
45483               result = fn.apply(storedContext, storedArgs);
45484               if (!timeout) {
45485                   storedContext = null;
45486                   storedArgs = [];
45487               }
45488           }
45489           else if (!timeout) {
45490               timeout = window.setTimeout(later, remaining);
45491           }
45492           return result;
45493       };
45494   }
45495   
45496 });
45497
45498  
45499
45500