7ae55d4024b05914c4127dcda4e66f2832426ced
[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  * @children Roo.bootstrap.Component
1601  * Bootstrap Container class
1602  * @cfg {Boolean} jumbotron is it a jumbotron element
1603  * @cfg {String} html content of element
1604  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1605  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1606  * @cfg {String} header content of header (for panel)
1607  * @cfg {String} footer content of footer (for panel)
1608  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1609  * @cfg {String} tag (header|aside|section) type of HTML tag.
1610  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1611  * @cfg {String} fa font awesome icon
1612  * @cfg {String} icon (info-sign|check|...) glyphicon name
1613  * @cfg {Boolean} hidden (true|false) hide the element
1614  * @cfg {Boolean} expandable (true|false) default false
1615  * @cfg {Boolean} expanded (true|false) default true
1616  * @cfg {String} rheader contet on the right of header
1617  * @cfg {Boolean} clickable (true|false) default false
1618
1619  *     
1620  * @constructor
1621  * Create a new Container
1622  * @param {Object} config The config object
1623  */
1624
1625 Roo.bootstrap.Container = function(config){
1626     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1627     
1628     this.addEvents({
1629         // raw events
1630          /**
1631          * @event expand
1632          * After the panel has been expand
1633          * 
1634          * @param {Roo.bootstrap.Container} this
1635          */
1636         "expand" : true,
1637         /**
1638          * @event collapse
1639          * After the panel has been collapsed
1640          * 
1641          * @param {Roo.bootstrap.Container} this
1642          */
1643         "collapse" : true,
1644         /**
1645          * @event click
1646          * When a element is chick
1647          * @param {Roo.bootstrap.Container} this
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1655     
1656     jumbotron : false,
1657     well: '',
1658     panel : '',
1659     header: '',
1660     footer : '',
1661     sticky: '',
1662     tag : false,
1663     alert : false,
1664     fa: false,
1665     icon : false,
1666     expandable : false,
1667     rheader : '',
1668     expanded : true,
1669     clickable: false,
1670   
1671      
1672     getChildContainer : function() {
1673         
1674         if(!this.el){
1675             return false;
1676         }
1677         
1678         if (this.panel.length) {
1679             return this.el.select('.panel-body',true).first();
1680         }
1681         
1682         return this.el;
1683     },
1684     
1685     
1686     getAutoCreate : function(){
1687         
1688         var cfg = {
1689             tag : this.tag || 'div',
1690             html : '',
1691             cls : ''
1692         };
1693         if (this.jumbotron) {
1694             cfg.cls = 'jumbotron';
1695         }
1696         
1697         
1698         
1699         // - this is applied by the parent..
1700         //if (this.cls) {
1701         //    cfg.cls = this.cls + '';
1702         //}
1703         
1704         if (this.sticky.length) {
1705             
1706             var bd = Roo.get(document.body);
1707             if (!bd.hasClass('bootstrap-sticky')) {
1708                 bd.addClass('bootstrap-sticky');
1709                 Roo.select('html',true).setStyle('height', '100%');
1710             }
1711              
1712             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1713         }
1714         
1715         
1716         if (this.well.length) {
1717             switch (this.well) {
1718                 case 'lg':
1719                 case 'sm':
1720                     cfg.cls +=' well well-' +this.well;
1721                     break;
1722                 default:
1723                     cfg.cls +=' well';
1724                     break;
1725             }
1726         }
1727         
1728         if (this.hidden) {
1729             cfg.cls += ' hidden';
1730         }
1731         
1732         
1733         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1734             cfg.cls +=' alert alert-' + this.alert;
1735         }
1736         
1737         var body = cfg;
1738         
1739         if (this.panel.length) {
1740             cfg.cls += ' panel panel-' + this.panel;
1741             cfg.cn = [];
1742             if (this.header.length) {
1743                 
1744                 var h = [];
1745                 
1746                 if(this.expandable){
1747                     
1748                     cfg.cls = cfg.cls + ' expandable';
1749                     
1750                     h.push({
1751                         tag: 'i',
1752                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1753                     });
1754                     
1755                 }
1756                 
1757                 h.push(
1758                     {
1759                         tag: 'span',
1760                         cls : 'panel-title',
1761                         html : (this.expandable ? '&nbsp;' : '') + this.header
1762                     },
1763                     {
1764                         tag: 'span',
1765                         cls: 'panel-header-right',
1766                         html: this.rheader
1767                     }
1768                 );
1769                 
1770                 cfg.cn.push({
1771                     cls : 'panel-heading',
1772                     style : this.expandable ? 'cursor: pointer' : '',
1773                     cn : h
1774                 });
1775                 
1776             }
1777             
1778             body = false;
1779             cfg.cn.push({
1780                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1781                 html : this.html
1782             });
1783             
1784             
1785             if (this.footer.length) {
1786                 cfg.cn.push({
1787                     cls : 'panel-footer',
1788                     html : this.footer
1789                     
1790                 });
1791             }
1792             
1793         }
1794         
1795         if (body) {
1796             body.html = this.html || cfg.html;
1797             // prefix with the icons..
1798             if (this.fa) {
1799                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1800             }
1801             if (this.icon) {
1802                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1803             }
1804             
1805             
1806         }
1807         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1808             cfg.cls =  'container';
1809         }
1810         
1811         return cfg;
1812     },
1813     
1814     initEvents: function() 
1815     {
1816         if(this.expandable){
1817             var headerEl = this.headerEl();
1818         
1819             if(headerEl){
1820                 headerEl.on('click', this.onToggleClick, this);
1821             }
1822         }
1823         
1824         if(this.clickable){
1825             this.el.on('click', this.onClick, this);
1826         }
1827         
1828     },
1829     
1830     onToggleClick : function()
1831     {
1832         var headerEl = this.headerEl();
1833         
1834         if(!headerEl){
1835             return;
1836         }
1837         
1838         if(this.expanded){
1839             this.collapse();
1840             return;
1841         }
1842         
1843         this.expand();
1844     },
1845     
1846     expand : function()
1847     {
1848         if(this.fireEvent('expand', this)) {
1849             
1850             this.expanded = true;
1851             
1852             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1853             
1854             this.el.select('.panel-body',true).first().removeClass('hide');
1855             
1856             var toggleEl = this.toggleEl();
1857
1858             if(!toggleEl){
1859                 return;
1860             }
1861
1862             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1863         }
1864         
1865     },
1866     
1867     collapse : function()
1868     {
1869         if(this.fireEvent('collapse', this)) {
1870             
1871             this.expanded = false;
1872             
1873             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1874             this.el.select('.panel-body',true).first().addClass('hide');
1875         
1876             var toggleEl = this.toggleEl();
1877
1878             if(!toggleEl){
1879                 return;
1880             }
1881
1882             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1883         }
1884     },
1885     
1886     toggleEl : function()
1887     {
1888         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1889             return;
1890         }
1891         
1892         return this.el.select('.panel-heading .fa',true).first();
1893     },
1894     
1895     headerEl : function()
1896     {
1897         if(!this.el || !this.panel.length || !this.header.length){
1898             return;
1899         }
1900         
1901         return this.el.select('.panel-heading',true).first()
1902     },
1903     
1904     bodyEl : function()
1905     {
1906         if(!this.el || !this.panel.length){
1907             return;
1908         }
1909         
1910         return this.el.select('.panel-body',true).first()
1911     },
1912     
1913     titleEl : function()
1914     {
1915         if(!this.el || !this.panel.length || !this.header.length){
1916             return;
1917         }
1918         
1919         return this.el.select('.panel-title',true).first();
1920     },
1921     
1922     setTitle : function(v)
1923     {
1924         var titleEl = this.titleEl();
1925         
1926         if(!titleEl){
1927             return;
1928         }
1929         
1930         titleEl.dom.innerHTML = v;
1931     },
1932     
1933     getTitle : function()
1934     {
1935         
1936         var titleEl = this.titleEl();
1937         
1938         if(!titleEl){
1939             return '';
1940         }
1941         
1942         return titleEl.dom.innerHTML;
1943     },
1944     
1945     setRightTitle : function(v)
1946     {
1947         var t = this.el.select('.panel-header-right',true).first();
1948         
1949         if(!t){
1950             return;
1951         }
1952         
1953         t.dom.innerHTML = v;
1954     },
1955     
1956     onClick : function(e)
1957     {
1958         e.preventDefault();
1959         
1960         this.fireEvent('click', this, e);
1961     }
1962 });
1963
1964  /*
1965  *  - LGPL
1966  *
1967  *  This is BS4's Card element.. - similar to our containers probably..
1968  * 
1969  */
1970 /**
1971  * @class Roo.bootstrap.Card
1972  * @extends Roo.bootstrap.Component
1973  * Bootstrap Card class
1974  *
1975  *
1976  * possible... may not be implemented..
1977  * @cfg {String} header_image  src url of image.
1978  * @cfg {String|Object} header
1979  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1980  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1981  * 
1982  * @cfg {String} title
1983  * @cfg {String} subtitle
1984  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1985  * @cfg {String} footer
1986  
1987  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1988  * 
1989  * @cfg {String} margin (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1991  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1992  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1993  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1996  *
1997  * @cfg {String} padding (0|1|2|3|4|5)
1998  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1999  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2000  * @cfg {String} padding_left (0|1|2|3|4|5)
2001  * @cfg {String} padding_right (0|1|2|3|4|5)
2002  * @cfg {String} padding_x (0|1|2|3|4|5)
2003  * @cfg {String} padding_y (0|1|2|3|4|5)
2004  *
2005  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2008  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2009  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  
2011  * @config {Boolean} dragable  if this card can be dragged.
2012  * @config {String} drag_group  group for drag
2013  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2014  * @config {String} drop_group  group for drag
2015  * 
2016  * @config {Boolean} collapsable can the body be collapsed.
2017  * @config {Boolean} collapsed is the body collapsed when rendered...
2018  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2019  * @config {Boolean} rotated is the body rotated when rendered...
2020  * 
2021  * @constructor
2022  * Create a new Container
2023  * @param {Object} config The config object
2024  */
2025
2026 Roo.bootstrap.Card = function(config){
2027     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2028     
2029     this.addEvents({
2030          // raw events
2031         /**
2032          * @event drop
2033          * When a element a card is dropped
2034          * @param {Roo.bootstrap.Card} this
2035          *
2036          * 
2037          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2038          * @param {String} position 'above' or 'below'
2039          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2040         
2041          */
2042         'drop' : true,
2043          /**
2044          * @event rotate
2045          * When a element a card is rotate
2046          * @param {Roo.bootstrap.Card} this
2047          * @param {Roo.Element} n the node being dropped?
2048          * @param {Boolean} rotate status
2049          */
2050         'rotate' : true,
2051         /**
2052          * @event cardover
2053          * When a card element is dragged over ready to drop (return false to block dropable)
2054          * @param {Roo.bootstrap.Card} this
2055          * @param {Object} data from dragdrop 
2056          */
2057          'cardover' : true
2058          
2059     });
2060 };
2061
2062
2063 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2064     
2065     
2066     weight : '',
2067     
2068     margin: '', /// may be better in component?
2069     margin_top: '', 
2070     margin_bottom: '', 
2071     margin_left: '',
2072     margin_right: '',
2073     margin_x: '',
2074     margin_y: '',
2075     
2076     padding : '',
2077     padding_top: '', 
2078     padding_bottom: '', 
2079     padding_left: '',
2080     padding_right: '',
2081     padding_x: '',
2082     padding_y: '',
2083     
2084     display: '', 
2085     display_xs: '', 
2086     display_sm: '', 
2087     display_lg: '',
2088     display_xl: '',
2089  
2090     header_image  : '',
2091     header : '',
2092     header_size : 0,
2093     title : '',
2094     subtitle : '',
2095     html : '',
2096     footer: '',
2097
2098     collapsable : false,
2099     collapsed : false,
2100     rotateable : false,
2101     rotated : false,
2102     
2103     dragable : false,
2104     drag_group : false,
2105     dropable : false,
2106     drop_group : false,
2107     childContainer : false,
2108     dropEl : false, /// the dom placeholde element that indicates drop location.
2109     containerEl: false, // body container
2110     bodyEl: false, // card-body
2111     headerContainerEl : false, //
2112     headerEl : false,
2113     header_imageEl : false,
2114     
2115     
2116     layoutCls : function()
2117     {
2118         var cls = '';
2119         var t = this;
2120         Roo.log(this.margin_bottom.length);
2121         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2122             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2123             
2124             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2125                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2126             }
2127             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2129             }
2130         });
2131         
2132         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2133             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2135             }
2136         });
2137         
2138         // more generic support?
2139         if (this.hidden) {
2140             cls += ' d-none';
2141         }
2142         
2143         return cls;
2144     },
2145  
2146        // Roo.log("Call onRender: " + this.xtype);
2147         /*  We are looking at something like this.
2148 <div class="card">
2149     <img src="..." class="card-img-top" alt="...">
2150     <div class="card-body">
2151         <h5 class="card-title">Card title</h5>
2152          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2153
2154         >> this bit is really the body...
2155         <div> << we will ad dthis in hopefully it will not break shit.
2156         
2157         ** card text does not actually have any styling...
2158         
2159             <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>
2160         
2161         </div> <<
2162           <a href="#" class="card-link">Card link</a>
2163           
2164     </div>
2165     <div class="card-footer">
2166         <small class="text-muted">Last updated 3 mins ago</small>
2167     </div>
2168 </div>
2169          */
2170     getAutoCreate : function(){
2171         
2172         var cfg = {
2173             tag : 'div',
2174             cls : 'card',
2175             cn : [ ]
2176         };
2177         
2178         if (this.weight.length && this.weight != 'light') {
2179             cfg.cls += ' text-white';
2180         } else {
2181             cfg.cls += ' text-dark'; // need as it's nested..
2182         }
2183         if (this.weight.length) {
2184             cfg.cls += ' bg-' + this.weight;
2185         }
2186         
2187         cfg.cls += ' ' + this.layoutCls(); 
2188         
2189         var hdr = false;
2190         var hdr_ctr = false;
2191         if (this.header.length) {
2192             hdr = {
2193                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2194                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2195                 cn : []
2196             };
2197             cfg.cn.push(hdr);
2198             hdr_ctr = hdr;
2199         } else {
2200             hdr = {
2201                 tag : 'div',
2202                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2203                 cn : []
2204             };
2205             cfg.cn.push(hdr);
2206             hdr_ctr = hdr;
2207         }
2208         if (this.collapsable) {
2209             hdr_ctr = {
2210             tag : 'a',
2211             cls : 'd-block user-select-none',
2212             cn: [
2213                     {
2214                         tag: 'i',
2215                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2216                     }
2217                    
2218                 ]
2219             };
2220             hdr.cn.push(hdr_ctr);
2221         }
2222         
2223         hdr_ctr.cn.push(        {
2224             tag: 'span',
2225             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2226             html : this.header
2227         });
2228         
2229         
2230         if (this.header_image.length) {
2231             cfg.cn.push({
2232                 tag : 'img',
2233                 cls : 'card-img-top',
2234                 src: this.header_image // escape?
2235             });
2236         } else {
2237             cfg.cn.push({
2238                     tag : 'div',
2239                     cls : 'card-img-top d-none' 
2240                 });
2241         }
2242             
2243         var body = {
2244             tag : 'div',
2245             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2246             cn : []
2247         };
2248         var obody = body;
2249         if (this.collapsable || this.rotateable) {
2250             obody = {
2251                 tag: 'div',
2252                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253                 cn : [  body ]
2254             };
2255         }
2256         
2257         cfg.cn.push(obody);
2258         
2259         if (this.title.length) {
2260             body.cn.push({
2261                 tag : 'div',
2262                 cls : 'card-title',
2263                 src: this.title // escape?
2264             });
2265         }  
2266         
2267         if (this.subtitle.length) {
2268             body.cn.push({
2269                 tag : 'div',
2270                 cls : 'card-title',
2271                 src: this.subtitle // escape?
2272             });
2273         }
2274         
2275         body.cn.push({
2276             tag : 'div',
2277             cls : 'roo-card-body-ctr'
2278         });
2279         
2280         if (this.html.length) {
2281             body.cn.push({
2282                 tag: 'div',
2283                 html : this.html
2284             });
2285         }
2286         // fixme ? handle objects?
2287         
2288         if (this.footer.length) {
2289            
2290             cfg.cn.push({
2291                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2292                 html : this.footer
2293             });
2294             
2295         } else {
2296             cfg.cn.push({cls : 'card-footer d-none'});
2297         }
2298         
2299         // footer...
2300         
2301         return cfg;
2302     },
2303     
2304     
2305     getCardHeader : function()
2306     {
2307         var  ret = this.el.select('.card-header',true).first();
2308         if (ret.hasClass('d-none')) {
2309             ret.removeClass('d-none');
2310         }
2311         
2312         return ret;
2313     },
2314     getCardFooter : function()
2315     {
2316         var  ret = this.el.select('.card-footer',true).first();
2317         if (ret.hasClass('d-none')) {
2318             ret.removeClass('d-none');
2319         }
2320         
2321         return ret;
2322     },
2323     getCardImageTop : function()
2324     {
2325         var  ret = this.header_imageEl;
2326         if (ret.hasClass('d-none')) {
2327             ret.removeClass('d-none');
2328         }
2329             
2330         return ret;
2331     },
2332     
2333     getChildContainer : function()
2334     {
2335         
2336         if(!this.el){
2337             return false;
2338         }
2339         return this.el.select('.roo-card-body-ctr',true).first();    
2340     },
2341     
2342     initEvents: function() 
2343     {
2344         this.bodyEl = this.el.select('.card-body',true).first(); 
2345         this.containerEl = this.getChildContainer();
2346         if(this.dragable){
2347             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2348                     containerScroll: true,
2349                     ddGroup: this.drag_group || 'default_card_drag_group'
2350             });
2351             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2352         }
2353         if (this.dropable) {
2354             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2355                 containerScroll: true,
2356                 ddGroup: this.drop_group || 'default_card_drag_group'
2357             });
2358             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2359             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2360             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2361             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2362             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2363         }
2364         
2365         if (this.collapsable) {
2366             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2367         }
2368         if (this.rotateable) {
2369             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2370         }
2371         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2372          
2373         this.footerEl = this.el.select('.card-footer',true).first();
2374         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2375         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2376         this.headerEl = this.el.select('.card-header',true).first();
2377         
2378         if (this.rotated) {
2379             this.el.addClass('roo-card-rotated');
2380             this.fireEvent('rotate', this, true);
2381         }
2382         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2383         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2384         
2385     },
2386     getDragData : function(e)
2387     {
2388         var target = this.getEl();
2389         if (target) {
2390             //this.handleSelection(e);
2391             
2392             var dragData = {
2393                 source: this,
2394                 copy: false,
2395                 nodes: this.getEl(),
2396                 records: []
2397             };
2398             
2399             
2400             dragData.ddel = target.dom ;    // the div element
2401             Roo.log(target.getWidth( ));
2402             dragData.ddel.style.width = target.getWidth() + 'px';
2403             
2404             return dragData;
2405         }
2406         return false;
2407     },
2408     /**
2409     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2410     *    whole Element becomes the target, and this causes the drop gesture to append.
2411     *
2412     *    Returns an object:
2413     *     {
2414            
2415            position : 'below' or 'above'
2416            card  : relateive to card OBJECT (or true for no cards listed)
2417            items_n : relative to nth item in list
2418            card_n : relative to  nth card in list
2419     }
2420     *
2421     *    
2422     */
2423     getTargetFromEvent : function(e, dragged_card_el)
2424     {
2425         var target = e.getTarget();
2426         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2427             target = target.parentNode;
2428         }
2429         
2430         var ret = {
2431             position: '',
2432             cards : [],
2433             card_n : -1,
2434             items_n : -1,
2435             card : false 
2436         };
2437         
2438         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2439         // see if target is one of the 'cards'...
2440         
2441         
2442         //Roo.log(this.items.length);
2443         var pos = false;
2444         
2445         var last_card_n = 0;
2446         var cards_len  = 0;
2447         for (var i = 0;i< this.items.length;i++) {
2448             
2449             if (!this.items[i].el.hasClass('card')) {
2450                  continue;
2451             }
2452             pos = this.getDropPoint(e, this.items[i].el.dom);
2453             
2454             cards_len = ret.cards.length;
2455             //Roo.log(this.items[i].el.dom.id);
2456             ret.cards.push(this.items[i]);
2457             last_card_n  = i;
2458             if (ret.card_n < 0 && pos == 'above') {
2459                 ret.position = cards_len > 0 ? 'below' : pos;
2460                 ret.items_n = i > 0 ? i - 1 : 0;
2461                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2462                 ret.card = ret.cards[ret.card_n];
2463             }
2464         }
2465         if (!ret.cards.length) {
2466             ret.card = true;
2467             ret.position = 'below';
2468             ret.items_n;
2469             return ret;
2470         }
2471         // could not find a card.. stick it at the end..
2472         if (ret.card_n < 0) {
2473             ret.card_n = last_card_n;
2474             ret.card = ret.cards[last_card_n];
2475             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2476             ret.position = 'below';
2477         }
2478         
2479         if (this.items[ret.items_n].el == dragged_card_el) {
2480             return false;
2481         }
2482         
2483         if (ret.position == 'below') {
2484             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2485             
2486             if (card_after  && card_after.el == dragged_card_el) {
2487                 return false;
2488             }
2489             return ret;
2490         }
2491         
2492         // its's after ..
2493         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2494         
2495         if (card_before  && card_before.el == dragged_card_el) {
2496             return false;
2497         }
2498         
2499         return ret;
2500     },
2501     
2502     onNodeEnter : function(n, dd, e, data){
2503         return false;
2504     },
2505     onNodeOver : function(n, dd, e, data)
2506     {
2507        
2508         var target_info = this.getTargetFromEvent(e,data.source.el);
2509         if (target_info === false) {
2510             this.dropPlaceHolder('hide');
2511             return false;
2512         }
2513         Roo.log(['getTargetFromEvent', target_info ]);
2514         
2515         
2516         if (this.fireEvent('cardover', this, [ data ]) === false) {
2517             return false;
2518         }
2519         
2520         this.dropPlaceHolder('show', target_info,data);
2521         
2522         return false; 
2523     },
2524     onNodeOut : function(n, dd, e, data){
2525         this.dropPlaceHolder('hide');
2526      
2527     },
2528     onNodeDrop : function(n, dd, e, data)
2529     {
2530         
2531         // call drop - return false if
2532         
2533         // this could actually fail - if the Network drops..
2534         // we will ignore this at present..- client should probably reload
2535         // the whole set of cards if stuff like that fails.
2536         
2537         
2538         var info = this.getTargetFromEvent(e,data.source.el);
2539         if (info === false) {
2540             return false;
2541         }
2542         this.dropPlaceHolder('hide');
2543   
2544           
2545     
2546         this.acceptCard(data.source, info.position, info.card, info.items_n);
2547         return true;
2548          
2549     },
2550     firstChildCard : function()
2551     {
2552         for (var i = 0;i< this.items.length;i++) {
2553             
2554             if (!this.items[i].el.hasClass('card')) {
2555                  continue;
2556             }
2557             return this.items[i];
2558         }
2559         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2560     },
2561     /**
2562      * accept card
2563      *
2564      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2565      */
2566     acceptCard : function(move_card,  position, next_to_card )
2567     {
2568         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2569             return false;
2570         }
2571         
2572         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2573         
2574         move_card.parent().removeCard(move_card);
2575         
2576         
2577         var dom = move_card.el.dom;
2578         dom.style.width = ''; // clear with - which is set by drag.
2579         
2580         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2581             var cardel = next_to_card.el.dom;
2582             
2583             if (position == 'above' ) {
2584                 cardel.parentNode.insertBefore(dom, cardel);
2585             } else if (cardel.nextSibling) {
2586                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2587             } else {
2588                 cardel.parentNode.append(dom);
2589             }
2590         } else {
2591             // card container???
2592             this.containerEl.dom.append(dom);
2593         }
2594         
2595         //FIXME HANDLE card = true 
2596         
2597         // add this to the correct place in items.
2598         
2599         // remove Card from items.
2600         
2601        
2602         if (this.items.length) {
2603             var nitems = [];
2604             //Roo.log([info.items_n, info.position, this.items.length]);
2605             for (var i =0; i < this.items.length; i++) {
2606                 if (i == to_items_n && position == 'above') {
2607                     nitems.push(move_card);
2608                 }
2609                 nitems.push(this.items[i]);
2610                 if (i == to_items_n && position == 'below') {
2611                     nitems.push(move_card);
2612                 }
2613             }
2614             this.items = nitems;
2615             Roo.log(this.items);
2616         } else {
2617             this.items.push(move_card);
2618         }
2619         
2620         move_card.parentId = this.id;
2621         
2622         return true;
2623         
2624         
2625     },
2626     removeCard : function(c)
2627     {
2628         this.items = this.items.filter(function(e) { return e != c });
2629  
2630         var dom = c.el.dom;
2631         dom.parentNode.removeChild(dom);
2632         dom.style.width = ''; // clear with - which is set by drag.
2633         c.parentId = false;
2634         
2635     },
2636     
2637     /**    Decide whether to drop above or below a View node. */
2638     getDropPoint : function(e, n, dd)
2639     {
2640         if (dd) {
2641              return false;
2642         }
2643         if (n == this.containerEl.dom) {
2644             return "above";
2645         }
2646         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2647         var c = t + (b - t) / 2;
2648         var y = Roo.lib.Event.getPageY(e);
2649         if(y <= c) {
2650             return "above";
2651         }else{
2652             return "below";
2653         }
2654     },
2655     onToggleCollapse : function(e)
2656         {
2657         if (this.collapsed) {
2658             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2659             this.collapsableEl.addClass('show');
2660             this.collapsed = false;
2661             return;
2662         }
2663         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2664         this.collapsableEl.removeClass('show');
2665         this.collapsed = true;
2666         
2667     
2668     },
2669     
2670     onToggleRotate : function(e)
2671     {
2672         this.collapsableEl.removeClass('show');
2673         this.footerEl.removeClass('d-none');
2674         this.el.removeClass('roo-card-rotated');
2675         this.el.removeClass('d-none');
2676         if (this.rotated) {
2677             
2678             this.collapsableEl.addClass('show');
2679             this.rotated = false;
2680             this.fireEvent('rotate', this, this.rotated);
2681             return;
2682         }
2683         this.el.addClass('roo-card-rotated');
2684         this.footerEl.addClass('d-none');
2685         this.el.select('.roo-collapsable').removeClass('show');
2686         
2687         this.rotated = true;
2688         this.fireEvent('rotate', this, this.rotated);
2689     
2690     },
2691     
2692     dropPlaceHolder: function (action, info, data)
2693     {
2694         if (this.dropEl === false) {
2695             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2696             cls : 'd-none'
2697             },true);
2698         }
2699         this.dropEl.removeClass(['d-none', 'd-block']);        
2700         if (action == 'hide') {
2701             
2702             this.dropEl.addClass('d-none');
2703             return;
2704         }
2705         // FIXME - info.card == true!!!
2706         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2707         
2708         if (info.card !== true) {
2709             var cardel = info.card.el.dom;
2710             
2711             if (info.position == 'above') {
2712                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2713             } else if (cardel.nextSibling) {
2714                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2715             } else {
2716                 cardel.parentNode.append(this.dropEl.dom);
2717             }
2718         } else {
2719             // card container???
2720             this.containerEl.dom.append(this.dropEl.dom);
2721         }
2722         
2723         this.dropEl.addClass('d-block roo-card-dropzone');
2724         
2725         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726         
2727         
2728     
2729     
2730     
2731     },
2732     setHeaderText: function(html)
2733     {
2734         this.header = html;
2735         if (this.headerContainerEl) {
2736             this.headerContainerEl.dom.innerHTML = html;
2737         }
2738     },
2739     onHeaderImageLoad : function(ev, he)
2740     {
2741         if (!this.header_image_fit_square) {
2742             return;
2743         }
2744         
2745         var hw = he.naturalHeight / he.naturalWidth;
2746         // wide image = < 0
2747         // tall image = > 1
2748         //var w = he.dom.naturalWidth;
2749         var ww = he.width;
2750         he.style.left =  0;
2751         he.style.position =  'relative';
2752         if (hw > 1) {
2753             var nw = (ww * (1/hw));
2754             Roo.get(he).setSize( ww * (1/hw),  ww);
2755             he.style.left =  ((ww - nw)/ 2) + 'px';
2756             he.style.position =  'relative';
2757         }
2758
2759     }
2760
2761     
2762 });
2763
2764 /*
2765  * - LGPL
2766  *
2767  * Card header - holder for the card header elements.
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.CardHeader
2773  * @extends Roo.bootstrap.Element
2774  * Bootstrap CardHeader class
2775  * @constructor
2776  * Create a new Card Header - that you can embed children into
2777  * @param {Object} config The config object
2778  */
2779
2780 Roo.bootstrap.CardHeader = function(config){
2781     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2782 };
2783
2784 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2785     
2786     
2787     container_method : 'getCardHeader' 
2788     
2789      
2790     
2791     
2792    
2793 });
2794
2795  
2796
2797  /*
2798  * - LGPL
2799  *
2800  * Card footer - holder for the card footer elements.
2801  * 
2802  */
2803
2804 /**
2805  * @class Roo.bootstrap.CardFooter
2806  * @extends Roo.bootstrap.Element
2807  * Bootstrap CardFooter class
2808  * @constructor
2809  * Create a new Card Footer - that you can embed children into
2810  * @param {Object} config The config object
2811  */
2812
2813 Roo.bootstrap.CardFooter = function(config){
2814     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2815 };
2816
2817 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2818     
2819     
2820     container_method : 'getCardFooter' 
2821     
2822      
2823     
2824     
2825    
2826 });
2827
2828  
2829
2830  /*
2831  * - LGPL
2832  *
2833  * Card header - holder for the card header elements.
2834  * 
2835  */
2836
2837 /**
2838  * @class Roo.bootstrap.CardImageTop
2839  * @extends Roo.bootstrap.Element
2840  * Bootstrap CardImageTop class
2841  * @constructor
2842  * Create a new Card Image Top container
2843  * @param {Object} config The config object
2844  */
2845
2846 Roo.bootstrap.CardImageTop = function(config){
2847     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2848 };
2849
2850 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2851     
2852    
2853     container_method : 'getCardImageTop' 
2854     
2855      
2856     
2857    
2858 });
2859
2860  
2861
2862  
2863 /*
2864 * Licence: LGPL
2865 */
2866
2867 /**
2868  * @class Roo.bootstrap.ButtonUploader
2869  * @extends Roo.bootstrap.Button
2870  * Bootstrap Button Uploader class - it's a button which when you add files to it
2871  *
2872  * 
2873  * @cfg {Number} errorTimeout default 3000
2874  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2875  * @cfg {Array}  html The button text.
2876  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2877  *
2878  * @constructor
2879  * Create a new CardUploader
2880  * @param {Object} config The config object
2881  */
2882
2883 Roo.bootstrap.ButtonUploader = function(config){
2884     
2885  
2886     
2887     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2888     
2889      
2890      this.addEvents({
2891          // raw events
2892         /**
2893          * @event beforeselect
2894          * When button is pressed, before show upload files dialog is shown
2895          * @param {Roo.bootstrap.UploaderButton} this
2896          *
2897          */
2898         'beforeselect' : true,
2899          /**
2900          * @event fired when files have been selected, 
2901          * When a the download link is clicked
2902          * @param {Roo.bootstrap.UploaderButton} this
2903          * @param {Array} Array of files that have been uploaded
2904          */
2905         'uploaded' : true
2906         
2907     });
2908 };
2909  
2910 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2911     
2912      
2913     errorTimeout : 3000,
2914      
2915     images : false,
2916    
2917     fileCollection : false,
2918     allowBlank : true,
2919     
2920     multiple : true,
2921     
2922     getAutoCreate : function()
2923     {
2924         var im = {
2925             tag: 'input',
2926             type : 'file',
2927             cls : 'd-none  roo-card-upload-selector' 
2928           
2929         };
2930         if (this.multiple) {
2931             im.multiple = 'multiple';
2932         }
2933         
2934         return  {
2935             cls :'div' ,
2936             cn : [
2937                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2938                 im
2939
2940             ]
2941         };
2942            
2943          
2944     },
2945      
2946    
2947     initEvents : function()
2948     {
2949         
2950         Roo.bootstrap.Button.prototype.initEvents.call(this);
2951         
2952         
2953         
2954         
2955         
2956         this.urlAPI = (window.createObjectURL && window) || 
2957                                 (window.URL && URL.revokeObjectURL && URL) || 
2958                                 (window.webkitURL && webkitURL);
2959                         
2960          
2961          
2962          
2963         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2964         
2965         this.selectorEl.on('change', this.onFileSelected, this);
2966          
2967          
2968        
2969     },
2970     
2971    
2972     onClick : function(e)
2973     {
2974         e.preventDefault();
2975         
2976         if ( this.fireEvent('beforeselect', this) === false) {
2977             return;
2978         }
2979          
2980         this.selectorEl.dom.click();
2981          
2982     },
2983     
2984     onFileSelected : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2989             return;
2990         }
2991         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2992         this.selectorEl.dom.value  = '';// hopefully reset..
2993         
2994         this.fireEvent('uploaded', this,  files );
2995         
2996     },
2997     
2998        
2999    
3000     
3001     /**
3002      * addCard - add an Attachment to the uploader
3003      * @param data - the data about the image to upload
3004      *
3005      * {
3006           id : 123
3007           title : "Title of file",
3008           is_uploaded : false,
3009           src : "http://.....",
3010           srcfile : { the File upload object },
3011           mimetype : file.type,
3012           preview : false,
3013           is_deleted : 0
3014           .. any other data...
3015         }
3016      *
3017      * 
3018     */
3019      
3020     reset: function()
3021     {
3022          
3023          this.selectorEl
3024     } 
3025     
3026     
3027     
3028     
3029 });
3030  /*
3031  * - LGPL
3032  *
3033  * image
3034  * 
3035  */
3036
3037
3038 /**
3039  * @class Roo.bootstrap.Img
3040  * @extends Roo.bootstrap.Component
3041  * Bootstrap Img class
3042  * @cfg {Boolean} imgResponsive false | true
3043  * @cfg {String} border rounded | circle | thumbnail
3044  * @cfg {String} src image source
3045  * @cfg {String} alt image alternative text
3046  * @cfg {String} href a tag href
3047  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3048  * @cfg {String} xsUrl xs image source
3049  * @cfg {String} smUrl sm image source
3050  * @cfg {String} mdUrl md image source
3051  * @cfg {String} lgUrl lg image source
3052  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3053  * 
3054  * @constructor
3055  * Create a new Input
3056  * @param {Object} config The config object
3057  */
3058
3059 Roo.bootstrap.Img = function(config){
3060     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3061     
3062     this.addEvents({
3063         // img events
3064         /**
3065          * @event click
3066          * The img click event for the img.
3067          * @param {Roo.EventObject} e
3068          */
3069         "click" : true,
3070         /**
3071          * @event load
3072          * The when any image loads
3073          * @param {Roo.EventObject} e
3074          */
3075         "load" : true
3076     });
3077 };
3078
3079 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3080     
3081     imgResponsive: true,
3082     border: '',
3083     src: 'about:blank',
3084     href: false,
3085     target: false,
3086     xsUrl: '',
3087     smUrl: '',
3088     mdUrl: '',
3089     lgUrl: '',
3090     backgroundContain : false,
3091
3092     getAutoCreate : function()
3093     {   
3094         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3095             return this.createSingleImg();
3096         }
3097         
3098         var cfg = {
3099             tag: 'div',
3100             cls: 'roo-image-responsive-group',
3101             cn: []
3102         };
3103         var _this = this;
3104         
3105         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3106             
3107             if(!_this[size + 'Url']){
3108                 return;
3109             }
3110             
3111             var img = {
3112                 tag: 'img',
3113                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3114                 html: _this.html || cfg.html,
3115                 src: _this[size + 'Url']
3116             };
3117             
3118             img.cls += ' roo-image-responsive-' + size;
3119             
3120             var s = ['xs', 'sm', 'md', 'lg'];
3121             
3122             s.splice(s.indexOf(size), 1);
3123             
3124             Roo.each(s, function(ss){
3125                 img.cls += ' hidden-' + ss;
3126             });
3127             
3128             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3129                 cfg.cls += ' img-' + _this.border;
3130             }
3131             
3132             if(_this.alt){
3133                 cfg.alt = _this.alt;
3134             }
3135             
3136             if(_this.href){
3137                 var a = {
3138                     tag: 'a',
3139                     href: _this.href,
3140                     cn: [
3141                         img
3142                     ]
3143                 };
3144
3145                 if(this.target){
3146                     a.target = _this.target;
3147                 }
3148             }
3149             
3150             cfg.cn.push((_this.href) ? a : img);
3151             
3152         });
3153         
3154         return cfg;
3155     },
3156     
3157     createSingleImg : function()
3158     {
3159         var cfg = {
3160             tag: 'img',
3161             cls: (this.imgResponsive) ? 'img-responsive' : '',
3162             html : null,
3163             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3164         };
3165         
3166         if (this.backgroundContain) {
3167             cfg.cls += ' background-contain';
3168         }
3169         
3170         cfg.html = this.html || cfg.html;
3171         
3172         if (this.backgroundContain) {
3173             cfg.style="background-image: url(" + this.src + ')';
3174         } else {
3175             cfg.src = this.src || cfg.src;
3176         }
3177         
3178         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3179             cfg.cls += ' img-' + this.border;
3180         }
3181         
3182         if(this.alt){
3183             cfg.alt = this.alt;
3184         }
3185         
3186         if(this.href){
3187             var a = {
3188                 tag: 'a',
3189                 href: this.href,
3190                 cn: [
3191                     cfg
3192                 ]
3193             };
3194             
3195             if(this.target){
3196                 a.target = this.target;
3197             }
3198             
3199         }
3200         
3201         return (this.href) ? a : cfg;
3202     },
3203     
3204     initEvents: function() 
3205     {
3206         if(!this.href){
3207             this.el.on('click', this.onClick, this);
3208         }
3209         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3210             this.el.on('load', this.onImageLoad, this);
3211         } else {
3212             // not sure if this works.. not tested
3213             this.el.select('img', true).on('load', this.onImageLoad, this);
3214         }
3215         
3216     },
3217     
3218     onClick : function(e)
3219     {
3220         Roo.log('img onclick');
3221         this.fireEvent('click', this, e);
3222     },
3223     onImageLoad: function(e)
3224     {
3225         Roo.log('img load');
3226         this.fireEvent('load', this, e);
3227     },
3228     
3229     /**
3230      * Sets the url of the image - used to update it
3231      * @param {String} url the url of the image
3232      */
3233     
3234     setSrc : function(url)
3235     {
3236         this.src =  url;
3237         
3238         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3239             if (this.backgroundContain) {
3240                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3241             } else {
3242                 this.el.dom.src =  url;
3243             }
3244             return;
3245         }
3246         
3247         this.el.select('img', true).first().dom.src =  url;
3248     }
3249     
3250     
3251    
3252 });
3253
3254  /*
3255  * - LGPL
3256  *
3257  * image
3258  * 
3259  */
3260
3261
3262 /**
3263  * @class Roo.bootstrap.Link
3264  * @extends Roo.bootstrap.Component
3265  * Bootstrap Link Class
3266  * @cfg {String} alt image alternative text
3267  * @cfg {String} href a tag href
3268  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3269  * @cfg {String} html the content of the link.
3270  * @cfg {String} anchor name for the anchor link
3271  * @cfg {String} fa - favicon
3272
3273  * @cfg {Boolean} preventDefault (true | false) default false
3274
3275  * 
3276  * @constructor
3277  * Create a new Input
3278  * @param {Object} config The config object
3279  */
3280
3281 Roo.bootstrap.Link = function(config){
3282     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3283     
3284     this.addEvents({
3285         // img events
3286         /**
3287          * @event click
3288          * The img click event for the img.
3289          * @param {Roo.EventObject} e
3290          */
3291         "click" : true
3292     });
3293 };
3294
3295 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3296     
3297     href: false,
3298     target: false,
3299     preventDefault: false,
3300     anchor : false,
3301     alt : false,
3302     fa: false,
3303
3304
3305     getAutoCreate : function()
3306     {
3307         var html = this.html || '';
3308         
3309         if (this.fa !== false) {
3310             html = '<i class="fa fa-' + this.fa + '"></i>';
3311         }
3312         var cfg = {
3313             tag: 'a'
3314         };
3315         // anchor's do not require html/href...
3316         if (this.anchor === false) {
3317             cfg.html = html;
3318             cfg.href = this.href || '#';
3319         } else {
3320             cfg.name = this.anchor;
3321             if (this.html !== false || this.fa !== false) {
3322                 cfg.html = html;
3323             }
3324             if (this.href !== false) {
3325                 cfg.href = this.href;
3326             }
3327         }
3328         
3329         if(this.alt !== false){
3330             cfg.alt = this.alt;
3331         }
3332         
3333         
3334         if(this.target !== false) {
3335             cfg.target = this.target;
3336         }
3337         
3338         return cfg;
3339     },
3340     
3341     initEvents: function() {
3342         
3343         if(!this.href || this.preventDefault){
3344             this.el.on('click', this.onClick, this);
3345         }
3346     },
3347     
3348     onClick : function(e)
3349     {
3350         if(this.preventDefault){
3351             e.preventDefault();
3352         }
3353         //Roo.log('img onclick');
3354         this.fireEvent('click', this, e);
3355     }
3356    
3357 });
3358
3359  /*
3360  * - LGPL
3361  *
3362  * header
3363  * 
3364  */
3365
3366 /**
3367  * @class Roo.bootstrap.Header
3368  * @extends Roo.bootstrap.Component
3369  * Bootstrap Header class
3370  * @cfg {String} html content of header
3371  * @cfg {Number} level (1|2|3|4|5|6) default 1
3372  * 
3373  * @constructor
3374  * Create a new Header
3375  * @param {Object} config The config object
3376  */
3377
3378
3379 Roo.bootstrap.Header  = function(config){
3380     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3381 };
3382
3383 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3384     
3385     //href : false,
3386     html : false,
3387     level : 1,
3388     
3389     
3390     
3391     getAutoCreate : function(){
3392         
3393         
3394         
3395         var cfg = {
3396             tag: 'h' + (1 *this.level),
3397             html: this.html || ''
3398         } ;
3399         
3400         return cfg;
3401     }
3402    
3403 });
3404
3405  
3406
3407  /*
3408  * Based on:
3409  * Ext JS Library 1.1.1
3410  * Copyright(c) 2006-2007, Ext JS, LLC.
3411  *
3412  * Originally Released Under LGPL - original licence link has changed is not relivant.
3413  *
3414  * Fork - LGPL
3415  * <script type="text/javascript">
3416  */
3417  
3418 /**
3419  * @class Roo.bootstrap.MenuMgr
3420  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3421  * @singleton
3422  */
3423 Roo.bootstrap.MenuMgr = function(){
3424    var menus, active, groups = {}, attached = false, lastShow = new Date();
3425
3426    // private - called when first menu is created
3427    function init(){
3428        menus = {};
3429        active = new Roo.util.MixedCollection();
3430        Roo.get(document).addKeyListener(27, function(){
3431            if(active.length > 0){
3432                hideAll();
3433            }
3434        });
3435    }
3436
3437    // private
3438    function hideAll(){
3439        if(active && active.length > 0){
3440            var c = active.clone();
3441            c.each(function(m){
3442                m.hide();
3443            });
3444        }
3445    }
3446
3447    // private
3448    function onHide(m){
3449        active.remove(m);
3450        if(active.length < 1){
3451            Roo.get(document).un("mouseup", onMouseDown);
3452             
3453            attached = false;
3454        }
3455    }
3456
3457    // private
3458    function onShow(m){
3459        var last = active.last();
3460        lastShow = new Date();
3461        active.add(m);
3462        if(!attached){
3463           Roo.get(document).on("mouseup", onMouseDown);
3464            
3465            attached = true;
3466        }
3467        if(m.parentMenu){
3468           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3469           m.parentMenu.activeChild = m;
3470        }else if(last && last.isVisible()){
3471           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3472        }
3473    }
3474
3475    // private
3476    function onBeforeHide(m){
3477        if(m.activeChild){
3478            m.activeChild.hide();
3479        }
3480        if(m.autoHideTimer){
3481            clearTimeout(m.autoHideTimer);
3482            delete m.autoHideTimer;
3483        }
3484    }
3485
3486    // private
3487    function onBeforeShow(m){
3488        var pm = m.parentMenu;
3489        if(!pm && !m.allowOtherMenus){
3490            hideAll();
3491        }else if(pm && pm.activeChild && active != m){
3492            pm.activeChild.hide();
3493        }
3494    }
3495
3496    // private this should really trigger on mouseup..
3497    function onMouseDown(e){
3498         Roo.log("on Mouse Up");
3499         
3500         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3501             Roo.log("MenuManager hideAll");
3502             hideAll();
3503             e.stopEvent();
3504         }
3505         
3506         
3507    }
3508
3509    // private
3510    function onBeforeCheck(mi, state){
3511        if(state){
3512            var g = groups[mi.group];
3513            for(var i = 0, l = g.length; i < l; i++){
3514                if(g[i] != mi){
3515                    g[i].setChecked(false);
3516                }
3517            }
3518        }
3519    }
3520
3521    return {
3522
3523        /**
3524         * Hides all menus that are currently visible
3525         */
3526        hideAll : function(){
3527             hideAll();  
3528        },
3529
3530        // private
3531        register : function(menu){
3532            if(!menus){
3533                init();
3534            }
3535            menus[menu.id] = menu;
3536            menu.on("beforehide", onBeforeHide);
3537            menu.on("hide", onHide);
3538            menu.on("beforeshow", onBeforeShow);
3539            menu.on("show", onShow);
3540            var g = menu.group;
3541            if(g && menu.events["checkchange"]){
3542                if(!groups[g]){
3543                    groups[g] = [];
3544                }
3545                groups[g].push(menu);
3546                menu.on("checkchange", onCheck);
3547            }
3548        },
3549
3550         /**
3551          * Returns a {@link Roo.menu.Menu} object
3552          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3553          * be used to generate and return a new Menu instance.
3554          */
3555        get : function(menu){
3556            if(typeof menu == "string"){ // menu id
3557                return menus[menu];
3558            }else if(menu.events){  // menu instance
3559                return menu;
3560            }
3561            /*else if(typeof menu.length == 'number'){ // array of menu items?
3562                return new Roo.bootstrap.Menu({items:menu});
3563            }else{ // otherwise, must be a config
3564                return new Roo.bootstrap.Menu(menu);
3565            }
3566            */
3567            return false;
3568        },
3569
3570        // private
3571        unregister : function(menu){
3572            delete menus[menu.id];
3573            menu.un("beforehide", onBeforeHide);
3574            menu.un("hide", onHide);
3575            menu.un("beforeshow", onBeforeShow);
3576            menu.un("show", onShow);
3577            var g = menu.group;
3578            if(g && menu.events["checkchange"]){
3579                groups[g].remove(menu);
3580                menu.un("checkchange", onCheck);
3581            }
3582        },
3583
3584        // private
3585        registerCheckable : function(menuItem){
3586            var g = menuItem.group;
3587            if(g){
3588                if(!groups[g]){
3589                    groups[g] = [];
3590                }
3591                groups[g].push(menuItem);
3592                menuItem.on("beforecheckchange", onBeforeCheck);
3593            }
3594        },
3595
3596        // private
3597        unregisterCheckable : function(menuItem){
3598            var g = menuItem.group;
3599            if(g){
3600                groups[g].remove(menuItem);
3601                menuItem.un("beforecheckchange", onBeforeCheck);
3602            }
3603        }
3604    };
3605 }();/*
3606  * - LGPL
3607  *
3608  * menu
3609  * 
3610  */
3611
3612 /**
3613  * @class Roo.bootstrap.Menu
3614  * @extends Roo.bootstrap.Component
3615  * Bootstrap Menu class - container for MenuItems
3616  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3617  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3618  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3619  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3620   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3621   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3622  
3623  * @constructor
3624  * Create a new Menu
3625  * @param {Object} config The config object
3626  */
3627
3628
3629 Roo.bootstrap.Menu = function(config){
3630     
3631     if (config.type == 'treeview') {
3632         // normally menu's are drawn attached to the document to handle layering etc..
3633         // however treeview (used by the docs menu is drawn into the parent element)
3634         this.container_method = 'getChildContainer'; 
3635     }
3636     
3637     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3638     if (this.registerMenu && this.type != 'treeview')  {
3639         Roo.bootstrap.MenuMgr.register(this);
3640     }
3641     
3642     
3643     this.addEvents({
3644         /**
3645          * @event beforeshow
3646          * Fires before this menu is displayed (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforeshow : true,
3650         /**
3651          * @event beforehide
3652          * Fires before this menu is hidden (return false to block)
3653          * @param {Roo.menu.Menu} this
3654          */
3655         beforehide : true,
3656         /**
3657          * @event show
3658          * Fires after this menu is displayed
3659          * @param {Roo.menu.Menu} this
3660          */
3661         show : true,
3662         /**
3663          * @event hide
3664          * Fires after this menu is hidden
3665          * @param {Roo.menu.Menu} this
3666          */
3667         hide : true,
3668         /**
3669          * @event click
3670          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3671          * @param {Roo.menu.Menu} this
3672          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3673          * @param {Roo.EventObject} e
3674          */
3675         click : true,
3676         /**
3677          * @event mouseover
3678          * Fires when the mouse is hovering over this menu
3679          * @param {Roo.menu.Menu} this
3680          * @param {Roo.EventObject} e
3681          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3682          */
3683         mouseover : true,
3684         /**
3685          * @event mouseout
3686          * Fires when the mouse exits this menu
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.EventObject} e
3689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690          */
3691         mouseout : true,
3692         /**
3693          * @event itemclick
3694          * Fires when a menu item contained in this menu is clicked
3695          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3696          * @param {Roo.EventObject} e
3697          */
3698         itemclick: true
3699     });
3700     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3701 };
3702
3703 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3704     
3705    /// html : false,
3706    
3707     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3708     type: false,
3709     /**
3710      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3711      */
3712     registerMenu : true,
3713     
3714     menuItems :false, // stores the menu items..
3715     
3716     hidden:true,
3717         
3718     parentMenu : false,
3719     
3720     stopEvent : true,
3721     
3722     isLink : false,
3723     
3724     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3725     
3726     hideTrigger : false,
3727     
3728     align : 'tl-bl?',
3729     
3730     
3731     getChildContainer : function() {
3732         return this.el;  
3733     },
3734     
3735     getAutoCreate : function(){
3736          
3737         //if (['right'].indexOf(this.align)!==-1) {
3738         //    cfg.cn[1].cls += ' pull-right'
3739         //}
3740          
3741         var cfg = {
3742             tag : 'ul',
3743             cls : 'dropdown-menu shadow' ,
3744             style : 'z-index:1000'
3745             
3746         };
3747         
3748         if (this.type === 'submenu') {
3749             cfg.cls = 'submenu active';
3750         }
3751         if (this.type === 'treeview') {
3752             cfg.cls = 'treeview-menu';
3753         }
3754         
3755         return cfg;
3756     },
3757     initEvents : function() {
3758         
3759        // Roo.log("ADD event");
3760        // Roo.log(this.triggerEl.dom);
3761         if (this.triggerEl) {
3762             
3763             this.triggerEl.on('click', this.onTriggerClick, this);
3764             
3765             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3766             
3767             if (!this.hideTrigger) {
3768                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3769                     // dropdown toggle on the 'a' in BS4?
3770                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3771                 } else {
3772                     this.triggerEl.addClass('dropdown-toggle');
3773                 }
3774             }
3775         }
3776         
3777         if (Roo.isTouch) {
3778             this.el.on('touchstart'  , this.onTouch, this);
3779         }
3780         this.el.on('click' , this.onClick, this);
3781
3782         this.el.on("mouseover", this.onMouseOver, this);
3783         this.el.on("mouseout", this.onMouseOut, this);
3784         
3785     },
3786     
3787     findTargetItem : function(e)
3788     {
3789         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3790         if(!t){
3791             return false;
3792         }
3793         //Roo.log(t);         Roo.log(t.id);
3794         if(t && t.id){
3795             //Roo.log(this.menuitems);
3796             return this.menuitems.get(t.id);
3797             
3798             //return this.items.get(t.menuItemId);
3799         }
3800         
3801         return false;
3802     },
3803     
3804     onTouch : function(e) 
3805     {
3806         Roo.log("menu.onTouch");
3807         //e.stopEvent(); this make the user popdown broken
3808         this.onClick(e);
3809     },
3810     
3811     onClick : function(e)
3812     {
3813         Roo.log("menu.onClick");
3814         
3815         var t = this.findTargetItem(e);
3816         if(!t || t.isContainer){
3817             return;
3818         }
3819         Roo.log(e);
3820         /*
3821         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3822             if(t == this.activeItem && t.shouldDeactivate(e)){
3823                 this.activeItem.deactivate();
3824                 delete this.activeItem;
3825                 return;
3826             }
3827             if(t.canActivate){
3828                 this.setActiveItem(t, true);
3829             }
3830             return;
3831             
3832             
3833         }
3834         */
3835        
3836         Roo.log('pass click event');
3837         
3838         t.onClick(e);
3839         
3840         this.fireEvent("click", this, t, e);
3841         
3842         var _this = this;
3843         
3844         if(!t.href.length || t.href == '#'){
3845             (function() { _this.hide(); }).defer(100);
3846         }
3847         
3848     },
3849     
3850     onMouseOver : function(e){
3851         var t  = this.findTargetItem(e);
3852         //Roo.log(t);
3853         //if(t){
3854         //    if(t.canActivate && !t.disabled){
3855         //        this.setActiveItem(t, true);
3856         //    }
3857         //}
3858         
3859         this.fireEvent("mouseover", this, e, t);
3860     },
3861     isVisible : function(){
3862         return !this.hidden;
3863     },
3864     onMouseOut : function(e){
3865         var t  = this.findTargetItem(e);
3866         
3867         //if(t ){
3868         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3869         //        this.activeItem.deactivate();
3870         //        delete this.activeItem;
3871         //    }
3872         //}
3873         this.fireEvent("mouseout", this, e, t);
3874     },
3875     
3876     
3877     /**
3878      * Displays this menu relative to another element
3879      * @param {String/HTMLElement/Roo.Element} element The element to align to
3880      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3881      * the element (defaults to this.defaultAlign)
3882      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3883      */
3884     show : function(el, pos, parentMenu)
3885     {
3886         if (false === this.fireEvent("beforeshow", this)) {
3887             Roo.log("show canceled");
3888             return;
3889         }
3890         this.parentMenu = parentMenu;
3891         if(!this.el){
3892             this.render();
3893         }
3894         this.el.addClass('show'); // show otherwise we do not know how big we are..
3895          
3896         var xy = this.el.getAlignToXY(el, pos);
3897         
3898         // bl-tl << left align  below
3899         // tl-bl << left align 
3900         
3901         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3902             // if it goes to far to the right.. -> align left.
3903             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3904         }
3905         if(xy[0] < 0){
3906             // was left align - go right?
3907             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3908         }
3909         
3910         // goes down the bottom
3911         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3912            xy[1]  < 0 ){
3913             var a = this.align.replace('?', '').split('-');
3914             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3915             
3916         }
3917         
3918         this.showAt(  xy , parentMenu, false);
3919     },
3920      /**
3921      * Displays this menu at a specific xy position
3922      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3923      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3924      */
3925     showAt : function(xy, parentMenu, /* private: */_e){
3926         this.parentMenu = parentMenu;
3927         if(!this.el){
3928             this.render();
3929         }
3930         if(_e !== false){
3931             this.fireEvent("beforeshow", this);
3932             //xy = this.el.adjustForConstraints(xy);
3933         }
3934         
3935         //this.el.show();
3936         this.hideMenuItems();
3937         this.hidden = false;
3938         if (this.triggerEl) {
3939             this.triggerEl.addClass('open');
3940         }
3941         
3942         this.el.addClass('show');
3943         
3944         
3945         
3946         // reassign x when hitting right
3947         
3948         // reassign y when hitting bottom
3949         
3950         // but the list may align on trigger left or trigger top... should it be a properity?
3951         
3952         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3953             this.el.setXY(xy);
3954         }
3955         
3956         this.focus();
3957         this.fireEvent("show", this);
3958     },
3959     
3960     focus : function(){
3961         return;
3962         if(!this.hidden){
3963             this.doFocus.defer(50, this);
3964         }
3965     },
3966
3967     doFocus : function(){
3968         if(!this.hidden){
3969             this.focusEl.focus();
3970         }
3971     },
3972
3973     /**
3974      * Hides this menu and optionally all parent menus
3975      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3976      */
3977     hide : function(deep)
3978     {
3979         if (false === this.fireEvent("beforehide", this)) {
3980             Roo.log("hide canceled");
3981             return;
3982         }
3983         this.hideMenuItems();
3984         if(this.el && this.isVisible()){
3985            
3986             if(this.activeItem){
3987                 this.activeItem.deactivate();
3988                 this.activeItem = null;
3989             }
3990             if (this.triggerEl) {
3991                 this.triggerEl.removeClass('open');
3992             }
3993             
3994             this.el.removeClass('show');
3995             this.hidden = true;
3996             this.fireEvent("hide", this);
3997         }
3998         if(deep === true && this.parentMenu){
3999             this.parentMenu.hide(true);
4000         }
4001     },
4002     
4003     onTriggerClick : function(e)
4004     {
4005         Roo.log('trigger click');
4006         
4007         var target = e.getTarget();
4008         
4009         Roo.log(target.nodeName.toLowerCase());
4010         
4011         if(target.nodeName.toLowerCase() === 'i'){
4012             e.preventDefault();
4013         }
4014         
4015     },
4016     
4017     onTriggerPress  : function(e)
4018     {
4019         Roo.log('trigger press');
4020         //Roo.log(e.getTarget());
4021        // Roo.log(this.triggerEl.dom);
4022        
4023         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4024         var pel = Roo.get(e.getTarget());
4025         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4026             Roo.log('is treeview or dropdown?');
4027             return;
4028         }
4029         
4030         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4031             return;
4032         }
4033         
4034         if (this.isVisible()) {
4035             Roo.log('hide');
4036             this.hide();
4037         } else {
4038             Roo.log('show');
4039             
4040             this.show(this.triggerEl, this.align, false);
4041         }
4042         
4043         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044             e.stopEvent();
4045         }
4046         
4047     },
4048        
4049     
4050     hideMenuItems : function()
4051     {
4052         Roo.log("hide Menu Items");
4053         if (!this.el) { 
4054             return;
4055         }
4056         
4057         this.el.select('.open',true).each(function(aa) {
4058             
4059             aa.removeClass('open');
4060          
4061         });
4062     },
4063     addxtypeChild : function (tree, cntr) {
4064         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4065           
4066         this.menuitems.add(comp);
4067         return comp;
4068
4069     },
4070     getEl : function()
4071     {
4072         Roo.log(this.el);
4073         return this.el;
4074     },
4075     
4076     clear : function()
4077     {
4078         this.getEl().dom.innerHTML = '';
4079         this.menuitems.clear();
4080     }
4081 });
4082
4083  
4084  /*
4085  * - LGPL
4086  *
4087  * menu item
4088  * 
4089  */
4090
4091
4092 /**
4093  * @class Roo.bootstrap.MenuItem
4094  * @extends Roo.bootstrap.Component
4095  * Bootstrap MenuItem class
4096  * @cfg {String} html the menu label
4097  * @cfg {String} href the link
4098  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4099  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4100  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4101  * @cfg {String} fa favicon to show on left of menu item.
4102  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4103  * 
4104  * 
4105  * @constructor
4106  * Create a new MenuItem
4107  * @param {Object} config The config object
4108  */
4109
4110
4111 Roo.bootstrap.MenuItem = function(config){
4112     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4113     this.addEvents({
4114         // raw events
4115         /**
4116          * @event click
4117          * The raw click event for the entire grid.
4118          * @param {Roo.bootstrap.MenuItem} this
4119          * @param {Roo.EventObject} e
4120          */
4121         "click" : true
4122     });
4123 };
4124
4125 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4126     
4127     href : false,
4128     html : false,
4129     preventDefault: false,
4130     isContainer : false,
4131     active : false,
4132     fa: false,
4133     
4134     getAutoCreate : function(){
4135         
4136         if(this.isContainer){
4137             return {
4138                 tag: 'li',
4139                 cls: 'dropdown-menu-item '
4140             };
4141         }
4142         var ctag = {
4143             tag: 'span',
4144             html: 'Link'
4145         };
4146         
4147         var anc = {
4148             tag : 'a',
4149             cls : 'dropdown-item',
4150             href : '#',
4151             cn : [  ]
4152         };
4153         
4154         if (this.fa !== false) {
4155             anc.cn.push({
4156                 tag : 'i',
4157                 cls : 'fa fa-' + this.fa
4158             });
4159         }
4160         
4161         anc.cn.push(ctag);
4162         
4163         
4164         var cfg= {
4165             tag: 'li',
4166             cls: 'dropdown-menu-item',
4167             cn: [ anc ]
4168         };
4169         if (this.parent().type == 'treeview') {
4170             cfg.cls = 'treeview-menu';
4171         }
4172         if (this.active) {
4173             cfg.cls += ' active';
4174         }
4175         
4176         
4177         
4178         anc.href = this.href || cfg.cn[0].href ;
4179         ctag.html = this.html || cfg.cn[0].html ;
4180         return cfg;
4181     },
4182     
4183     initEvents: function()
4184     {
4185         if (this.parent().type == 'treeview') {
4186             this.el.select('a').on('click', this.onClick, this);
4187         }
4188         
4189         if (this.menu) {
4190             this.menu.parentType = this.xtype;
4191             this.menu.triggerEl = this.el;
4192             this.menu = this.addxtype(Roo.apply({}, this.menu));
4193         }
4194         
4195     },
4196     onClick : function(e)
4197     {
4198         Roo.log('item on click ');
4199         
4200         if(this.preventDefault){
4201             e.preventDefault();
4202         }
4203         //this.parent().hideMenuItems();
4204         
4205         this.fireEvent('click', this, e);
4206     },
4207     getEl : function()
4208     {
4209         return this.el;
4210     } 
4211 });
4212
4213  
4214
4215  /*
4216  * - LGPL
4217  *
4218  * menu separator
4219  * 
4220  */
4221
4222
4223 /**
4224  * @class Roo.bootstrap.MenuSeparator
4225  * @extends Roo.bootstrap.Component
4226  * Bootstrap MenuSeparator class
4227  * 
4228  * @constructor
4229  * Create a new MenuItem
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.MenuSeparator = function(config){
4235     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             cls: 'divider',
4243             tag : 'li'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @builder-top
4262  * @parent none
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Array} buttons Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @singleton
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.Navbar
5525  * @extends Roo.bootstrap.Component
5526  * Bootstrap Navbar class
5527
5528  * @constructor
5529  * Create a new Navbar
5530  * @param {Object} config The config object
5531  */
5532
5533
5534 Roo.bootstrap.Navbar = function(config){
5535     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5536     this.addEvents({
5537         // raw events
5538         /**
5539          * @event beforetoggle
5540          * Fire before toggle the menu
5541          * @param {Roo.EventObject} e
5542          */
5543         "beforetoggle" : true
5544     });
5545 };
5546
5547 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5548     
5549     
5550    
5551     // private
5552     navItems : false,
5553     loadMask : false,
5554     
5555     
5556     getAutoCreate : function(){
5557         
5558         
5559         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5560         
5561     },
5562     
5563     initEvents :function ()
5564     {
5565         //Roo.log(this.el.select('.navbar-toggle',true));
5566         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5567         
5568         var mark = {
5569             tag: "div",
5570             cls:"x-dlg-mask"
5571         };
5572         
5573         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5574         
5575         var size = this.el.getSize();
5576         this.maskEl.setSize(size.width, size.height);
5577         this.maskEl.enableDisplayMode("block");
5578         this.maskEl.hide();
5579         
5580         if(this.loadMask){
5581             this.maskEl.show();
5582         }
5583     },
5584     
5585     
5586     getChildContainer : function()
5587     {
5588         if (this.el && this.el.select('.collapse').getCount()) {
5589             return this.el.select('.collapse',true).first();
5590         }
5591         
5592         return this.el;
5593     },
5594     
5595     mask : function()
5596     {
5597         this.maskEl.show();
5598     },
5599     
5600     unmask : function()
5601     {
5602         this.maskEl.hide();
5603     },
5604     onToggle : function()
5605     {
5606         
5607         if(this.fireEvent('beforetoggle', this) === false){
5608             return;
5609         }
5610         var ce = this.el.select('.navbar-collapse',true).first();
5611       
5612         if (!ce.hasClass('show')) {
5613            this.expand();
5614         } else {
5615             this.collapse();
5616         }
5617         
5618         
5619     
5620     },
5621     /**
5622      * Expand the navbar pulldown 
5623      */
5624     expand : function ()
5625     {
5626        
5627         var ce = this.el.select('.navbar-collapse',true).first();
5628         if (ce.hasClass('collapsing')) {
5629             return;
5630         }
5631         ce.dom.style.height = '';
5632                // show it...
5633         ce.addClass('in'); // old...
5634         ce.removeClass('collapse');
5635         ce.addClass('show');
5636         var h = ce.getHeight();
5637         Roo.log(h);
5638         ce.removeClass('show');
5639         // at this point we should be able to see it..
5640         ce.addClass('collapsing');
5641         
5642         ce.setHeight(0); // resize it ...
5643         ce.on('transitionend', function() {
5644             //Roo.log('done transition');
5645             ce.removeClass('collapsing');
5646             ce.addClass('show');
5647             ce.removeClass('collapse');
5648
5649             ce.dom.style.height = '';
5650         }, this, { single: true} );
5651         ce.setHeight(h);
5652         ce.dom.scrollTop = 0;
5653     },
5654     /**
5655      * Collapse the navbar pulldown 
5656      */
5657     collapse : function()
5658     {
5659          var ce = this.el.select('.navbar-collapse',true).first();
5660        
5661         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5662             // it's collapsed or collapsing..
5663             return;
5664         }
5665         ce.removeClass('in'); // old...
5666         ce.setHeight(ce.getHeight());
5667         ce.removeClass('show');
5668         ce.addClass('collapsing');
5669         
5670         ce.on('transitionend', function() {
5671             ce.dom.style.height = '';
5672             ce.removeClass('collapsing');
5673             ce.addClass('collapse');
5674         }, this, { single: true} );
5675         ce.setHeight(0);
5676     }
5677     
5678     
5679     
5680 });
5681
5682
5683
5684  
5685
5686  /*
5687  * - LGPL
5688  *
5689  * navbar
5690  * 
5691  */
5692
5693 /**
5694  * @class Roo.bootstrap.NavSimplebar
5695  * @extends Roo.bootstrap.Navbar
5696  * Bootstrap Sidebar class
5697  *
5698  * @cfg {Boolean} inverse is inverted color
5699  * 
5700  * @cfg {String} type (nav | pills | tabs)
5701  * @cfg {Boolean} arrangement stacked | justified
5702  * @cfg {String} align (left | right) alignment
5703  * 
5704  * @cfg {Boolean} main (true|false) main nav bar? default false
5705  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5706  * 
5707  * @cfg {String} tag (header|footer|nav|div) default is nav 
5708
5709  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5710  * 
5711  * 
5712  * @constructor
5713  * Create a new Sidebar
5714  * @param {Object} config The config object
5715  */
5716
5717
5718 Roo.bootstrap.NavSimplebar = function(config){
5719     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5720 };
5721
5722 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5723     
5724     inverse: false,
5725     
5726     type: false,
5727     arrangement: '',
5728     align : false,
5729     
5730     weight : 'light',
5731     
5732     main : false,
5733     
5734     
5735     tag : false,
5736     
5737     
5738     getAutoCreate : function(){
5739         
5740         
5741         var cfg = {
5742             tag : this.tag || 'div',
5743             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5744         };
5745         if (['light','white'].indexOf(this.weight) > -1) {
5746             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5747         }
5748         cfg.cls += ' bg-' + this.weight;
5749         
5750         if (this.inverse) {
5751             cfg.cls += ' navbar-inverse';
5752             
5753         }
5754         
5755         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5756         
5757         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758             return cfg;
5759         }
5760         
5761         
5762     
5763         
5764         cfg.cn = [
5765             {
5766                 cls: 'nav nav-' + this.xtype,
5767                 tag : 'ul'
5768             }
5769         ];
5770         
5771          
5772         this.type = this.type || 'nav';
5773         if (['tabs','pills'].indexOf(this.type) != -1) {
5774             cfg.cn[0].cls += ' nav-' + this.type
5775         
5776         
5777         } else {
5778             if (this.type!=='nav') {
5779                 Roo.log('nav type must be nav/tabs/pills')
5780             }
5781             cfg.cn[0].cls += ' navbar-nav'
5782         }
5783         
5784         
5785         
5786         
5787         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5788             cfg.cn[0].cls += ' nav-' + this.arrangement;
5789         }
5790         
5791         
5792         if (this.align === 'right') {
5793             cfg.cn[0].cls += ' navbar-right';
5794         }
5795         
5796         
5797         
5798         
5799         return cfg;
5800     
5801         
5802     }
5803     
5804     
5805     
5806 });
5807
5808
5809
5810  
5811
5812  
5813        /*
5814  * - LGPL
5815  *
5816  * navbar
5817  * navbar-fixed-top
5818  * navbar-expand-md  fixed-top 
5819  */
5820
5821 /**
5822  * @class Roo.bootstrap.NavHeaderbar
5823  * @extends Roo.bootstrap.NavSimplebar
5824  * Bootstrap Sidebar class
5825  *
5826  * @cfg {String} brand what is brand
5827  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5828  * @cfg {String} brand_href href of the brand
5829  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5830  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5831  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5832  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5833  * 
5834  * @constructor
5835  * Create a new Sidebar
5836  * @param {Object} config The config object
5837  */
5838
5839
5840 Roo.bootstrap.NavHeaderbar = function(config){
5841     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5842       
5843 };
5844
5845 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5846     
5847     position: '',
5848     brand: '',
5849     brand_href: false,
5850     srButton : true,
5851     autohide : false,
5852     desktopCenter : false,
5853    
5854     
5855     getAutoCreate : function(){
5856         
5857         var   cfg = {
5858             tag: this.nav || 'nav',
5859             cls: 'navbar navbar-expand-md',
5860             role: 'navigation',
5861             cn: []
5862         };
5863         
5864         var cn = cfg.cn;
5865         if (this.desktopCenter) {
5866             cn.push({cls : 'container', cn : []});
5867             cn = cn[0].cn;
5868         }
5869         
5870         if(this.srButton){
5871             var btn = {
5872                 tag: 'button',
5873                 type: 'button',
5874                 cls: 'navbar-toggle navbar-toggler',
5875                 'data-toggle': 'collapse',
5876                 cn: [
5877                     {
5878                         tag: 'span',
5879                         cls: 'sr-only',
5880                         html: 'Toggle navigation'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar navbar-toggler-icon'
5885                     },
5886                     {
5887                         tag: 'span',
5888                         cls: 'icon-bar'
5889                     },
5890                     {
5891                         tag: 'span',
5892                         cls: 'icon-bar'
5893                     }
5894                 ]
5895             };
5896             
5897             cn.push( Roo.bootstrap.version == 4 ? btn : {
5898                 tag: 'div',
5899                 cls: 'navbar-header',
5900                 cn: [
5901                     btn
5902                 ]
5903             });
5904         }
5905         
5906         cn.push({
5907             tag: 'div',
5908             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5909             cn : []
5910         });
5911         
5912         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5913         
5914         if (['light','white'].indexOf(this.weight) > -1) {
5915             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5916         }
5917         cfg.cls += ' bg-' + this.weight;
5918         
5919         
5920         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5921             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5922             
5923             // tag can override this..
5924             
5925             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5926         }
5927         
5928         if (this.brand !== '') {
5929             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5930             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5931                 tag: 'a',
5932                 href: this.brand_href ? this.brand_href : '#',
5933                 cls: 'navbar-brand',
5934                 cn: [
5935                 this.brand
5936                 ]
5937             });
5938         }
5939         
5940         if(this.main){
5941             cfg.cls += ' main-nav';
5942         }
5943         
5944         
5945         return cfg;
5946
5947         
5948     },
5949     getHeaderChildContainer : function()
5950     {
5951         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5952             return this.el.select('.navbar-header',true).first();
5953         }
5954         
5955         return this.getChildContainer();
5956     },
5957     
5958     getChildContainer : function()
5959     {
5960          
5961         return this.el.select('.roo-navbar-collapse',true).first();
5962          
5963         
5964     },
5965     
5966     initEvents : function()
5967     {
5968         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5969         
5970         if (this.autohide) {
5971             
5972             var prevScroll = 0;
5973             var ft = this.el;
5974             
5975             Roo.get(document).on('scroll',function(e) {
5976                 var ns = Roo.get(document).getScroll().top;
5977                 var os = prevScroll;
5978                 prevScroll = ns;
5979                 
5980                 if(ns > os){
5981                     ft.removeClass('slideDown');
5982                     ft.addClass('slideUp');
5983                     return;
5984                 }
5985                 ft.removeClass('slideUp');
5986                 ft.addClass('slideDown');
5987                  
5988               
5989           },this);
5990         }
5991     }    
5992     
5993 });
5994
5995
5996
5997  
5998
5999  /*
6000  * - LGPL
6001  *
6002  * navbar
6003  * 
6004  */
6005
6006 /**
6007  * @class Roo.bootstrap.NavSidebar
6008  * @extends Roo.bootstrap.Navbar
6009  * Bootstrap Sidebar class
6010  * 
6011  * @constructor
6012  * Create a new Sidebar
6013  * @param {Object} config The config object
6014  */
6015
6016
6017 Roo.bootstrap.NavSidebar = function(config){
6018     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6019 };
6020
6021 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6022     
6023     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6024     
6025     getAutoCreate : function(){
6026         
6027         
6028         return  {
6029             tag: 'div',
6030             cls: 'sidebar sidebar-nav'
6031         };
6032     
6033         
6034     }
6035     
6036     
6037     
6038 });
6039
6040
6041
6042  
6043
6044  /*
6045  * - LGPL
6046  *
6047  * nav group
6048  * 
6049  */
6050
6051 /**
6052  * @class Roo.bootstrap.NavGroup
6053  * @extends Roo.bootstrap.Component
6054  * Bootstrap NavGroup class
6055  * @cfg {String} align (left|right)
6056  * @cfg {Boolean} inverse
6057  * @cfg {String} type (nav|pills|tab) default nav
6058  * @cfg {String} navId - reference Id for navbar.
6059  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6060  * 
6061  * @constructor
6062  * Create a new nav group
6063  * @param {Object} config The config object
6064  */
6065
6066 Roo.bootstrap.NavGroup = function(config){
6067     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6068     this.navItems = [];
6069    
6070     Roo.bootstrap.NavGroup.register(this);
6071      this.addEvents({
6072         /**
6073              * @event changed
6074              * Fires when the active item changes
6075              * @param {Roo.bootstrap.NavGroup} this
6076              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6077              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6078          */
6079         'changed': true
6080      });
6081     
6082 };
6083
6084 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6085     
6086     align: '',
6087     inverse: false,
6088     form: false,
6089     type: 'nav',
6090     navId : '',
6091     // private
6092     pilltype : true,
6093     
6094     navItems : false, 
6095     
6096     getAutoCreate : function()
6097     {
6098         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6099         
6100         cfg = {
6101             tag : 'ul',
6102             cls: 'nav' 
6103         };
6104         if (Roo.bootstrap.version == 4) {
6105             if (['tabs','pills'].indexOf(this.type) != -1) {
6106                 cfg.cls += ' nav-' + this.type; 
6107             } else {
6108                 // trying to remove so header bar can right align top?
6109                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6110                     // do not use on header bar... 
6111                     cfg.cls += ' navbar-nav';
6112                 }
6113             }
6114             
6115         } else {
6116             if (['tabs','pills'].indexOf(this.type) != -1) {
6117                 cfg.cls += ' nav-' + this.type
6118             } else {
6119                 if (this.type !== 'nav') {
6120                     Roo.log('nav type must be nav/tabs/pills')
6121                 }
6122                 cfg.cls += ' navbar-nav'
6123             }
6124         }
6125         
6126         if (this.parent() && this.parent().sidebar) {
6127             cfg = {
6128                 tag: 'ul',
6129                 cls: 'dashboard-menu sidebar-menu'
6130             };
6131             
6132             return cfg;
6133         }
6134         
6135         if (this.form === true) {
6136             cfg = {
6137                 tag: 'form',
6138                 cls: 'navbar-form form-inline'
6139             };
6140             //nav navbar-right ml-md-auto
6141             if (this.align === 'right') {
6142                 cfg.cls += ' navbar-right ml-md-auto';
6143             } else {
6144                 cfg.cls += ' navbar-left';
6145             }
6146         }
6147         
6148         if (this.align === 'right') {
6149             cfg.cls += ' navbar-right ml-md-auto';
6150         } else {
6151             cfg.cls += ' mr-auto';
6152         }
6153         
6154         if (this.inverse) {
6155             cfg.cls += ' navbar-inverse';
6156             
6157         }
6158         
6159         
6160         return cfg;
6161     },
6162     /**
6163     * sets the active Navigation item
6164     * @param {Roo.bootstrap.NavItem} the new current navitem
6165     */
6166     setActiveItem : function(item)
6167     {
6168         var prev = false;
6169         Roo.each(this.navItems, function(v){
6170             if (v == item) {
6171                 return ;
6172             }
6173             if (v.isActive()) {
6174                 v.setActive(false, true);
6175                 prev = v;
6176                 
6177             }
6178             
6179         });
6180
6181         item.setActive(true, true);
6182         this.fireEvent('changed', this, item, prev);
6183         
6184         
6185     },
6186     /**
6187     * gets the active Navigation item
6188     * @return {Roo.bootstrap.NavItem} the current navitem
6189     */
6190     getActive : function()
6191     {
6192         
6193         var prev = false;
6194         Roo.each(this.navItems, function(v){
6195             
6196             if (v.isActive()) {
6197                 prev = v;
6198                 
6199             }
6200             
6201         });
6202         return prev;
6203     },
6204     
6205     indexOfNav : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v,i){
6210             
6211             if (v.isActive()) {
6212                 prev = i;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     /**
6220     * adds a Navigation item
6221     * @param {Roo.bootstrap.NavItem} the navitem to add
6222     */
6223     addItem : function(cfg)
6224     {
6225         if (this.form && Roo.bootstrap.version == 4) {
6226             cfg.tag = 'div';
6227         }
6228         var cn = new Roo.bootstrap.NavItem(cfg);
6229         this.register(cn);
6230         cn.parentId = this.id;
6231         cn.onRender(this.el, null);
6232         return cn;
6233     },
6234     /**
6235     * register a Navigation item
6236     * @param {Roo.bootstrap.NavItem} the navitem to add
6237     */
6238     register : function(item)
6239     {
6240         this.navItems.push( item);
6241         item.navId = this.navId;
6242     
6243     },
6244     
6245     /**
6246     * clear all the Navigation item
6247     */
6248    
6249     clearAll : function()
6250     {
6251         this.navItems = [];
6252         this.el.dom.innerHTML = '';
6253     },
6254     
6255     getNavItem: function(tabId)
6256     {
6257         var ret = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.tabId == tabId) {
6260                ret =  e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return ret;
6267     },
6268     
6269     setActiveNext : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i > this.navItems.length) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i+1]);
6276     },
6277     setActivePrev : function()
6278     {
6279         var i = this.indexOfNav(this.getActive());
6280         if (i  < 1) {
6281             return;
6282         }
6283         this.setActiveItem(this.navItems[i-1]);
6284     },
6285     clearWasActive : function(except) {
6286         Roo.each(this.navItems, function(e) {
6287             if (e.tabId != except.tabId && e.was_active) {
6288                e.was_active = false;
6289                return false;
6290             }
6291             return true;
6292             
6293         });
6294     },
6295     getWasActive : function ()
6296     {
6297         var r = false;
6298         Roo.each(this.navItems, function(e) {
6299             if (e.was_active) {
6300                r = e;
6301                return false;
6302             }
6303             return true;
6304             
6305         });
6306         return r;
6307     }
6308     
6309     
6310 });
6311
6312  
6313 Roo.apply(Roo.bootstrap.NavGroup, {
6314     
6315     groups: {},
6316      /**
6317     * register a Navigation Group
6318     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6319     */
6320     register : function(navgrp)
6321     {
6322         this.groups[navgrp.navId] = navgrp;
6323         
6324     },
6325     /**
6326     * fetch a Navigation Group based on the navigation ID
6327     * @param {string} the navgroup to add
6328     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6329     */
6330     get: function(navId) {
6331         if (typeof(this.groups[navId]) == 'undefined') {
6332             return false;
6333             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6334         }
6335         return this.groups[navId] ;
6336     }
6337     
6338     
6339     
6340 });
6341
6342  /*
6343  * - LGPL
6344  *
6345  * row
6346  * 
6347  */
6348
6349 /**
6350  * @class Roo.bootstrap.NavItem
6351  * @extends Roo.bootstrap.Component
6352  * Bootstrap Navbar.NavItem class
6353  * @cfg {String} href  link to
6354  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6355  * @cfg {Boolean} button_outline show and outlined button
6356  * @cfg {String} html content of button
6357  * @cfg {String} badge text inside badge
6358  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6359  * @cfg {String} glyphicon DEPRICATED - use fa
6360  * @cfg {String} icon DEPRICATED - use fa
6361  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6362  * @cfg {Boolean} active Is item active
6363  * @cfg {Boolean} disabled Is item disabled
6364  * @cfg {String} linkcls  Link Class
6365  * @cfg {Boolean} preventDefault (true | false) default false
6366  * @cfg {String} tabId the tab that this item activates.
6367  * @cfg {String} tagtype (a|span) render as a href or span?
6368  * @cfg {Boolean} animateRef (true|false) link to element default false  
6369   
6370  * @constructor
6371  * Create a new Navbar Item
6372  * @param {Object} config The config object
6373  */
6374 Roo.bootstrap.NavItem = function(config){
6375     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6376     this.addEvents({
6377         // raw events
6378         /**
6379          * @event click
6380          * The raw click event for the entire grid.
6381          * @param {Roo.EventObject} e
6382          */
6383         "click" : true,
6384          /**
6385             * @event changed
6386             * Fires when the active item active state changes
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {boolean} state the new state
6389              
6390          */
6391         'changed': true,
6392         /**
6393             * @event scrollto
6394             * Fires when scroll to element
6395             * @param {Roo.bootstrap.NavItem} this
6396             * @param {Object} options
6397             * @param {Roo.EventObject} e
6398              
6399          */
6400         'scrollto': true
6401     });
6402    
6403 };
6404
6405 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6406     
6407     href: false,
6408     html: '',
6409     badge: '',
6410     icon: false,
6411     fa : false,
6412     glyphicon: false,
6413     active: false,
6414     preventDefault : false,
6415     tabId : false,
6416     tagtype : 'a',
6417     tag: 'li',
6418     disabled : false,
6419     animateRef : false,
6420     was_active : false,
6421     button_weight : '',
6422     button_outline : false,
6423     linkcls : '',
6424     navLink: false,
6425     
6426     getAutoCreate : function(){
6427          
6428         var cfg = {
6429             tag: this.tag,
6430             cls: 'nav-item'
6431         };
6432         
6433         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6434         
6435         if (this.active) {
6436             cfg.cls +=  ' active' ;
6437         }
6438         if (this.disabled) {
6439             cfg.cls += ' disabled';
6440         }
6441         
6442         // BS4 only?
6443         if (this.button_weight.length) {
6444             cfg.tag = this.href ? 'a' : 'button';
6445             cfg.html = this.html || '';
6446             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6447             if (this.href) {
6448                 cfg.href = this.href;
6449             }
6450             if (this.fa) {
6451                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6452             } else {
6453                 cfg.cls += " nav-html";
6454             }
6455             
6456             // menu .. should add dropdown-menu class - so no need for carat..
6457             
6458             if (this.badge !== '') {
6459                  
6460                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6461             }
6462             return cfg;
6463         }
6464         
6465         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6466             cfg.cn = [
6467                 {
6468                     tag: this.tagtype,
6469                     href : this.href || "#",
6470                     html: this.html || '',
6471                     cls : ''
6472                 }
6473             ];
6474             if (this.tagtype == 'a') {
6475                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6476         
6477             }
6478             if (this.icon) {
6479                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6480             } else  if (this.fa) {
6481                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6482             } else if(this.glyphicon) {
6483                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6484             } else {
6485                 cfg.cn[0].cls += " nav-html";
6486             }
6487             
6488             if (this.menu) {
6489                 cfg.cn[0].html += " <span class='caret'></span>";
6490              
6491             }
6492             
6493             if (this.badge !== '') {
6494                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6495             }
6496         }
6497         
6498         
6499         
6500         return cfg;
6501     },
6502     onRender : function(ct, position)
6503     {
6504        // Roo.log("Call onRender: " + this.xtype);
6505         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6506             this.tag = 'div';
6507         }
6508         
6509         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6510         this.navLink = this.el.select('.nav-link',true).first();
6511         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6512         return ret;
6513     },
6514       
6515     
6516     initEvents: function() 
6517     {
6518         if (typeof (this.menu) != 'undefined') {
6519             this.menu.parentType = this.xtype;
6520             this.menu.triggerEl = this.el;
6521             this.menu = this.addxtype(Roo.apply({}, this.menu));
6522         }
6523         
6524         this.el.on('click', this.onClick, this);
6525         
6526         //if(this.tagtype == 'span'){
6527         //    this.el.select('span',true).on('click', this.onClick, this);
6528         //}
6529        
6530         // at this point parent should be available..
6531         this.parent().register(this);
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if (e.getTarget('.dropdown-menu-item')) {
6537             // did you click on a menu itemm.... - then don't trigger onclick..
6538             return;
6539         }
6540         
6541         if(
6542                 this.preventDefault || 
6543                 this.href == '#' 
6544         ){
6545             Roo.log("NavItem - prevent Default?");
6546             e.preventDefault();
6547         }
6548         
6549         if (this.disabled) {
6550             return;
6551         }
6552         
6553         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6554         if (tg && tg.transition) {
6555             Roo.log("waiting for the transitionend");
6556             return;
6557         }
6558         
6559         
6560         
6561         //Roo.log("fire event clicked");
6562         if(this.fireEvent('click', this, e) === false){
6563             return;
6564         };
6565         
6566         if(this.tagtype == 'span'){
6567             return;
6568         }
6569         
6570         //Roo.log(this.href);
6571         var ael = this.el.select('a',true).first();
6572         //Roo.log(ael);
6573         
6574         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6575             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6576             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6577                 return; // ignore... - it's a 'hash' to another page.
6578             }
6579             Roo.log("NavItem - prevent Default?");
6580             e.preventDefault();
6581             this.scrollToElement(e);
6582         }
6583         
6584         
6585         var p =  this.parent();
6586    
6587         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6588             if (typeof(p.setActiveItem) !== 'undefined') {
6589                 p.setActiveItem(this);
6590             }
6591         }
6592         
6593         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6594         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6595             // remove the collapsed menu expand...
6596             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6597         }
6598     },
6599     
6600     isActive: function () {
6601         return this.active
6602     },
6603     setActive : function(state, fire, is_was_active)
6604     {
6605         if (this.active && !state && this.navId) {
6606             this.was_active = true;
6607             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608             if (nv) {
6609                 nv.clearWasActive(this);
6610             }
6611             
6612         }
6613         this.active = state;
6614         
6615         if (!state ) {
6616             this.el.removeClass('active');
6617             this.navLink ? this.navLink.removeClass('active') : false;
6618         } else if (!this.el.hasClass('active')) {
6619             
6620             this.el.addClass('active');
6621             if (Roo.bootstrap.version == 4 && this.navLink ) {
6622                 this.navLink.addClass('active');
6623             }
6624             
6625         }
6626         if (fire) {
6627             this.fireEvent('changed', this, state);
6628         }
6629         
6630         // show a panel if it's registered and related..
6631         
6632         if (!this.navId || !this.tabId || !state || is_was_active) {
6633             return;
6634         }
6635         
6636         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6637         if (!tg) {
6638             return;
6639         }
6640         var pan = tg.getPanelByName(this.tabId);
6641         if (!pan) {
6642             return;
6643         }
6644         // if we can not flip to new panel - go back to old nav highlight..
6645         if (false == tg.showPanel(pan)) {
6646             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6647             if (nv) {
6648                 var onav = nv.getWasActive();
6649                 if (onav) {
6650                     onav.setActive(true, false, true);
6651                 }
6652             }
6653             
6654         }
6655         
6656         
6657         
6658     },
6659      // this should not be here...
6660     setDisabled : function(state)
6661     {
6662         this.disabled = state;
6663         if (!state ) {
6664             this.el.removeClass('disabled');
6665         } else if (!this.el.hasClass('disabled')) {
6666             this.el.addClass('disabled');
6667         }
6668         
6669     },
6670     
6671     /**
6672      * Fetch the element to display the tooltip on.
6673      * @return {Roo.Element} defaults to this.el
6674      */
6675     tooltipEl : function()
6676     {
6677         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6678     },
6679     
6680     scrollToElement : function(e)
6681     {
6682         var c = document.body;
6683         
6684         /*
6685          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6686          */
6687         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6688             c = document.documentElement;
6689         }
6690         
6691         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6692         
6693         if(!target){
6694             return;
6695         }
6696
6697         var o = target.calcOffsetsTo(c);
6698         
6699         var options = {
6700             target : target,
6701             value : o[1]
6702         };
6703         
6704         this.fireEvent('scrollto', this, options, e);
6705         
6706         Roo.get(c).scrollTo('top', options.value, true);
6707         
6708         return;
6709     },
6710     /**
6711      * Set the HTML (text content) of the item
6712      * @param {string} html  content for the nav item
6713      */
6714     setHtml : function(html)
6715     {
6716         this.html = html;
6717         this.htmlEl.dom.innerHTML = html;
6718         
6719     } 
6720 });
6721  
6722
6723  /*
6724  * - LGPL
6725  *
6726  * sidebar item
6727  *
6728  *  li
6729  *    <span> icon </span>
6730  *    <span> text </span>
6731  *    <span>badge </span>
6732  */
6733
6734 /**
6735  * @class Roo.bootstrap.NavSidebarItem
6736  * @extends Roo.bootstrap.NavItem
6737  * Bootstrap Navbar.NavSidebarItem class
6738  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6739  * {Boolean} open is the menu open
6740  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6741  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6742  * {String} buttonSize (sm|md|lg)the extra classes for the button
6743  * {Boolean} showArrow show arrow next to the text (default true)
6744  * @constructor
6745  * Create a new Navbar Button
6746  * @param {Object} config The config object
6747  */
6748 Roo.bootstrap.NavSidebarItem = function(config){
6749     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6750     this.addEvents({
6751         // raw events
6752         /**
6753          * @event click
6754          * The raw click event for the entire grid.
6755          * @param {Roo.EventObject} e
6756          */
6757         "click" : true,
6758          /**
6759             * @event changed
6760             * Fires when the active item active state changes
6761             * @param {Roo.bootstrap.NavSidebarItem} this
6762             * @param {boolean} state the new state
6763              
6764          */
6765         'changed': true
6766     });
6767    
6768 };
6769
6770 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6771     
6772     badgeWeight : 'default',
6773     
6774     open: false,
6775     
6776     buttonView : false,
6777     
6778     buttonWeight : 'default',
6779     
6780     buttonSize : 'md',
6781     
6782     showArrow : true,
6783     
6784     getAutoCreate : function(){
6785         
6786         
6787         var a = {
6788                 tag: 'a',
6789                 href : this.href || '#',
6790                 cls: '',
6791                 html : '',
6792                 cn : []
6793         };
6794         
6795         if(this.buttonView){
6796             a = {
6797                 tag: 'button',
6798                 href : this.href || '#',
6799                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6800                 html : this.html,
6801                 cn : []
6802             };
6803         }
6804         
6805         var cfg = {
6806             tag: 'li',
6807             cls: '',
6808             cn: [ a ]
6809         };
6810         
6811         if (this.active) {
6812             cfg.cls += ' active';
6813         }
6814         
6815         if (this.disabled) {
6816             cfg.cls += ' disabled';
6817         }
6818         if (this.open) {
6819             cfg.cls += ' open x-open';
6820         }
6821         // left icon..
6822         if (this.glyphicon || this.icon) {
6823             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6824             a.cn.push({ tag : 'i', cls : c }) ;
6825         }
6826         
6827         if(!this.buttonView){
6828             var span = {
6829                 tag: 'span',
6830                 html : this.html || ''
6831             };
6832
6833             a.cn.push(span);
6834             
6835         }
6836         
6837         if (this.badge !== '') {
6838             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6839         }
6840         
6841         if (this.menu) {
6842             
6843             if(this.showArrow){
6844                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6845             }
6846             
6847             a.cls += ' dropdown-toggle treeview' ;
6848         }
6849         
6850         return cfg;
6851     },
6852     
6853     initEvents : function()
6854     { 
6855         if (typeof (this.menu) != 'undefined') {
6856             this.menu.parentType = this.xtype;
6857             this.menu.triggerEl = this.el;
6858             this.menu = this.addxtype(Roo.apply({}, this.menu));
6859         }
6860         
6861         this.el.on('click', this.onClick, this);
6862         
6863         if(this.badge !== ''){
6864             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6865         }
6866         
6867     },
6868     
6869     onClick : function(e)
6870     {
6871         if(this.disabled){
6872             e.preventDefault();
6873             return;
6874         }
6875         
6876         if(this.preventDefault){
6877             e.preventDefault();
6878         }
6879         
6880         this.fireEvent('click', this, e);
6881     },
6882     
6883     disable : function()
6884     {
6885         this.setDisabled(true);
6886     },
6887     
6888     enable : function()
6889     {
6890         this.setDisabled(false);
6891     },
6892     
6893     setDisabled : function(state)
6894     {
6895         if(this.disabled == state){
6896             return;
6897         }
6898         
6899         this.disabled = state;
6900         
6901         if (state) {
6902             this.el.addClass('disabled');
6903             return;
6904         }
6905         
6906         this.el.removeClass('disabled');
6907         
6908         return;
6909     },
6910     
6911     setActive : function(state)
6912     {
6913         if(this.active == state){
6914             return;
6915         }
6916         
6917         this.active = state;
6918         
6919         if (state) {
6920             this.el.addClass('active');
6921             return;
6922         }
6923         
6924         this.el.removeClass('active');
6925         
6926         return;
6927     },
6928     
6929     isActive: function () 
6930     {
6931         return this.active;
6932     },
6933     
6934     setBadge : function(str)
6935     {
6936         if(!this.badgeEl){
6937             return;
6938         }
6939         
6940         this.badgeEl.dom.innerHTML = str;
6941     }
6942     
6943    
6944      
6945  
6946 });
6947  
6948
6949  /*
6950  * - LGPL
6951  *
6952  *  Breadcrumb Nav
6953  * 
6954  */
6955 Roo.namespace('Roo.bootstrap.breadcrumb');
6956
6957
6958 /**
6959  * @class Roo.bootstrap.breadcrumb.Nav
6960  * @extends Roo.bootstrap.Component
6961  * Bootstrap Breadcrumb Nav Class
6962  *  
6963  * @children Roo.bootstrap.breadcrumb.Item
6964  * 
6965  * @constructor
6966  * Create a new breadcrumb.Nav
6967  * @param {Object} config The config object
6968  */
6969
6970
6971 Roo.bootstrap.breadcrumb.Nav = function(config){
6972     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6973     
6974     
6975 };
6976
6977 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6978     
6979     getAutoCreate : function()
6980     {
6981
6982         var cfg = {
6983             tag: 'nav',
6984             cn : [
6985                 {
6986                     tag : 'ol',
6987                     cls : 'breadcrumb'
6988                 }
6989             ]
6990             
6991         };
6992           
6993         return cfg;
6994     },
6995     
6996     initEvents: function()
6997     {
6998         this.olEl = this.el.select('ol',true).first();    
6999     },
7000     getChildContainer : function()
7001     {
7002         return this.olEl;  
7003     }
7004     
7005 });
7006
7007  /*
7008  * - LGPL
7009  *
7010  *  Breadcrumb Item
7011  * 
7012  */
7013
7014
7015 /**
7016  * @class Roo.bootstrap.breadcrumb.Nav
7017  * @extends Roo.bootstrap.Component
7018  * Bootstrap Breadcrumb Nav Class
7019  *  
7020  * @children Roo.bootstrap.breadcrumb.Component
7021  * @cfg {String} html the content of the link.
7022  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7023  * @cfg {Boolean} active is it active
7024
7025  * 
7026  * @constructor
7027  * Create a new breadcrumb.Nav
7028  * @param {Object} config The config object
7029  */
7030
7031 Roo.bootstrap.breadcrumb.Item = function(config){
7032     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7033     this.addEvents({
7034         // img events
7035         /**
7036          * @event click
7037          * The img click event for the img.
7038          * @param {Roo.EventObject} e
7039          */
7040         "click" : true
7041     });
7042     
7043 };
7044
7045 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7046     
7047     href: false,
7048     html : '',
7049     
7050     getAutoCreate : function()
7051     {
7052
7053         var cfg = {
7054             tag: 'li',
7055             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7056         };
7057         if (this.href !== false) {
7058             cfg.cn = [{
7059                 tag : 'a',
7060                 href : this.href,
7061                 html : this.html
7062             }];
7063         } else {
7064             cfg.html = this.html;
7065         }
7066         
7067         return cfg;
7068     },
7069     
7070     initEvents: function()
7071     {
7072         if (this.href) {
7073             this.el.select('a', true).first().on('click',this.onClick, this)
7074         }
7075         
7076     },
7077     onClick : function(e)
7078     {
7079         e.preventDefault();
7080         this.fireEvent('click',this,  e);
7081     }
7082     
7083 });
7084
7085  /*
7086  * - LGPL
7087  *
7088  * row
7089  * 
7090  */
7091
7092 /**
7093  * @class Roo.bootstrap.Row
7094  * @extends Roo.bootstrap.Component
7095  * Bootstrap Row class (contains columns...)
7096  * 
7097  * @constructor
7098  * Create a new Row
7099  * @param {Object} config The config object
7100  */
7101
7102 Roo.bootstrap.Row = function(config){
7103     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7104 };
7105
7106 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7107     
7108     getAutoCreate : function(){
7109        return {
7110             cls: 'row clearfix'
7111        };
7112     }
7113     
7114     
7115 });
7116
7117  
7118
7119  /*
7120  * - LGPL
7121  *
7122  * pagination
7123  * 
7124  */
7125
7126 /**
7127  * @class Roo.bootstrap.Pagination
7128  * @extends Roo.bootstrap.Component
7129  * Bootstrap Pagination class
7130  * @cfg {String} size xs | sm | md | lg
7131  * @cfg {Boolean} inverse false | true
7132  * 
7133  * @constructor
7134  * Create a new Pagination
7135  * @param {Object} config The config object
7136  */
7137
7138 Roo.bootstrap.Pagination = function(config){
7139     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7140 };
7141
7142 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7143     
7144     cls: false,
7145     size: false,
7146     inverse: false,
7147     
7148     getAutoCreate : function(){
7149         var cfg = {
7150             tag: 'ul',
7151                 cls: 'pagination'
7152         };
7153         if (this.inverse) {
7154             cfg.cls += ' inverse';
7155         }
7156         if (this.html) {
7157             cfg.html=this.html;
7158         }
7159         if (this.cls) {
7160             cfg.cls += " " + this.cls;
7161         }
7162         return cfg;
7163     }
7164    
7165 });
7166
7167  
7168
7169  /*
7170  * - LGPL
7171  *
7172  * Pagination item
7173  * 
7174  */
7175
7176
7177 /**
7178  * @class Roo.bootstrap.PaginationItem
7179  * @extends Roo.bootstrap.Component
7180  * Bootstrap PaginationItem class
7181  * @cfg {String} html text
7182  * @cfg {String} href the link
7183  * @cfg {Boolean} preventDefault (true | false) default true
7184  * @cfg {Boolean} active (true | false) default false
7185  * @cfg {Boolean} disabled default false
7186  * 
7187  * 
7188  * @constructor
7189  * Create a new PaginationItem
7190  * @param {Object} config The config object
7191  */
7192
7193
7194 Roo.bootstrap.PaginationItem = function(config){
7195     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7196     this.addEvents({
7197         // raw events
7198         /**
7199          * @event click
7200          * The raw click event for the entire grid.
7201          * @param {Roo.EventObject} e
7202          */
7203         "click" : true
7204     });
7205 };
7206
7207 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7208     
7209     href : false,
7210     html : false,
7211     preventDefault: true,
7212     active : false,
7213     cls : false,
7214     disabled: false,
7215     
7216     getAutoCreate : function(){
7217         var cfg= {
7218             tag: 'li',
7219             cn: [
7220                 {
7221                     tag : 'a',
7222                     href : this.href ? this.href : '#',
7223                     html : this.html ? this.html : ''
7224                 }
7225             ]
7226         };
7227         
7228         if(this.cls){
7229             cfg.cls = this.cls;
7230         }
7231         
7232         if(this.disabled){
7233             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7234         }
7235         
7236         if(this.active){
7237             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7238         }
7239         
7240         return cfg;
7241     },
7242     
7243     initEvents: function() {
7244         
7245         this.el.on('click', this.onClick, this);
7246         
7247     },
7248     onClick : function(e)
7249     {
7250         Roo.log('PaginationItem on click ');
7251         if(this.preventDefault){
7252             e.preventDefault();
7253         }
7254         
7255         if(this.disabled){
7256             return;
7257         }
7258         
7259         this.fireEvent('click', this, e);
7260     }
7261    
7262 });
7263
7264  
7265
7266  /*
7267  * - LGPL
7268  *
7269  * slider
7270  * 
7271  */
7272
7273
7274 /**
7275  * @class Roo.bootstrap.Slider
7276  * @extends Roo.bootstrap.Component
7277  * Bootstrap Slider class
7278  *    
7279  * @constructor
7280  * Create a new Slider
7281  * @param {Object} config The config object
7282  */
7283
7284 Roo.bootstrap.Slider = function(config){
7285     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7286 };
7287
7288 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7289     
7290     getAutoCreate : function(){
7291         
7292         var cfg = {
7293             tag: 'div',
7294             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7295             cn: [
7296                 {
7297                     tag: 'a',
7298                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7299                 }
7300             ]
7301         };
7302         
7303         return cfg;
7304     }
7305    
7306 });
7307
7308  /*
7309  * Based on:
7310  * Ext JS Library 1.1.1
7311  * Copyright(c) 2006-2007, Ext JS, LLC.
7312  *
7313  * Originally Released Under LGPL - original licence link has changed is not relivant.
7314  *
7315  * Fork - LGPL
7316  * <script type="text/javascript">
7317  */
7318  /**
7319  * @extends Roo.dd.DDProxy
7320  * @class Roo.grid.SplitDragZone
7321  * Support for Column Header resizing
7322  * @constructor
7323  * @param {Object} config
7324  */
7325 // private
7326 // This is a support class used internally by the Grid components
7327 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7328     this.grid = grid;
7329     this.view = grid.getView();
7330     this.proxy = this.view.resizeProxy;
7331     Roo.grid.SplitDragZone.superclass.constructor.call(
7332         this,
7333         hd, // ID
7334         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7335         {  // CONFIG
7336             dragElId : Roo.id(this.proxy.dom),
7337             resizeFrame:false
7338         }
7339     );
7340     
7341     this.setHandleElId(Roo.id(hd));
7342     if (hd2 !== false) {
7343         this.setOuterHandleElId(Roo.id(hd2));
7344     }
7345     
7346     this.scroll = false;
7347 };
7348 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7349     fly: Roo.Element.fly,
7350
7351     b4StartDrag : function(x, y){
7352         this.view.headersDisabled = true;
7353         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7354                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7355         );
7356         this.proxy.setHeight(h);
7357         
7358         // for old system colWidth really stored the actual width?
7359         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7360         // which in reality did not work.. - it worked only for fixed sizes
7361         // for resizable we need to use actual sizes.
7362         var w = this.cm.getColumnWidth(this.cellIndex);
7363         if (!this.view.mainWrap) {
7364             // bootstrap.
7365             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7366         }
7367         
7368         
7369         
7370         // this was w-this.grid.minColumnWidth;
7371         // doesnt really make sense? - w = thie curren width or the rendered one?
7372         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7373         this.resetConstraints();
7374         this.setXConstraint(minw, 1000);
7375         this.setYConstraint(0, 0);
7376         this.minX = x - minw;
7377         this.maxX = x + 1000;
7378         this.startPos = x;
7379         if (!this.view.mainWrap) { // this is Bootstrap code..
7380             this.getDragEl().style.display='block';
7381         }
7382         
7383         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7384     },
7385
7386
7387     handleMouseDown : function(e){
7388         ev = Roo.EventObject.setEvent(e);
7389         var t = this.fly(ev.getTarget());
7390         if(t.hasClass("x-grid-split")){
7391             this.cellIndex = this.view.getCellIndex(t.dom);
7392             this.split = t.dom;
7393             this.cm = this.grid.colModel;
7394             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7395                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7396             }
7397         }
7398     },
7399
7400     endDrag : function(e){
7401         this.view.headersDisabled = false;
7402         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7403         var diff = endX - this.startPos;
7404         // 
7405         var w = this.cm.getColumnWidth(this.cellIndex);
7406         if (!this.view.mainWrap) {
7407             w = 0;
7408         }
7409         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7410     },
7411
7412     autoOffset : function(){
7413         this.setDelta(0,0);
7414     }
7415 });/*
7416  * Based on:
7417  * Ext JS Library 1.1.1
7418  * Copyright(c) 2006-2007, Ext JS, LLC.
7419  *
7420  * Originally Released Under LGPL - original licence link has changed is not relivant.
7421  *
7422  * Fork - LGPL
7423  * <script type="text/javascript">
7424  */
7425
7426 /**
7427  * @class Roo.grid.AbstractSelectionModel
7428  * @extends Roo.util.Observable
7429  * @abstract
7430  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7431  * implemented by descendant classes.  This class should not be directly instantiated.
7432  * @constructor
7433  */
7434 Roo.grid.AbstractSelectionModel = function(){
7435     this.locked = false;
7436     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7437 };
7438
7439 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7440     /** @ignore Called by the grid automatically. Do not call directly. */
7441     init : function(grid){
7442         this.grid = grid;
7443         this.initEvents();
7444     },
7445
7446     /**
7447      * Locks the selections.
7448      */
7449     lock : function(){
7450         this.locked = true;
7451     },
7452
7453     /**
7454      * Unlocks the selections.
7455      */
7456     unlock : function(){
7457         this.locked = false;
7458     },
7459
7460     /**
7461      * Returns true if the selections are locked.
7462      * @return {Boolean}
7463      */
7464     isLocked : function(){
7465         return this.locked;
7466     }
7467 });/*
7468  * Based on:
7469  * Ext JS Library 1.1.1
7470  * Copyright(c) 2006-2007, Ext JS, LLC.
7471  *
7472  * Originally Released Under LGPL - original licence link has changed is not relivant.
7473  *
7474  * Fork - LGPL
7475  * <script type="text/javascript">
7476  */
7477 /**
7478  * @extends Roo.grid.AbstractSelectionModel
7479  * @class Roo.grid.RowSelectionModel
7480  * The default SelectionModel used by {@link Roo.grid.Grid}.
7481  * It supports multiple selections and keyboard selection/navigation. 
7482  * @constructor
7483  * @param {Object} config
7484  */
7485 Roo.grid.RowSelectionModel = function(config){
7486     Roo.apply(this, config);
7487     this.selections = new Roo.util.MixedCollection(false, function(o){
7488         return o.id;
7489     });
7490
7491     this.last = false;
7492     this.lastActive = false;
7493
7494     this.addEvents({
7495         /**
7496         * @event selectionchange
7497         * Fires when the selection changes
7498         * @param {SelectionModel} this
7499         */
7500        "selectionchange" : true,
7501        /**
7502         * @event afterselectionchange
7503         * Fires after the selection changes (eg. by key press or clicking)
7504         * @param {SelectionModel} this
7505         */
7506        "afterselectionchange" : true,
7507        /**
7508         * @event beforerowselect
7509         * Fires when a row is selected being selected, return false to cancel.
7510         * @param {SelectionModel} this
7511         * @param {Number} rowIndex The selected index
7512         * @param {Boolean} keepExisting False if other selections will be cleared
7513         */
7514        "beforerowselect" : true,
7515        /**
7516         * @event rowselect
7517         * Fires when a row is selected.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         * @param {Roo.data.Record} r The record
7521         */
7522        "rowselect" : true,
7523        /**
7524         * @event rowdeselect
7525         * Fires when a row is deselected.
7526         * @param {SelectionModel} this
7527         * @param {Number} rowIndex The selected index
7528         */
7529         "rowdeselect" : true
7530     });
7531     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7532     this.locked = false;
7533 };
7534
7535 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7536     /**
7537      * @cfg {Boolean} singleSelect
7538      * True to allow selection of only one row at a time (defaults to false)
7539      */
7540     singleSelect : false,
7541
7542     // private
7543     initEvents : function(){
7544
7545         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7546             this.grid.on("mousedown", this.handleMouseDown, this);
7547         }else{ // allow click to work like normal
7548             this.grid.on("rowclick", this.handleDragableRowClick, this);
7549         }
7550         // bootstrap does not have a view..
7551         var view = this.grid.view ? this.grid.view : this.grid;
7552         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7553             "up" : function(e){
7554                 if(!e.shiftKey){
7555                     this.selectPrevious(e.shiftKey);
7556                 }else if(this.last !== false && this.lastActive !== false){
7557                     var last = this.last;
7558                     this.selectRange(this.last,  this.lastActive-1);
7559                     view.focusRow(this.lastActive);
7560                     if(last !== false){
7561                         this.last = last;
7562                     }
7563                 }else{
7564                     this.selectFirstRow();
7565                 }
7566                 this.fireEvent("afterselectionchange", this);
7567             },
7568             "down" : function(e){
7569                 if(!e.shiftKey){
7570                     this.selectNext(e.shiftKey);
7571                 }else if(this.last !== false && this.lastActive !== false){
7572                     var last = this.last;
7573                     this.selectRange(this.last,  this.lastActive+1);
7574                     view.focusRow(this.lastActive);
7575                     if(last !== false){
7576                         this.last = last;
7577                     }
7578                 }else{
7579                     this.selectFirstRow();
7580                 }
7581                 this.fireEvent("afterselectionchange", this);
7582             },
7583             scope: this
7584         });
7585
7586          
7587         view.on("refresh", this.onRefresh, this);
7588         view.on("rowupdated", this.onRowUpdated, this);
7589         view.on("rowremoved", this.onRemove, this);
7590     },
7591
7592     // private
7593     onRefresh : function(){
7594         var ds = this.grid.ds, i, v = this.grid.view;
7595         var s = this.selections;
7596         s.each(function(r){
7597             if((i = ds.indexOfId(r.id)) != -1){
7598                 v.onRowSelect(i);
7599                 s.add(ds.getAt(i)); // updating the selection relate data
7600             }else{
7601                 s.remove(r);
7602             }
7603         });
7604     },
7605
7606     // private
7607     onRemove : function(v, index, r){
7608         this.selections.remove(r);
7609     },
7610
7611     // private
7612     onRowUpdated : function(v, index, r){
7613         if(this.isSelected(r)){
7614             v.onRowSelect(index);
7615         }
7616     },
7617
7618     /**
7619      * Select records.
7620      * @param {Array} records The records to select
7621      * @param {Boolean} keepExisting (optional) True to keep existing selections
7622      */
7623     selectRecords : function(records, keepExisting){
7624         if(!keepExisting){
7625             this.clearSelections();
7626         }
7627         var ds = this.grid.ds;
7628         for(var i = 0, len = records.length; i < len; i++){
7629             this.selectRow(ds.indexOf(records[i]), true);
7630         }
7631     },
7632
7633     /**
7634      * Gets the number of selected rows.
7635      * @return {Number}
7636      */
7637     getCount : function(){
7638         return this.selections.length;
7639     },
7640
7641     /**
7642      * Selects the first row in the grid.
7643      */
7644     selectFirstRow : function(){
7645         this.selectRow(0);
7646     },
7647
7648     /**
7649      * Select the last row.
7650      * @param {Boolean} keepExisting (optional) True to keep existing selections
7651      */
7652     selectLastRow : function(keepExisting){
7653         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7654     },
7655
7656     /**
7657      * Selects the row immediately following the last selected row.
7658      * @param {Boolean} keepExisting (optional) True to keep existing selections
7659      */
7660     selectNext : function(keepExisting){
7661         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7662             this.selectRow(this.last+1, keepExisting);
7663             var view = this.grid.view ? this.grid.view : this.grid;
7664             view.focusRow(this.last);
7665         }
7666     },
7667
7668     /**
7669      * Selects the row that precedes the last selected row.
7670      * @param {Boolean} keepExisting (optional) True to keep existing selections
7671      */
7672     selectPrevious : function(keepExisting){
7673         if(this.last){
7674             this.selectRow(this.last-1, keepExisting);
7675             var view = this.grid.view ? this.grid.view : this.grid;
7676             view.focusRow(this.last);
7677         }
7678     },
7679
7680     /**
7681      * Returns the selected records
7682      * @return {Array} Array of selected records
7683      */
7684     getSelections : function(){
7685         return [].concat(this.selections.items);
7686     },
7687
7688     /**
7689      * Returns the first selected record.
7690      * @return {Record}
7691      */
7692     getSelected : function(){
7693         return this.selections.itemAt(0);
7694     },
7695
7696
7697     /**
7698      * Clears all selections.
7699      */
7700     clearSelections : function(fast){
7701         if(this.locked) {
7702             return;
7703         }
7704         if(fast !== true){
7705             var ds = this.grid.ds;
7706             var s = this.selections;
7707             s.each(function(r){
7708                 this.deselectRow(ds.indexOfId(r.id));
7709             }, this);
7710             s.clear();
7711         }else{
7712             this.selections.clear();
7713         }
7714         this.last = false;
7715     },
7716
7717
7718     /**
7719      * Selects all rows.
7720      */
7721     selectAll : function(){
7722         if(this.locked) {
7723             return;
7724         }
7725         this.selections.clear();
7726         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7727             this.selectRow(i, true);
7728         }
7729     },
7730
7731     /**
7732      * Returns True if there is a selection.
7733      * @return {Boolean}
7734      */
7735     hasSelection : function(){
7736         return this.selections.length > 0;
7737     },
7738
7739     /**
7740      * Returns True if the specified row is selected.
7741      * @param {Number/Record} record The record or index of the record to check
7742      * @return {Boolean}
7743      */
7744     isSelected : function(index){
7745         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7746         return (r && this.selections.key(r.id) ? true : false);
7747     },
7748
7749     /**
7750      * Returns True if the specified record id is selected.
7751      * @param {String} id The id of record to check
7752      * @return {Boolean}
7753      */
7754     isIdSelected : function(id){
7755         return (this.selections.key(id) ? true : false);
7756     },
7757
7758     // private
7759     handleMouseDown : function(e, t)
7760     {
7761         var view = this.grid.view ? this.grid.view : this.grid;
7762         var rowIndex;
7763         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7764             return;
7765         };
7766         if(e.shiftKey && this.last !== false){
7767             var last = this.last;
7768             this.selectRange(last, rowIndex, e.ctrlKey);
7769             this.last = last; // reset the last
7770             view.focusRow(rowIndex);
7771         }else{
7772             var isSelected = this.isSelected(rowIndex);
7773             if(e.button !== 0 && isSelected){
7774                 view.focusRow(rowIndex);
7775             }else if(e.ctrlKey && isSelected){
7776                 this.deselectRow(rowIndex);
7777             }else if(!isSelected){
7778                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7779                 view.focusRow(rowIndex);
7780             }
7781         }
7782         this.fireEvent("afterselectionchange", this);
7783     },
7784     // private
7785     handleDragableRowClick :  function(grid, rowIndex, e) 
7786     {
7787         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7788             this.selectRow(rowIndex, false);
7789             var view = this.grid.view ? this.grid.view : this.grid;
7790             view.focusRow(rowIndex);
7791              this.fireEvent("afterselectionchange", this);
7792         }
7793     },
7794     
7795     /**
7796      * Selects multiple rows.
7797      * @param {Array} rows Array of the indexes of the row to select
7798      * @param {Boolean} keepExisting (optional) True to keep existing selections
7799      */
7800     selectRows : function(rows, keepExisting){
7801         if(!keepExisting){
7802             this.clearSelections();
7803         }
7804         for(var i = 0, len = rows.length; i < len; i++){
7805             this.selectRow(rows[i], true);
7806         }
7807     },
7808
7809     /**
7810      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7811      * @param {Number} startRow The index of the first row in the range
7812      * @param {Number} endRow The index of the last row in the range
7813      * @param {Boolean} keepExisting (optional) True to retain existing selections
7814      */
7815     selectRange : function(startRow, endRow, keepExisting){
7816         if(this.locked) {
7817             return;
7818         }
7819         if(!keepExisting){
7820             this.clearSelections();
7821         }
7822         if(startRow <= endRow){
7823             for(var i = startRow; i <= endRow; i++){
7824                 this.selectRow(i, true);
7825             }
7826         }else{
7827             for(var i = startRow; i >= endRow; i--){
7828                 this.selectRow(i, true);
7829             }
7830         }
7831     },
7832
7833     /**
7834      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7835      * @param {Number} startRow The index of the first row in the range
7836      * @param {Number} endRow The index of the last row in the range
7837      */
7838     deselectRange : function(startRow, endRow, preventViewNotify){
7839         if(this.locked) {
7840             return;
7841         }
7842         for(var i = startRow; i <= endRow; i++){
7843             this.deselectRow(i, preventViewNotify);
7844         }
7845     },
7846
7847     /**
7848      * Selects a row.
7849      * @param {Number} row The index of the row to select
7850      * @param {Boolean} keepExisting (optional) True to keep existing selections
7851      */
7852     selectRow : function(index, keepExisting, preventViewNotify){
7853         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7854             return;
7855         }
7856         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7857             if(!keepExisting || this.singleSelect){
7858                 this.clearSelections();
7859             }
7860             var r = this.grid.ds.getAt(index);
7861             this.selections.add(r);
7862             this.last = this.lastActive = index;
7863             if(!preventViewNotify){
7864                 var view = this.grid.view ? this.grid.view : this.grid;
7865                 view.onRowSelect(index);
7866             }
7867             this.fireEvent("rowselect", this, index, r);
7868             this.fireEvent("selectionchange", this);
7869         }
7870     },
7871
7872     /**
7873      * Deselects a row.
7874      * @param {Number} row The index of the row to deselect
7875      */
7876     deselectRow : function(index, preventViewNotify){
7877         if(this.locked) {
7878             return;
7879         }
7880         if(this.last == index){
7881             this.last = false;
7882         }
7883         if(this.lastActive == index){
7884             this.lastActive = false;
7885         }
7886         var r = this.grid.ds.getAt(index);
7887         this.selections.remove(r);
7888         if(!preventViewNotify){
7889             var view = this.grid.view ? this.grid.view : this.grid;
7890             view.onRowDeselect(index);
7891         }
7892         this.fireEvent("rowdeselect", this, index);
7893         this.fireEvent("selectionchange", this);
7894     },
7895
7896     // private
7897     restoreLast : function(){
7898         if(this._last){
7899             this.last = this._last;
7900         }
7901     },
7902
7903     // private
7904     acceptsNav : function(row, col, cm){
7905         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7906     },
7907
7908     // private
7909     onEditorKey : function(field, e){
7910         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7911         if(k == e.TAB){
7912             e.stopEvent();
7913             ed.completeEdit();
7914             if(e.shiftKey){
7915                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7916             }else{
7917                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7918             }
7919         }else if(k == e.ENTER && !e.ctrlKey){
7920             e.stopEvent();
7921             ed.completeEdit();
7922             if(e.shiftKey){
7923                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7924             }else{
7925                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7926             }
7927         }else if(k == e.ESC){
7928             ed.cancelEdit();
7929         }
7930         if(newCell){
7931             g.startEditing(newCell[0], newCell[1]);
7932         }
7933     }
7934 });/*
7935  * Based on:
7936  * Ext JS Library 1.1.1
7937  * Copyright(c) 2006-2007, Ext JS, LLC.
7938  *
7939  * Originally Released Under LGPL - original licence link has changed is not relivant.
7940  *
7941  * Fork - LGPL
7942  * <script type="text/javascript">
7943  */
7944  
7945
7946 /**
7947  * @class Roo.grid.ColumnModel
7948  * @extends Roo.util.Observable
7949  * This is the default implementation of a ColumnModel used by the Grid. It defines
7950  * the columns in the grid.
7951  * <br>Usage:<br>
7952  <pre><code>
7953  var colModel = new Roo.grid.ColumnModel([
7954         {header: "Ticker", width: 60, sortable: true, locked: true},
7955         {header: "Company Name", width: 150, sortable: true},
7956         {header: "Market Cap.", width: 100, sortable: true},
7957         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7958         {header: "Employees", width: 100, sortable: true, resizable: false}
7959  ]);
7960  </code></pre>
7961  * <p>
7962  
7963  * The config options listed for this class are options which may appear in each
7964  * individual column definition.
7965  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7966  * @constructor
7967  * @param {Object} config An Array of column config objects. See this class's
7968  * config objects for details.
7969 */
7970 Roo.grid.ColumnModel = function(config){
7971         /**
7972      * The config passed into the constructor
7973      */
7974     this.config = []; //config;
7975     this.lookup = {};
7976
7977     // if no id, create one
7978     // if the column does not have a dataIndex mapping,
7979     // map it to the order it is in the config
7980     for(var i = 0, len = config.length; i < len; i++){
7981         this.addColumn(config[i]);
7982         
7983     }
7984
7985     /**
7986      * The width of columns which have no width specified (defaults to 100)
7987      * @type Number
7988      */
7989     this.defaultWidth = 100;
7990
7991     /**
7992      * Default sortable of columns which have no sortable specified (defaults to false)
7993      * @type Boolean
7994      */
7995     this.defaultSortable = false;
7996
7997     this.addEvents({
7998         /**
7999              * @event widthchange
8000              * Fires when the width of a column changes.
8001              * @param {ColumnModel} this
8002              * @param {Number} columnIndex The column index
8003              * @param {Number} newWidth The new width
8004              */
8005             "widthchange": true,
8006         /**
8007              * @event headerchange
8008              * Fires when the text of a header changes.
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Number} newText The new header text
8012              */
8013             "headerchange": true,
8014         /**
8015              * @event hiddenchange
8016              * Fires when a column is hidden or "unhidden".
8017              * @param {ColumnModel} this
8018              * @param {Number} columnIndex The column index
8019              * @param {Boolean} hidden true if hidden, false otherwise
8020              */
8021             "hiddenchange": true,
8022             /**
8023          * @event columnmoved
8024          * Fires when a column is moved.
8025          * @param {ColumnModel} this
8026          * @param {Number} oldIndex
8027          * @param {Number} newIndex
8028          */
8029         "columnmoved" : true,
8030         /**
8031          * @event columlockchange
8032          * Fires when a column's locked state is changed
8033          * @param {ColumnModel} this
8034          * @param {Number} colIndex
8035          * @param {Boolean} locked true if locked
8036          */
8037         "columnlockchange" : true
8038     });
8039     Roo.grid.ColumnModel.superclass.constructor.call(this);
8040 };
8041 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8042     /**
8043      * @cfg {String} header The header text to display in the Grid view.
8044      */
8045         /**
8046      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8047      */
8048         /**
8049      * @cfg {String} smHeader Header at Bootsrap Small width
8050      */
8051         /**
8052      * @cfg {String} mdHeader Header at Bootsrap Medium width
8053      */
8054         /**
8055      * @cfg {String} lgHeader Header at Bootsrap Large width
8056      */
8057         /**
8058      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8059      */
8060     /**
8061      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8062      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8063      * specified, the column's index is used as an index into the Record's data Array.
8064      */
8065     /**
8066      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8067      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8068      */
8069     /**
8070      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8071      * Defaults to the value of the {@link #defaultSortable} property.
8072      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8073      */
8074     /**
8075      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8076      */
8077     /**
8078      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8079      */
8080     /**
8081      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8082      */
8083     /**
8084      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8085      */
8086     /**
8087      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8088      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8089      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8090      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8091      */
8092        /**
8093      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8094      */
8095     /**
8096      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8097      */
8098     /**
8099      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8100      */
8101     /**
8102      * @cfg {String} cursor (Optional)
8103      */
8104     /**
8105      * @cfg {String} tooltip (Optional)
8106      */
8107     /**
8108      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8109      */
8110     /**
8111      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8112      */
8113     /**
8114      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8115      */
8116     /**
8117      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8118      */
8119         /**
8120      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8121      */
8122     /**
8123      * Returns the id of the column at the specified index.
8124      * @param {Number} index The column index
8125      * @return {String} the id
8126      */
8127     getColumnId : function(index){
8128         return this.config[index].id;
8129     },
8130
8131     /**
8132      * Returns the column for a specified id.
8133      * @param {String} id The column id
8134      * @return {Object} the column
8135      */
8136     getColumnById : function(id){
8137         return this.lookup[id];
8138     },
8139
8140     
8141     /**
8142      * Returns the column Object for a specified dataIndex.
8143      * @param {String} dataIndex The column dataIndex
8144      * @return {Object|Boolean} the column or false if not found
8145      */
8146     getColumnByDataIndex: function(dataIndex){
8147         var index = this.findColumnIndex(dataIndex);
8148         return index > -1 ? this.config[index] : false;
8149     },
8150     
8151     /**
8152      * Returns the index for a specified column id.
8153      * @param {String} id The column id
8154      * @return {Number} the index, or -1 if not found
8155      */
8156     getIndexById : function(id){
8157         for(var i = 0, len = this.config.length; i < len; i++){
8158             if(this.config[i].id == id){
8159                 return i;
8160             }
8161         }
8162         return -1;
8163     },
8164     
8165     /**
8166      * Returns the index for a specified column dataIndex.
8167      * @param {String} dataIndex The column dataIndex
8168      * @return {Number} the index, or -1 if not found
8169      */
8170     
8171     findColumnIndex : function(dataIndex){
8172         for(var i = 0, len = this.config.length; i < len; i++){
8173             if(this.config[i].dataIndex == dataIndex){
8174                 return i;
8175             }
8176         }
8177         return -1;
8178     },
8179     
8180     
8181     moveColumn : function(oldIndex, newIndex){
8182         var c = this.config[oldIndex];
8183         this.config.splice(oldIndex, 1);
8184         this.config.splice(newIndex, 0, c);
8185         this.dataMap = null;
8186         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8187     },
8188
8189     isLocked : function(colIndex){
8190         return this.config[colIndex].locked === true;
8191     },
8192
8193     setLocked : function(colIndex, value, suppressEvent){
8194         if(this.isLocked(colIndex) == value){
8195             return;
8196         }
8197         this.config[colIndex].locked = value;
8198         if(!suppressEvent){
8199             this.fireEvent("columnlockchange", this, colIndex, value);
8200         }
8201     },
8202
8203     getTotalLockedWidth : function(){
8204         var totalWidth = 0;
8205         for(var i = 0; i < this.config.length; i++){
8206             if(this.isLocked(i) && !this.isHidden(i)){
8207                 this.totalWidth += this.getColumnWidth(i);
8208             }
8209         }
8210         return totalWidth;
8211     },
8212
8213     getLockedCount : function(){
8214         for(var i = 0, len = this.config.length; i < len; i++){
8215             if(!this.isLocked(i)){
8216                 return i;
8217             }
8218         }
8219         
8220         return this.config.length;
8221     },
8222
8223     /**
8224      * Returns the number of columns.
8225      * @return {Number}
8226      */
8227     getColumnCount : function(visibleOnly){
8228         if(visibleOnly === true){
8229             var c = 0;
8230             for(var i = 0, len = this.config.length; i < len; i++){
8231                 if(!this.isHidden(i)){
8232                     c++;
8233                 }
8234             }
8235             return c;
8236         }
8237         return this.config.length;
8238     },
8239
8240     /**
8241      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8242      * @param {Function} fn
8243      * @param {Object} scope (optional)
8244      * @return {Array} result
8245      */
8246     getColumnsBy : function(fn, scope){
8247         var r = [];
8248         for(var i = 0, len = this.config.length; i < len; i++){
8249             var c = this.config[i];
8250             if(fn.call(scope||this, c, i) === true){
8251                 r[r.length] = c;
8252             }
8253         }
8254         return r;
8255     },
8256
8257     /**
8258      * Returns true if the specified column is sortable.
8259      * @param {Number} col The column index
8260      * @return {Boolean}
8261      */
8262     isSortable : function(col){
8263         if(typeof this.config[col].sortable == "undefined"){
8264             return this.defaultSortable;
8265         }
8266         return this.config[col].sortable;
8267     },
8268
8269     /**
8270      * Returns the rendering (formatting) function defined for the column.
8271      * @param {Number} col The column index.
8272      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8273      */
8274     getRenderer : function(col){
8275         if(!this.config[col].renderer){
8276             return Roo.grid.ColumnModel.defaultRenderer;
8277         }
8278         return this.config[col].renderer;
8279     },
8280
8281     /**
8282      * Sets the rendering (formatting) function for a column.
8283      * @param {Number} col The column index
8284      * @param {Function} fn The function to use to process the cell's raw data
8285      * to return HTML markup for the grid view. The render function is called with
8286      * the following parameters:<ul>
8287      * <li>Data value.</li>
8288      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8289      * <li>css A CSS style string to apply to the table cell.</li>
8290      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8291      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8292      * <li>Row index</li>
8293      * <li>Column index</li>
8294      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8295      */
8296     setRenderer : function(col, fn){
8297         this.config[col].renderer = fn;
8298     },
8299
8300     /**
8301      * Returns the width for the specified column.
8302      * @param {Number} col The column index
8303      * @param (optional) {String} gridSize bootstrap width size.
8304      * @return {Number}
8305      */
8306     getColumnWidth : function(col, gridSize)
8307         {
8308                 var cfg = this.config[col];
8309                 
8310                 if (typeof(gridSize) == 'undefined') {
8311                         return cfg.width * 1 || this.defaultWidth;
8312                 }
8313                 if (gridSize === false) { // if we set it..
8314                         return cfg.width || false;
8315                 }
8316                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8317                 
8318                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8319                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8320                                 continue;
8321                         }
8322                         return cfg[ sizes[i] ];
8323                 }
8324                 return 1;
8325                 
8326     },
8327
8328     /**
8329      * Sets the width for a column.
8330      * @param {Number} col The column index
8331      * @param {Number} width The new width
8332      */
8333     setColumnWidth : function(col, width, suppressEvent){
8334         this.config[col].width = width;
8335         this.totalWidth = null;
8336         if(!suppressEvent){
8337              this.fireEvent("widthchange", this, col, width);
8338         }
8339     },
8340
8341     /**
8342      * Returns the total width of all columns.
8343      * @param {Boolean} includeHidden True to include hidden column widths
8344      * @return {Number}
8345      */
8346     getTotalWidth : function(includeHidden){
8347         if(!this.totalWidth){
8348             this.totalWidth = 0;
8349             for(var i = 0, len = this.config.length; i < len; i++){
8350                 if(includeHidden || !this.isHidden(i)){
8351                     this.totalWidth += this.getColumnWidth(i);
8352                 }
8353             }
8354         }
8355         return this.totalWidth;
8356     },
8357
8358     /**
8359      * Returns the header for the specified column.
8360      * @param {Number} col The column index
8361      * @return {String}
8362      */
8363     getColumnHeader : function(col){
8364         return this.config[col].header;
8365     },
8366
8367     /**
8368      * Sets the header for a column.
8369      * @param {Number} col The column index
8370      * @param {String} header The new header
8371      */
8372     setColumnHeader : function(col, header){
8373         this.config[col].header = header;
8374         this.fireEvent("headerchange", this, col, header);
8375     },
8376
8377     /**
8378      * Returns the tooltip for the specified column.
8379      * @param {Number} col The column index
8380      * @return {String}
8381      */
8382     getColumnTooltip : function(col){
8383             return this.config[col].tooltip;
8384     },
8385     /**
8386      * Sets the tooltip for a column.
8387      * @param {Number} col The column index
8388      * @param {String} tooltip The new tooltip
8389      */
8390     setColumnTooltip : function(col, tooltip){
8391             this.config[col].tooltip = tooltip;
8392     },
8393
8394     /**
8395      * Returns the dataIndex for the specified column.
8396      * @param {Number} col The column index
8397      * @return {Number}
8398      */
8399     getDataIndex : function(col){
8400         return this.config[col].dataIndex;
8401     },
8402
8403     /**
8404      * Sets the dataIndex for a column.
8405      * @param {Number} col The column index
8406      * @param {Number} dataIndex The new dataIndex
8407      */
8408     setDataIndex : function(col, dataIndex){
8409         this.config[col].dataIndex = dataIndex;
8410     },
8411
8412     
8413     
8414     /**
8415      * Returns true if the cell is editable.
8416      * @param {Number} colIndex The column index
8417      * @param {Number} rowIndex The row index - this is nto actually used..?
8418      * @return {Boolean}
8419      */
8420     isCellEditable : function(colIndex, rowIndex){
8421         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8422     },
8423
8424     /**
8425      * Returns the editor defined for the cell/column.
8426      * return false or null to disable editing.
8427      * @param {Number} colIndex The column index
8428      * @param {Number} rowIndex The row index
8429      * @return {Object}
8430      */
8431     getCellEditor : function(colIndex, rowIndex){
8432         return this.config[colIndex].editor;
8433     },
8434
8435     /**
8436      * Sets if a column is editable.
8437      * @param {Number} col The column index
8438      * @param {Boolean} editable True if the column is editable
8439      */
8440     setEditable : function(col, editable){
8441         this.config[col].editable = editable;
8442     },
8443
8444
8445     /**
8446      * Returns true if the column is hidden.
8447      * @param {Number} colIndex The column index
8448      * @return {Boolean}
8449      */
8450     isHidden : function(colIndex){
8451         return this.config[colIndex].hidden;
8452     },
8453
8454
8455     /**
8456      * Returns true if the column width cannot be changed
8457      */
8458     isFixed : function(colIndex){
8459         return this.config[colIndex].fixed;
8460     },
8461
8462     /**
8463      * Returns true if the column can be resized
8464      * @return {Boolean}
8465      */
8466     isResizable : function(colIndex){
8467         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8468     },
8469     /**
8470      * Sets if a column is hidden.
8471      * @param {Number} colIndex The column index
8472      * @param {Boolean} hidden True if the column is hidden
8473      */
8474     setHidden : function(colIndex, hidden){
8475         this.config[colIndex].hidden = hidden;
8476         this.totalWidth = null;
8477         this.fireEvent("hiddenchange", this, colIndex, hidden);
8478     },
8479
8480     /**
8481      * Sets the editor for a column.
8482      * @param {Number} col The column index
8483      * @param {Object} editor The editor object
8484      */
8485     setEditor : function(col, editor){
8486         this.config[col].editor = editor;
8487     },
8488     /**
8489      * Add a column (experimental...) - defaults to adding to the end..
8490      * @param {Object} config 
8491     */
8492     addColumn : function(c)
8493     {
8494     
8495         var i = this.config.length;
8496         this.config[i] = c;
8497         
8498         if(typeof c.dataIndex == "undefined"){
8499             c.dataIndex = i;
8500         }
8501         if(typeof c.renderer == "string"){
8502             c.renderer = Roo.util.Format[c.renderer];
8503         }
8504         if(typeof c.id == "undefined"){
8505             c.id = Roo.id();
8506         }
8507         if(c.editor && c.editor.xtype){
8508             c.editor  = Roo.factory(c.editor, Roo.grid);
8509         }
8510         if(c.editor && c.editor.isFormField){
8511             c.editor = new Roo.grid.GridEditor(c.editor);
8512         }
8513         this.lookup[c.id] = c;
8514     }
8515     
8516 });
8517
8518 Roo.grid.ColumnModel.defaultRenderer = function(value)
8519 {
8520     if(typeof value == "object") {
8521         return value;
8522     }
8523         if(typeof value == "string" && value.length < 1){
8524             return "&#160;";
8525         }
8526     
8527         return String.format("{0}", value);
8528 };
8529
8530 // Alias for backwards compatibility
8531 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8532 /*
8533  * Based on:
8534  * Ext JS Library 1.1.1
8535  * Copyright(c) 2006-2007, Ext JS, LLC.
8536  *
8537  * Originally Released Under LGPL - original licence link has changed is not relivant.
8538  *
8539  * Fork - LGPL
8540  * <script type="text/javascript">
8541  */
8542  
8543 /**
8544  * @class Roo.LoadMask
8545  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8546  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8547  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8548  * element's UpdateManager load indicator and will be destroyed after the initial load.
8549  * @constructor
8550  * Create a new LoadMask
8551  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8552  * @param {Object} config The config object
8553  */
8554 Roo.LoadMask = function(el, config){
8555     this.el = Roo.get(el);
8556     Roo.apply(this, config);
8557     if(this.store){
8558         this.store.on('beforeload', this.onBeforeLoad, this);
8559         this.store.on('load', this.onLoad, this);
8560         this.store.on('loadexception', this.onLoadException, this);
8561         this.removeMask = false;
8562     }else{
8563         var um = this.el.getUpdateManager();
8564         um.showLoadIndicator = false; // disable the default indicator
8565         um.on('beforeupdate', this.onBeforeLoad, this);
8566         um.on('update', this.onLoad, this);
8567         um.on('failure', this.onLoad, this);
8568         this.removeMask = true;
8569     }
8570 };
8571
8572 Roo.LoadMask.prototype = {
8573     /**
8574      * @cfg {Boolean} removeMask
8575      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8576      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8577      */
8578     removeMask : false,
8579     /**
8580      * @cfg {String} msg
8581      * The text to display in a centered loading message box (defaults to 'Loading...')
8582      */
8583     msg : 'Loading...',
8584     /**
8585      * @cfg {String} msgCls
8586      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8587      */
8588     msgCls : 'x-mask-loading',
8589
8590     /**
8591      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8592      * @type Boolean
8593      */
8594     disabled: false,
8595
8596     /**
8597      * Disables the mask to prevent it from being displayed
8598      */
8599     disable : function(){
8600        this.disabled = true;
8601     },
8602
8603     /**
8604      * Enables the mask so that it can be displayed
8605      */
8606     enable : function(){
8607         this.disabled = false;
8608     },
8609     
8610     onLoadException : function()
8611     {
8612         Roo.log(arguments);
8613         
8614         if (typeof(arguments[3]) != 'undefined') {
8615             Roo.MessageBox.alert("Error loading",arguments[3]);
8616         } 
8617         /*
8618         try {
8619             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8620                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8621             }   
8622         } catch(e) {
8623             
8624         }
8625         */
8626     
8627         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8628     },
8629     // private
8630     onLoad : function()
8631     {
8632         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8633     },
8634
8635     // private
8636     onBeforeLoad : function(){
8637         if(!this.disabled){
8638             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8639         }
8640     },
8641
8642     // private
8643     destroy : function(){
8644         if(this.store){
8645             this.store.un('beforeload', this.onBeforeLoad, this);
8646             this.store.un('load', this.onLoad, this);
8647             this.store.un('loadexception', this.onLoadException, this);
8648         }else{
8649             var um = this.el.getUpdateManager();
8650             um.un('beforeupdate', this.onBeforeLoad, this);
8651             um.un('update', this.onLoad, this);
8652             um.un('failure', this.onLoad, this);
8653         }
8654     }
8655 };/**
8656  * @class Roo.bootstrap.Table
8657  * @licence LGBL
8658  * @extends Roo.bootstrap.Component
8659  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8660  * Similar to Roo.grid.Grid
8661  * <pre><code>
8662  var table = Roo.factory({
8663     xtype : 'Table',
8664     xns : Roo.bootstrap,
8665     autoSizeColumns: true,
8666     
8667     
8668     store : {
8669         xtype : 'Store',
8670         xns : Roo.data,
8671         remoteSort : true,
8672         sortInfo : { direction : 'ASC', field: 'name' },
8673         proxy : {
8674            xtype : 'HttpProxy',
8675            xns : Roo.data,
8676            method : 'GET',
8677            url : 'https://example.com/some.data.url.json'
8678         },
8679         reader : {
8680            xtype : 'JsonReader',
8681            xns : Roo.data,
8682            fields : [ 'id', 'name', whatever' ],
8683            id : 'id',
8684            root : 'data'
8685         }
8686     },
8687     cm : [
8688         {
8689             xtype : 'ColumnModel',
8690             xns : Roo.grid,
8691             align : 'center',
8692             cursor : 'pointer',
8693             dataIndex : 'is_in_group',
8694             header : "Name",
8695             sortable : true,
8696             renderer : function(v, x , r) {  
8697             
8698                 return String.format("{0}", v)
8699             }
8700             width : 3
8701         } // more columns..
8702     ],
8703     selModel : {
8704         xtype : 'RowSelectionModel',
8705         xns : Roo.bootstrap.Table
8706         // you can add listeners to catch selection change here....
8707     }
8708      
8709
8710  });
8711  // set any options
8712  grid.render(Roo.get("some-div"));
8713 </code></pre>
8714
8715 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8716
8717
8718
8719  *
8720  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8721  * @cfg {Roo.data.Store} store The data store to use
8722  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8723  * 
8724  * @cfg {String} cls table class
8725  *
8726  * 
8727  * @cfg {boolean} striped Should the rows be alternative striped
8728  * @cfg {boolean} bordered Add borders to the table
8729  * @cfg {boolean} hover Add hover highlighting
8730  * @cfg {boolean} condensed Format condensed
8731  * @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,
8732  *                also adds table-responsive (see bootstrap docs for details)
8733  * @cfg {Boolean} loadMask (true|false) default false
8734  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8735  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8736  * @cfg {Boolean} rowSelection (true|false) default false
8737  * @cfg {Boolean} cellSelection (true|false) default false
8738  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8739  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8740  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8741  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8742  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8743  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8744  * 
8745  * @constructor
8746  * Create a new Table
8747  * @param {Object} config The config object
8748  */
8749
8750 Roo.bootstrap.Table = function(config)
8751 {
8752     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8753      
8754     // BC...
8755     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8756     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8757     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8758     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8759     
8760     this.view = this; // compat with grid.
8761     
8762     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8763     if (this.sm) {
8764         this.sm.grid = this;
8765         this.selModel = Roo.factory(this.sm, Roo.grid);
8766         this.sm = this.selModel;
8767         this.sm.xmodule = this.xmodule || false;
8768     }
8769     
8770     if (this.cm && typeof(this.cm.config) == 'undefined') {
8771         this.colModel = new Roo.grid.ColumnModel(this.cm);
8772         this.cm = this.colModel;
8773         this.cm.xmodule = this.xmodule || false;
8774     }
8775     if (this.store) {
8776         this.store= Roo.factory(this.store, Roo.data);
8777         this.ds = this.store;
8778         this.ds.xmodule = this.xmodule || false;
8779          
8780     }
8781     if (this.footer && this.store) {
8782         this.footer.dataSource = this.ds;
8783         this.footer = Roo.factory(this.footer);
8784     }
8785     
8786     /** @private */
8787     this.addEvents({
8788         /**
8789          * @event cellclick
8790          * Fires when a cell is clicked
8791          * @param {Roo.bootstrap.Table} this
8792          * @param {Roo.Element} el
8793          * @param {Number} rowIndex
8794          * @param {Number} columnIndex
8795          * @param {Roo.EventObject} e
8796          */
8797         "cellclick" : true,
8798         /**
8799          * @event celldblclick
8800          * Fires when a cell is double clicked
8801          * @param {Roo.bootstrap.Table} this
8802          * @param {Roo.Element} el
8803          * @param {Number} rowIndex
8804          * @param {Number} columnIndex
8805          * @param {Roo.EventObject} e
8806          */
8807         "celldblclick" : true,
8808         /**
8809          * @event rowclick
8810          * Fires when a row is clicked
8811          * @param {Roo.bootstrap.Table} this
8812          * @param {Roo.Element} el
8813          * @param {Number} rowIndex
8814          * @param {Roo.EventObject} e
8815          */
8816         "rowclick" : true,
8817         /**
8818          * @event rowdblclick
8819          * Fires when a row is double clicked
8820          * @param {Roo.bootstrap.Table} this
8821          * @param {Roo.Element} el
8822          * @param {Number} rowIndex
8823          * @param {Roo.EventObject} e
8824          */
8825         "rowdblclick" : true,
8826         /**
8827          * @event mouseover
8828          * Fires when a mouseover occur
8829          * @param {Roo.bootstrap.Table} this
8830          * @param {Roo.Element} el
8831          * @param {Number} rowIndex
8832          * @param {Number} columnIndex
8833          * @param {Roo.EventObject} e
8834          */
8835         "mouseover" : true,
8836         /**
8837          * @event mouseout
8838          * Fires when a mouseout occur
8839          * @param {Roo.bootstrap.Table} this
8840          * @param {Roo.Element} el
8841          * @param {Number} rowIndex
8842          * @param {Number} columnIndex
8843          * @param {Roo.EventObject} e
8844          */
8845         "mouseout" : true,
8846         /**
8847          * @event rowclass
8848          * Fires when a row is rendered, so you can change add a style to it.
8849          * @param {Roo.bootstrap.Table} this
8850          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8851          */
8852         'rowclass' : true,
8853           /**
8854          * @event rowsrendered
8855          * Fires when all the  rows have been rendered
8856          * @param {Roo.bootstrap.Table} this
8857          */
8858         'rowsrendered' : true,
8859         /**
8860          * @event contextmenu
8861          * The raw contextmenu event for the entire grid.
8862          * @param {Roo.EventObject} e
8863          */
8864         "contextmenu" : true,
8865         /**
8866          * @event rowcontextmenu
8867          * Fires when a row is right clicked
8868          * @param {Roo.bootstrap.Table} this
8869          * @param {Number} rowIndex
8870          * @param {Roo.EventObject} e
8871          */
8872         "rowcontextmenu" : true,
8873         /**
8874          * @event cellcontextmenu
8875          * Fires when a cell is right clicked
8876          * @param {Roo.bootstrap.Table} this
8877          * @param {Number} rowIndex
8878          * @param {Number} cellIndex
8879          * @param {Roo.EventObject} e
8880          */
8881          "cellcontextmenu" : true,
8882          /**
8883          * @event headercontextmenu
8884          * Fires when a header is right clicked
8885          * @param {Roo.bootstrap.Table} this
8886          * @param {Number} columnIndex
8887          * @param {Roo.EventObject} e
8888          */
8889         "headercontextmenu" : true,
8890         /**
8891          * @event mousedown
8892          * The raw mousedown event for the entire grid.
8893          * @param {Roo.EventObject} e
8894          */
8895         "mousedown" : true
8896         
8897     });
8898 };
8899
8900 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8901     
8902     cls: false,
8903     
8904     striped : false,
8905     scrollBody : false,
8906     bordered: false,
8907     hover:  false,
8908     condensed : false,
8909     responsive : false,
8910     sm : false,
8911     cm : false,
8912     store : false,
8913     loadMask : false,
8914     footerShow : true,
8915     headerShow : true,
8916     enableColumnResize: true,
8917   
8918     rowSelection : false,
8919     cellSelection : false,
8920     layout : false,
8921
8922     minColumnWidth : 50,
8923     
8924     // Roo.Element - the tbody
8925     bodyEl: false,  // <tbody> Roo.Element - thead element    
8926     headEl: false,  // <thead> Roo.Element - thead element
8927     resizeProxy : false, // proxy element for dragging?
8928
8929
8930     
8931     container: false, // used by gridpanel...
8932     
8933     lazyLoad : false,
8934     
8935     CSS : Roo.util.CSS,
8936     
8937     auto_hide_footer : false,
8938     
8939     view: false, // actually points to this..
8940     
8941     getAutoCreate : function()
8942     {
8943         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'table',
8947             cls : 'table', 
8948             cn : []
8949         };
8950         // this get's auto added by panel.Grid
8951         if (this.scrollBody) {
8952             cfg.cls += ' table-body-fixed';
8953         }    
8954         if (this.striped) {
8955             cfg.cls += ' table-striped';
8956         }
8957         
8958         if (this.hover) {
8959             cfg.cls += ' table-hover';
8960         }
8961         if (this.bordered) {
8962             cfg.cls += ' table-bordered';
8963         }
8964         if (this.condensed) {
8965             cfg.cls += ' table-condensed';
8966         }
8967         
8968         if (this.responsive) {
8969             cfg.cls += ' table-responsive';
8970         }
8971         
8972         if (this.cls) {
8973             cfg.cls+=  ' ' +this.cls;
8974         }
8975         
8976         
8977         
8978         if (this.layout) {
8979             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8980         }
8981         
8982         if(this.store || this.cm){
8983             if(this.headerShow){
8984                 cfg.cn.push(this.renderHeader());
8985             }
8986             
8987             cfg.cn.push(this.renderBody());
8988             
8989             if(this.footerShow){
8990                 cfg.cn.push(this.renderFooter());
8991             }
8992             // where does this come from?
8993             //cfg.cls+=  ' TableGrid';
8994         }
8995         
8996         return { cn : [ cfg ] };
8997     },
8998     
8999     initEvents : function()
9000     {   
9001         if(!this.store || !this.cm){
9002             return;
9003         }
9004         if (this.selModel) {
9005             this.selModel.initEvents();
9006         }
9007         
9008         
9009         //Roo.log('initEvents with ds!!!!');
9010         
9011         this.bodyEl = this.el.select('tbody', true).first();
9012         this.headEl = this.el.select('thead', true).first();
9013         this.mainFoot = this.el.select('tfoot', true).first();
9014         
9015         
9016         
9017         
9018         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9019             e.on('click', this.sort, this);
9020         }, this);
9021         
9022         
9023         // why is this done????? = it breaks dialogs??
9024         //this.parent().el.setStyle('position', 'relative');
9025         
9026         
9027         if (this.footer) {
9028             this.footer.parentId = this.id;
9029             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9030             
9031             if(this.lazyLoad){
9032                 this.el.select('tfoot tr td').first().addClass('hide');
9033             }
9034         } 
9035         
9036         if(this.loadMask) {
9037             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9038         }
9039         
9040         this.store.on('load', this.onLoad, this);
9041         this.store.on('beforeload', this.onBeforeLoad, this);
9042         this.store.on('update', this.onUpdate, this);
9043         this.store.on('add', this.onAdd, this);
9044         this.store.on("clear", this.clear, this);
9045         
9046         this.el.on("contextmenu", this.onContextMenu, this);
9047         
9048         
9049         this.cm.on("headerchange", this.onHeaderChange, this);
9050         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9051
9052  //?? does bodyEl get replaced on render?
9053         this.bodyEl.on("click", this.onClick, this);
9054         this.bodyEl.on("dblclick", this.onDblClick, this);        
9055         this.bodyEl.on('scroll', this.onBodyScroll, this);
9056
9057         // guessing mainbody will work - this relays usually caught by selmodel at present.
9058         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9059   
9060   
9061         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9062         
9063   
9064         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9065             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9066         }
9067         
9068         this.initCSS();
9069     },
9070     // Compatibility with grid - we implement all the view features at present.
9071     getView : function()
9072     {
9073         return this;
9074     },
9075     
9076     initCSS : function()
9077     {
9078         
9079         
9080         var cm = this.cm, styles = [];
9081         this.CSS.removeStyleSheet(this.id + '-cssrules');
9082         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9083         // we can honour xs/sm/md/xl  as widths...
9084         // we first have to decide what widht we are currently at...
9085         var sz = Roo.getGridSize();
9086         
9087         var total = 0;
9088         var last = -1;
9089         var cols = []; // visable cols.
9090         var total_abs = 0;
9091         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9092             var w = cm.getColumnWidth(i, false);
9093             if(cm.isHidden(i)){
9094                 cols.push( { rel : false, abs : 0 });
9095                 continue;
9096             }
9097             if (w !== false) {
9098                 cols.push( { rel : false, abs : w });
9099                 total_abs += w;
9100                 last = i; // not really..
9101                 continue;
9102             }
9103             var w = cm.getColumnWidth(i, sz);
9104             if (w > 0) {
9105                 last = i
9106             }
9107             total += w;
9108             cols.push( { rel : w, abs : false });
9109         }
9110         
9111         var avail = this.bodyEl.dom.clientWidth - total_abs;
9112         
9113         var unitWidth = Math.floor(avail / total);
9114         var rem = avail - (unitWidth * total);
9115         
9116         var hidden, width, pos = 0 , splithide , left;
9117         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9118             
9119             hidden = 'display:none;';
9120             left = '';
9121             width  = 'width:0px;';
9122             splithide = '';
9123             if(!cm.isHidden(i)){
9124                 hidden = '';
9125                 
9126                 
9127                 // we can honour xs/sm/md/xl ?
9128                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9129                 if (w===0) {
9130                     hidden = 'display:none;';
9131                 }
9132                 // width should return a small number...
9133                 if (i == last) {
9134                     w+=rem; // add the remaining with..
9135                 }
9136                 pos += w;
9137                 left = "left:" + (pos -4) + "px;";
9138                 width = "width:" + w+ "px;";
9139                 
9140             }
9141             if (this.responsive) {
9142                 width = '';
9143                 left = '';
9144                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9145                 splithide = 'display: none;';
9146             }
9147             
9148             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9149             if (this.headEl) {
9150                 if (i == last) {
9151                     splithide = 'display:none;';
9152                 }
9153                 
9154                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9155                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9156                 );
9157             }
9158             
9159         }
9160         //Roo.log(styles.join(''));
9161         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9162         
9163     },
9164     
9165     
9166     
9167     onContextMenu : function(e, t)
9168     {
9169         this.processEvent("contextmenu", e);
9170     },
9171     
9172     processEvent : function(name, e)
9173     {
9174         if (name != 'touchstart' ) {
9175             this.fireEvent(name, e);    
9176         }
9177         
9178         var t = e.getTarget();
9179         
9180         var cell = Roo.get(t);
9181         
9182         if(!cell){
9183             return;
9184         }
9185         
9186         if(cell.findParent('tfoot', false, true)){
9187             return;
9188         }
9189         
9190         if(cell.findParent('thead', false, true)){
9191             
9192             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9193                 cell = Roo.get(t).findParent('th', false, true);
9194                 if (!cell) {
9195                     Roo.log("failed to find th in thead?");
9196                     Roo.log(e.getTarget());
9197                     return;
9198                 }
9199             }
9200             
9201             var cellIndex = cell.dom.cellIndex;
9202             
9203             var ename = name == 'touchstart' ? 'click' : name;
9204             this.fireEvent("header" + ename, this, cellIndex, e);
9205             
9206             return;
9207         }
9208         
9209         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9210             cell = Roo.get(t).findParent('td', false, true);
9211             if (!cell) {
9212                 Roo.log("failed to find th in tbody?");
9213                 Roo.log(e.getTarget());
9214                 return;
9215             }
9216         }
9217         
9218         var row = cell.findParent('tr', false, true);
9219         var cellIndex = cell.dom.cellIndex;
9220         var rowIndex = row.dom.rowIndex - 1;
9221         
9222         if(row !== false){
9223             
9224             this.fireEvent("row" + name, this, rowIndex, e);
9225             
9226             if(cell !== false){
9227             
9228                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9229             }
9230         }
9231         
9232     },
9233     
9234     onMouseover : function(e, el)
9235     {
9236         var cell = Roo.get(el);
9237         
9238         if(!cell){
9239             return;
9240         }
9241         
9242         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9243             cell = cell.findParent('td', false, true);
9244         }
9245         
9246         var row = cell.findParent('tr', false, true);
9247         var cellIndex = cell.dom.cellIndex;
9248         var rowIndex = row.dom.rowIndex - 1; // start from 0
9249         
9250         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9251         
9252     },
9253     
9254     onMouseout : function(e, el)
9255     {
9256         var cell = Roo.get(el);
9257         
9258         if(!cell){
9259             return;
9260         }
9261         
9262         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9263             cell = cell.findParent('td', false, true);
9264         }
9265         
9266         var row = cell.findParent('tr', false, true);
9267         var cellIndex = cell.dom.cellIndex;
9268         var rowIndex = row.dom.rowIndex - 1; // start from 0
9269         
9270         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9271         
9272     },
9273     
9274     onClick : function(e, el)
9275     {
9276         var cell = Roo.get(el);
9277         
9278         if(!cell || (!this.cellSelection && !this.rowSelection)){
9279             return;
9280         }
9281         
9282         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9283             cell = cell.findParent('td', false, true);
9284         }
9285         
9286         if(!cell || typeof(cell) == 'undefined'){
9287             return;
9288         }
9289         
9290         var row = cell.findParent('tr', false, true);
9291         
9292         if(!row || typeof(row) == 'undefined'){
9293             return;
9294         }
9295         
9296         var cellIndex = cell.dom.cellIndex;
9297         var rowIndex = this.getRowIndex(row);
9298         
9299         // why??? - should these not be based on SelectionModel?
9300         //if(this.cellSelection){
9301             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9302         //}
9303         
9304         //if(this.rowSelection){
9305             this.fireEvent('rowclick', this, row, rowIndex, e);
9306         //}
9307          
9308     },
9309         
9310     onDblClick : function(e,el)
9311     {
9312         var cell = Roo.get(el);
9313         
9314         if(!cell || (!this.cellSelection && !this.rowSelection)){
9315             return;
9316         }
9317         
9318         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9319             cell = cell.findParent('td', false, true);
9320         }
9321         
9322         if(!cell || typeof(cell) == 'undefined'){
9323             return;
9324         }
9325         
9326         var row = cell.findParent('tr', false, true);
9327         
9328         if(!row || typeof(row) == 'undefined'){
9329             return;
9330         }
9331         
9332         var cellIndex = cell.dom.cellIndex;
9333         var rowIndex = this.getRowIndex(row);
9334         
9335         if(this.cellSelection){
9336             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9337         }
9338         
9339         if(this.rowSelection){
9340             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9341         }
9342     },
9343     findRowIndex : function(el)
9344     {
9345         var cell = Roo.get(el);
9346         if(!cell) {
9347             return false;
9348         }
9349         var row = cell.findParent('tr', false, true);
9350         
9351         if(!row || typeof(row) == 'undefined'){
9352             return false;
9353         }
9354         return this.getRowIndex(row);
9355     },
9356     sort : function(e,el)
9357     {
9358         var col = Roo.get(el);
9359         
9360         if(!col.hasClass('sortable')){
9361             return;
9362         }
9363         
9364         var sort = col.attr('sort');
9365         var dir = 'ASC';
9366         
9367         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9368             dir = 'DESC';
9369         }
9370         
9371         this.store.sortInfo = {field : sort, direction : dir};
9372         
9373         if (this.footer) {
9374             Roo.log("calling footer first");
9375             this.footer.onClick('first');
9376         } else {
9377         
9378             this.store.load({ params : { start : 0 } });
9379         }
9380     },
9381     
9382     renderHeader : function()
9383     {
9384         var header = {
9385             tag: 'thead',
9386             cn : []
9387         };
9388         
9389         var cm = this.cm;
9390         this.totalWidth = 0;
9391         
9392         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9393             
9394             var config = cm.config[i];
9395             
9396             var c = {
9397                 tag: 'th',
9398                 cls : 'x-hcol-' + i,
9399                 style : '',
9400                 
9401                 html: cm.getColumnHeader(i)
9402             };
9403             
9404             var tooltip = cm.getColumnTooltip(i);
9405             if (tooltip) {
9406                 c.tooltip = tooltip;
9407             }
9408             
9409             
9410             var hh = '';
9411             
9412             if(typeof(config.sortable) != 'undefined' && config.sortable){
9413                 c.cls += ' sortable';
9414                 c.html = '<i class="fa"></i>' + c.html;
9415             }
9416             
9417             // could use BS4 hidden-..-down 
9418             
9419             if(typeof(config.lgHeader) != 'undefined'){
9420                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9421             }
9422             
9423             if(typeof(config.mdHeader) != 'undefined'){
9424                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9425             }
9426             
9427             if(typeof(config.smHeader) != 'undefined'){
9428                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9429             }
9430             
9431             if(typeof(config.xsHeader) != 'undefined'){
9432                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9433             }
9434             
9435             if(hh.length){
9436                 c.html = hh;
9437             }
9438             
9439             if(typeof(config.tooltip) != 'undefined'){
9440                 c.tooltip = config.tooltip;
9441             }
9442             
9443             if(typeof(config.colspan) != 'undefined'){
9444                 c.colspan = config.colspan;
9445             }
9446             
9447             // hidden is handled by CSS now
9448             
9449             if(typeof(config.dataIndex) != 'undefined'){
9450                 c.sort = config.dataIndex;
9451             }
9452             
9453            
9454             
9455             if(typeof(config.align) != 'undefined' && config.align.length){
9456                 c.style += ' text-align:' + config.align + ';';
9457             }
9458             
9459             /* width is done in CSS
9460              *if(typeof(config.width) != 'undefined'){
9461                 c.style += ' width:' + config.width + 'px;';
9462                 this.totalWidth += config.width;
9463             } else {
9464                 this.totalWidth += 100; // assume minimum of 100 per column?
9465             }
9466             */
9467             
9468             if(typeof(config.cls) != 'undefined'){
9469                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9470             }
9471             // this is the bit that doesnt reall work at all...
9472             
9473             if (this.responsive) {
9474                  
9475             
9476                 ['xs','sm','md','lg'].map(function(size){
9477                     
9478                     if(typeof(config[size]) == 'undefined'){
9479                         return;
9480                     }
9481                      
9482                     if (!config[size]) { // 0 = hidden
9483                         // BS 4 '0' is treated as hide that column and below.
9484                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9485                         return;
9486                     }
9487                     
9488                     c.cls += ' col-' + size + '-' + config[size] + (
9489                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9490                     );
9491                     
9492                     
9493                 });
9494             }
9495             // at the end?
9496             
9497             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9498             
9499             
9500             
9501             
9502             header.cn.push(c)
9503         }
9504         
9505         return header;
9506     },
9507     
9508     renderBody : function()
9509     {
9510         var body = {
9511             tag: 'tbody',
9512             cn : [
9513                 {
9514                     tag: 'tr',
9515                     cn : [
9516                         {
9517                             tag : 'td',
9518                             colspan :  this.cm.getColumnCount()
9519                         }
9520                     ]
9521                 }
9522             ]
9523         };
9524         
9525         return body;
9526     },
9527     
9528     renderFooter : function()
9529     {
9530         var footer = {
9531             tag: 'tfoot',
9532             cn : [
9533                 {
9534                     tag: 'tr',
9535                     cn : [
9536                         {
9537                             tag : 'td',
9538                             colspan :  this.cm.getColumnCount()
9539                         }
9540                     ]
9541                 }
9542             ]
9543         };
9544         
9545         return footer;
9546     },
9547     
9548     
9549     
9550     onLoad : function()
9551     {
9552 //        Roo.log('ds onload');
9553         this.clear();
9554         
9555         var _this = this;
9556         var cm = this.cm;
9557         var ds = this.store;
9558         
9559         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9560             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9561             if (_this.store.sortInfo) {
9562                     
9563                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9564                     e.select('i', true).addClass(['fa-arrow-up']);
9565                 }
9566                 
9567                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9568                     e.select('i', true).addClass(['fa-arrow-down']);
9569                 }
9570             }
9571         });
9572         
9573         var tbody =  this.bodyEl;
9574               
9575         if(ds.getCount() > 0){
9576             ds.data.each(function(d,rowIndex){
9577                 var row =  this.renderRow(cm, ds, rowIndex);
9578                 
9579                 tbody.createChild(row);
9580                 
9581                 var _this = this;
9582                 
9583                 if(row.cellObjects.length){
9584                     Roo.each(row.cellObjects, function(r){
9585                         _this.renderCellObject(r);
9586                     })
9587                 }
9588                 
9589             }, this);
9590         }
9591         
9592         var tfoot = this.el.select('tfoot', true).first();
9593         
9594         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9595             
9596             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9597             
9598             var total = this.ds.getTotalCount();
9599             
9600             if(this.footer.pageSize < total){
9601                 this.mainFoot.show();
9602             }
9603         }
9604         
9605         Roo.each(this.el.select('tbody td', true).elements, function(e){
9606             e.on('mouseover', _this.onMouseover, _this);
9607         });
9608         
9609         Roo.each(this.el.select('tbody td', true).elements, function(e){
9610             e.on('mouseout', _this.onMouseout, _this);
9611         });
9612         this.fireEvent('rowsrendered', this);
9613         
9614         this.autoSize();
9615         
9616         this.initCSS(); /// resize cols
9617
9618         
9619     },
9620     
9621     
9622     onUpdate : function(ds,record)
9623     {
9624         this.refreshRow(record);
9625         this.autoSize();
9626     },
9627     
9628     onRemove : function(ds, record, index, isUpdate){
9629         if(isUpdate !== true){
9630             this.fireEvent("beforerowremoved", this, index, record);
9631         }
9632         var bt = this.bodyEl.dom;
9633         
9634         var rows = this.el.select('tbody > tr', true).elements;
9635         
9636         if(typeof(rows[index]) != 'undefined'){
9637             bt.removeChild(rows[index].dom);
9638         }
9639         
9640 //        if(bt.rows[index]){
9641 //            bt.removeChild(bt.rows[index]);
9642 //        }
9643         
9644         if(isUpdate !== true){
9645             //this.stripeRows(index);
9646             //this.syncRowHeights(index, index);
9647             //this.layout();
9648             this.fireEvent("rowremoved", this, index, record);
9649         }
9650     },
9651     
9652     onAdd : function(ds, records, rowIndex)
9653     {
9654         //Roo.log('on Add called');
9655         // - note this does not handle multiple adding very well..
9656         var bt = this.bodyEl.dom;
9657         for (var i =0 ; i < records.length;i++) {
9658             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9659             //Roo.log(records[i]);
9660             //Roo.log(this.store.getAt(rowIndex+i));
9661             this.insertRow(this.store, rowIndex + i, false);
9662             return;
9663         }
9664         
9665     },
9666     
9667     
9668     refreshRow : function(record){
9669         var ds = this.store, index;
9670         if(typeof record == 'number'){
9671             index = record;
9672             record = ds.getAt(index);
9673         }else{
9674             index = ds.indexOf(record);
9675             if (index < 0) {
9676                 return; // should not happen - but seems to 
9677             }
9678         }
9679         this.insertRow(ds, index, true);
9680         this.autoSize();
9681         this.onRemove(ds, record, index+1, true);
9682         this.autoSize();
9683         //this.syncRowHeights(index, index);
9684         //this.layout();
9685         this.fireEvent("rowupdated", this, index, record);
9686     },
9687     // private - called by RowSelection
9688     onRowSelect : function(rowIndex){
9689         var row = this.getRowDom(rowIndex);
9690         row.addClass(['bg-info','info']);
9691     },
9692     // private - called by RowSelection
9693     onRowDeselect : function(rowIndex)
9694     {
9695         if (rowIndex < 0) {
9696             return;
9697         }
9698         var row = this.getRowDom(rowIndex);
9699         row.removeClass(['bg-info','info']);
9700     },
9701       /**
9702      * Focuses the specified row.
9703      * @param {Number} row The row index
9704      */
9705     focusRow : function(row)
9706     {
9707         //Roo.log('GridView.focusRow');
9708         var x = this.bodyEl.dom.scrollLeft;
9709         this.focusCell(row, 0, false);
9710         this.bodyEl.dom.scrollLeft = x;
9711
9712     },
9713      /**
9714      * Focuses the specified cell.
9715      * @param {Number} row The row index
9716      * @param {Number} col The column index
9717      * @param {Boolean} hscroll false to disable horizontal scrolling
9718      */
9719     focusCell : function(row, col, hscroll)
9720     {
9721         //Roo.log('GridView.focusCell');
9722         var el = this.ensureVisible(row, col, hscroll);
9723         // not sure what focusEL achives = it's a <a> pos relative 
9724         //this.focusEl.alignTo(el, "tl-tl");
9725         //if(Roo.isGecko){
9726         //    this.focusEl.focus();
9727         //}else{
9728         //    this.focusEl.focus.defer(1, this.focusEl);
9729         //}
9730     },
9731     
9732      /**
9733      * Scrolls the specified cell into view
9734      * @param {Number} row The row index
9735      * @param {Number} col The column index
9736      * @param {Boolean} hscroll false to disable horizontal scrolling
9737      */
9738     ensureVisible : function(row, col, hscroll)
9739     {
9740         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9741         //return null; //disable for testing.
9742         if(typeof row != "number"){
9743             row = row.rowIndex;
9744         }
9745         if(row < 0 && row >= this.ds.getCount()){
9746             return  null;
9747         }
9748         col = (col !== undefined ? col : 0);
9749         var cm = this.cm;
9750         while(cm.isHidden(col)){
9751             col++;
9752         }
9753
9754         var el = this.getCellDom(row, col);
9755         if(!el){
9756             return null;
9757         }
9758         var c = this.bodyEl.dom;
9759
9760         var ctop = parseInt(el.offsetTop, 10);
9761         var cleft = parseInt(el.offsetLeft, 10);
9762         var cbot = ctop + el.offsetHeight;
9763         var cright = cleft + el.offsetWidth;
9764
9765         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9766         var ch = 0; //?? header is not withing the area?
9767         var stop = parseInt(c.scrollTop, 10);
9768         var sleft = parseInt(c.scrollLeft, 10);
9769         var sbot = stop + ch;
9770         var sright = sleft + c.clientWidth;
9771         /*
9772         Roo.log('GridView.ensureVisible:' +
9773                 ' ctop:' + ctop +
9774                 ' c.clientHeight:' + c.clientHeight +
9775                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9776                 ' stop:' + stop +
9777                 ' cbot:' + cbot +
9778                 ' sbot:' + sbot +
9779                 ' ch:' + ch  
9780                 );
9781         */
9782         if(ctop < stop){
9783             c.scrollTop = ctop;
9784             //Roo.log("set scrolltop to ctop DISABLE?");
9785         }else if(cbot > sbot){
9786             //Roo.log("set scrolltop to cbot-ch");
9787             c.scrollTop = cbot-ch;
9788         }
9789
9790         if(hscroll !== false){
9791             if(cleft < sleft){
9792                 c.scrollLeft = cleft;
9793             }else if(cright > sright){
9794                 c.scrollLeft = cright-c.clientWidth;
9795             }
9796         }
9797
9798         return el;
9799     },
9800     
9801     
9802     insertRow : function(dm, rowIndex, isUpdate){
9803         
9804         if(!isUpdate){
9805             this.fireEvent("beforerowsinserted", this, rowIndex);
9806         }
9807             //var s = this.getScrollState();
9808         var row = this.renderRow(this.cm, this.store, rowIndex);
9809         // insert before rowIndex..
9810         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9811         
9812         var _this = this;
9813                 
9814         if(row.cellObjects.length){
9815             Roo.each(row.cellObjects, function(r){
9816                 _this.renderCellObject(r);
9817             })
9818         }
9819             
9820         if(!isUpdate){
9821             this.fireEvent("rowsinserted", this, rowIndex);
9822             //this.syncRowHeights(firstRow, lastRow);
9823             //this.stripeRows(firstRow);
9824             //this.layout();
9825         }
9826         
9827     },
9828     
9829     
9830     getRowDom : function(rowIndex)
9831     {
9832         var rows = this.el.select('tbody > tr', true).elements;
9833         
9834         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9835         
9836     },
9837     getCellDom : function(rowIndex, colIndex)
9838     {
9839         var row = this.getRowDom(rowIndex);
9840         if (row === false) {
9841             return false;
9842         }
9843         var cols = row.select('td', true).elements;
9844         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9845         
9846     },
9847     
9848     // returns the object tree for a tr..
9849   
9850     
9851     renderRow : function(cm, ds, rowIndex) 
9852     {
9853         var d = ds.getAt(rowIndex);
9854         
9855         var row = {
9856             tag : 'tr',
9857             cls : 'x-row-' + rowIndex,
9858             cn : []
9859         };
9860             
9861         var cellObjects = [];
9862         
9863         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9864             var config = cm.config[i];
9865             
9866             var renderer = cm.getRenderer(i);
9867             var value = '';
9868             var id = false;
9869             
9870             if(typeof(renderer) !== 'undefined'){
9871                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9872             }
9873             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9874             // and are rendered into the cells after the row is rendered - using the id for the element.
9875             
9876             if(typeof(value) === 'object'){
9877                 id = Roo.id();
9878                 cellObjects.push({
9879                     container : id,
9880                     cfg : value 
9881                 })
9882             }
9883             
9884             var rowcfg = {
9885                 record: d,
9886                 rowIndex : rowIndex,
9887                 colIndex : i,
9888                 rowClass : ''
9889             };
9890
9891             this.fireEvent('rowclass', this, rowcfg);
9892             
9893             var td = {
9894                 tag: 'td',
9895                 // this might end up displaying HTML?
9896                 // this is too messy... - better to only do it on columsn you know are going to be too long
9897                 //tooltip : (typeof(value) === 'object') ? '' : value,
9898                 cls : rowcfg.rowClass + ' x-col-' + i,
9899                 style: '',
9900                 html: (typeof(value) === 'object') ? '' : value
9901             };
9902             
9903             if (id) {
9904                 td.id = id;
9905             }
9906             
9907             if(typeof(config.colspan) != 'undefined'){
9908                 td.colspan = config.colspan;
9909             }
9910             
9911             
9912             
9913             if(typeof(config.align) != 'undefined' && config.align.length){
9914                 td.style += ' text-align:' + config.align + ';';
9915             }
9916             if(typeof(config.valign) != 'undefined' && config.valign.length){
9917                 td.style += ' vertical-align:' + config.valign + ';';
9918             }
9919             /*
9920             if(typeof(config.width) != 'undefined'){
9921                 td.style += ' width:' +  config.width + 'px;';
9922             }
9923             */
9924             
9925             if(typeof(config.cursor) != 'undefined'){
9926                 td.style += ' cursor:' +  config.cursor + ';';
9927             }
9928             
9929             if(typeof(config.cls) != 'undefined'){
9930                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9931             }
9932             if (this.responsive) {
9933                 ['xs','sm','md','lg'].map(function(size){
9934                     
9935                     if(typeof(config[size]) == 'undefined'){
9936                         return;
9937                     }
9938                     
9939                     
9940                       
9941                     if (!config[size]) { // 0 = hidden
9942                         // BS 4 '0' is treated as hide that column and below.
9943                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9944                         return;
9945                     }
9946                     
9947                     td.cls += ' col-' + size + '-' + config[size] + (
9948                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9949                     );
9950                      
9951     
9952                 });
9953             }
9954             row.cn.push(td);
9955            
9956         }
9957         
9958         row.cellObjects = cellObjects;
9959         
9960         return row;
9961           
9962     },
9963     
9964     
9965     
9966     onBeforeLoad : function()
9967     {
9968         
9969     },
9970      /**
9971      * Remove all rows
9972      */
9973     clear : function()
9974     {
9975         this.el.select('tbody', true).first().dom.innerHTML = '';
9976     },
9977     /**
9978      * Show or hide a row.
9979      * @param {Number} rowIndex to show or hide
9980      * @param {Boolean} state hide
9981      */
9982     setRowVisibility : function(rowIndex, state)
9983     {
9984         var bt = this.bodyEl.dom;
9985         
9986         var rows = this.el.select('tbody > tr', true).elements;
9987         
9988         if(typeof(rows[rowIndex]) == 'undefined'){
9989             return;
9990         }
9991         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9992         
9993     },
9994     
9995     
9996     getSelectionModel : function(){
9997         if(!this.selModel){
9998             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9999         }
10000         return this.selModel;
10001     },
10002     /*
10003      * Render the Roo.bootstrap object from renderder
10004      */
10005     renderCellObject : function(r)
10006     {
10007         var _this = this;
10008         
10009         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10010         
10011         var t = r.cfg.render(r.container);
10012         
10013         if(r.cfg.cn){
10014             Roo.each(r.cfg.cn, function(c){
10015                 var child = {
10016                     container: t.getChildContainer(),
10017                     cfg: c
10018                 };
10019                 _this.renderCellObject(child);
10020             })
10021         }
10022     },
10023     /**
10024      * get the Row Index from a dom element.
10025      * @param {Roo.Element} row The row to look for
10026      * @returns {Number} the row
10027      */
10028     getRowIndex : function(row)
10029     {
10030         var rowIndex = -1;
10031         
10032         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10033             if(el != row){
10034                 return;
10035             }
10036             
10037             rowIndex = index;
10038         });
10039         
10040         return rowIndex;
10041     },
10042     /**
10043      * get the header TH element for columnIndex
10044      * @param {Number} columnIndex
10045      * @returns {Roo.Element}
10046      */
10047     getHeaderIndex: function(colIndex)
10048     {
10049         var cols = this.headEl.select('th', true).elements;
10050         return cols[colIndex]; 
10051     },
10052     /**
10053      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10054      * @param {domElement} cell to look for
10055      * @returns {Number} the column
10056      */
10057     getCellIndex : function(cell)
10058     {
10059         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10060         if(id){
10061             return parseInt(id[1], 10);
10062         }
10063         return 0;
10064     },
10065      /**
10066      * Returns the grid's underlying element = used by panel.Grid
10067      * @return {Element} The element
10068      */
10069     getGridEl : function(){
10070         return this.el;
10071     },
10072      /**
10073      * Forces a resize - used by panel.Grid
10074      * @return {Element} The element
10075      */
10076     autoSize : function()
10077     {
10078         //var ctr = Roo.get(this.container.dom.parentElement);
10079         var ctr = Roo.get(this.el.dom);
10080         
10081         var thd = this.getGridEl().select('thead',true).first();
10082         var tbd = this.getGridEl().select('tbody', true).first();
10083         var tfd = this.getGridEl().select('tfoot', true).first();
10084         
10085         var cw = ctr.getWidth();
10086         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10087         
10088         if (tbd) {
10089             
10090             tbd.setWidth(ctr.getWidth());
10091             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10092             // this needs fixing for various usage - currently only hydra job advers I think..
10093             //tdb.setHeight(
10094             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10095             //); 
10096             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10097             cw -= barsize;
10098         }
10099         cw = Math.max(cw, this.totalWidth);
10100         this.getGridEl().select('tbody tr',true).setWidth(cw);
10101         this.initCSS();
10102         
10103         // resize 'expandable coloumn?
10104         
10105         return; // we doe not have a view in this design..
10106         
10107     },
10108     onBodyScroll: function()
10109     {
10110         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10111         if(this.headEl){
10112             this.headEl.setStyle({
10113                 'position' : 'relative',
10114                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10115             });
10116         }
10117         
10118         if(this.lazyLoad){
10119             
10120             var scrollHeight = this.bodyEl.dom.scrollHeight;
10121             
10122             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10123             
10124             var height = this.bodyEl.getHeight();
10125             
10126             if(scrollHeight - height == scrollTop) {
10127                 
10128                 var total = this.ds.getTotalCount();
10129                 
10130                 if(this.footer.cursor + this.footer.pageSize < total){
10131                     
10132                     this.footer.ds.load({
10133                         params : {
10134                             start : this.footer.cursor + this.footer.pageSize,
10135                             limit : this.footer.pageSize
10136                         },
10137                         add : true
10138                     });
10139                 }
10140             }
10141             
10142         }
10143     },
10144     onColumnSplitterMoved : function(i, diff)
10145     {
10146         this.userResized = true;
10147         
10148         var cm = this.colModel;
10149         
10150         var w = this.getHeaderIndex(i).getWidth() + diff;
10151         
10152         
10153         cm.setColumnWidth(i, w, true);
10154         this.initCSS();
10155         //var cid = cm.getColumnId(i); << not used in this version?
10156        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10157         
10158         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10159         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10160         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10161 */
10162         //this.updateSplitters();
10163         //this.layout(); << ??
10164         this.fireEvent("columnresize", i, w);
10165     },
10166     onHeaderChange : function()
10167     {
10168         var header = this.renderHeader();
10169         var table = this.el.select('table', true).first();
10170         
10171         this.headEl.remove();
10172         this.headEl = table.createChild(header, this.bodyEl, false);
10173         
10174         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10175             e.on('click', this.sort, this);
10176         }, this);
10177         
10178         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10179             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10180         }
10181         
10182     },
10183     
10184     onHiddenChange : function(colModel, colIndex, hidden)
10185     {
10186         /*
10187         this.cm.setHidden()
10188         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10189         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10190         
10191         this.CSS.updateRule(thSelector, "display", "");
10192         this.CSS.updateRule(tdSelector, "display", "");
10193         
10194         if(hidden){
10195             this.CSS.updateRule(thSelector, "display", "none");
10196             this.CSS.updateRule(tdSelector, "display", "none");
10197         }
10198         */
10199         // onload calls initCSS()
10200         this.onHeaderChange();
10201         this.onLoad();
10202     },
10203     
10204     setColumnWidth: function(col_index, width)
10205     {
10206         // width = "md-2 xs-2..."
10207         if(!this.colModel.config[col_index]) {
10208             return;
10209         }
10210         
10211         var w = width.split(" ");
10212         
10213         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10214         
10215         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10216         
10217         
10218         for(var j = 0; j < w.length; j++) {
10219             
10220             if(!w[j]) {
10221                 continue;
10222             }
10223             
10224             var size_cls = w[j].split("-");
10225             
10226             if(!Number.isInteger(size_cls[1] * 1)) {
10227                 continue;
10228             }
10229             
10230             if(!this.colModel.config[col_index][size_cls[0]]) {
10231                 continue;
10232             }
10233             
10234             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10235                 continue;
10236             }
10237             
10238             h_row[0].classList.replace(
10239                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10240                 "col-"+size_cls[0]+"-"+size_cls[1]
10241             );
10242             
10243             for(var i = 0; i < rows.length; i++) {
10244                 
10245                 var size_cls = w[j].split("-");
10246                 
10247                 if(!Number.isInteger(size_cls[1] * 1)) {
10248                     continue;
10249                 }
10250                 
10251                 if(!this.colModel.config[col_index][size_cls[0]]) {
10252                     continue;
10253                 }
10254                 
10255                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10256                     continue;
10257                 }
10258                 
10259                 rows[i].classList.replace(
10260                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10261                     "col-"+size_cls[0]+"-"+size_cls[1]
10262                 );
10263             }
10264             
10265             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10266         }
10267     }
10268 });
10269
10270 // currently only used to find the split on drag.. 
10271 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10272
10273 /**
10274  * @depricated
10275 */
10276 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10277 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10278 /*
10279  * - LGPL
10280  *
10281  * table cell
10282  * 
10283  */
10284
10285 /**
10286  * @class Roo.bootstrap.TableCell
10287  * @extends Roo.bootstrap.Component
10288  * Bootstrap TableCell class
10289  * @cfg {String} html cell contain text
10290  * @cfg {String} cls cell class
10291  * @cfg {String} tag cell tag (td|th) default td
10292  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10293  * @cfg {String} align Aligns the content in a cell
10294  * @cfg {String} axis Categorizes cells
10295  * @cfg {String} bgcolor Specifies the background color of a cell
10296  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10297  * @cfg {Number} colspan Specifies the number of columns a cell should span
10298  * @cfg {String} headers Specifies one or more header cells a cell is related to
10299  * @cfg {Number} height Sets the height of a cell
10300  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10301  * @cfg {Number} rowspan Sets the number of rows a cell should span
10302  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10303  * @cfg {String} valign Vertical aligns the content in a cell
10304  * @cfg {Number} width Specifies the width of a cell
10305  * 
10306  * @constructor
10307  * Create a new TableCell
10308  * @param {Object} config The config object
10309  */
10310
10311 Roo.bootstrap.TableCell = function(config){
10312     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10313 };
10314
10315 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10316     
10317     html: false,
10318     cls: false,
10319     tag: false,
10320     abbr: false,
10321     align: false,
10322     axis: false,
10323     bgcolor: false,
10324     charoff: false,
10325     colspan: false,
10326     headers: false,
10327     height: false,
10328     nowrap: false,
10329     rowspan: false,
10330     scope: false,
10331     valign: false,
10332     width: false,
10333     
10334     
10335     getAutoCreate : function(){
10336         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10337         
10338         cfg = {
10339             tag: 'td'
10340         };
10341         
10342         if(this.tag){
10343             cfg.tag = this.tag;
10344         }
10345         
10346         if (this.html) {
10347             cfg.html=this.html
10348         }
10349         if (this.cls) {
10350             cfg.cls=this.cls
10351         }
10352         if (this.abbr) {
10353             cfg.abbr=this.abbr
10354         }
10355         if (this.align) {
10356             cfg.align=this.align
10357         }
10358         if (this.axis) {
10359             cfg.axis=this.axis
10360         }
10361         if (this.bgcolor) {
10362             cfg.bgcolor=this.bgcolor
10363         }
10364         if (this.charoff) {
10365             cfg.charoff=this.charoff
10366         }
10367         if (this.colspan) {
10368             cfg.colspan=this.colspan
10369         }
10370         if (this.headers) {
10371             cfg.headers=this.headers
10372         }
10373         if (this.height) {
10374             cfg.height=this.height
10375         }
10376         if (this.nowrap) {
10377             cfg.nowrap=this.nowrap
10378         }
10379         if (this.rowspan) {
10380             cfg.rowspan=this.rowspan
10381         }
10382         if (this.scope) {
10383             cfg.scope=this.scope
10384         }
10385         if (this.valign) {
10386             cfg.valign=this.valign
10387         }
10388         if (this.width) {
10389             cfg.width=this.width
10390         }
10391         
10392         
10393         return cfg;
10394     }
10395    
10396 });
10397
10398  
10399
10400  /*
10401  * - LGPL
10402  *
10403  * table row
10404  * 
10405  */
10406
10407 /**
10408  * @class Roo.bootstrap.TableRow
10409  * @extends Roo.bootstrap.Component
10410  * Bootstrap TableRow class
10411  * @cfg {String} cls row class
10412  * @cfg {String} align Aligns the content in a table row
10413  * @cfg {String} bgcolor Specifies a background color for a table row
10414  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10415  * @cfg {String} valign Vertical aligns the content in a table row
10416  * 
10417  * @constructor
10418  * Create a new TableRow
10419  * @param {Object} config The config object
10420  */
10421
10422 Roo.bootstrap.TableRow = function(config){
10423     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10424 };
10425
10426 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10427     
10428     cls: false,
10429     align: false,
10430     bgcolor: false,
10431     charoff: false,
10432     valign: false,
10433     
10434     getAutoCreate : function(){
10435         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10436         
10437         cfg = {
10438             tag: 'tr'
10439         };
10440             
10441         if(this.cls){
10442             cfg.cls = this.cls;
10443         }
10444         if(this.align){
10445             cfg.align = this.align;
10446         }
10447         if(this.bgcolor){
10448             cfg.bgcolor = this.bgcolor;
10449         }
10450         if(this.charoff){
10451             cfg.charoff = this.charoff;
10452         }
10453         if(this.valign){
10454             cfg.valign = this.valign;
10455         }
10456         
10457         return cfg;
10458     }
10459    
10460 });
10461
10462  
10463
10464  /*
10465  * - LGPL
10466  *
10467  * table body
10468  * 
10469  */
10470
10471 /**
10472  * @class Roo.bootstrap.TableBody
10473  * @extends Roo.bootstrap.Component
10474  * Bootstrap TableBody class
10475  * @cfg {String} cls element class
10476  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10477  * @cfg {String} align Aligns the content inside the element
10478  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10479  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10480  * 
10481  * @constructor
10482  * Create a new TableBody
10483  * @param {Object} config The config object
10484  */
10485
10486 Roo.bootstrap.TableBody = function(config){
10487     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10488 };
10489
10490 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10491     
10492     cls: false,
10493     tag: false,
10494     align: false,
10495     charoff: false,
10496     valign: false,
10497     
10498     getAutoCreate : function(){
10499         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10500         
10501         cfg = {
10502             tag: 'tbody'
10503         };
10504             
10505         if (this.cls) {
10506             cfg.cls=this.cls
10507         }
10508         if(this.tag){
10509             cfg.tag = this.tag;
10510         }
10511         
10512         if(this.align){
10513             cfg.align = this.align;
10514         }
10515         if(this.charoff){
10516             cfg.charoff = this.charoff;
10517         }
10518         if(this.valign){
10519             cfg.valign = this.valign;
10520         }
10521         
10522         return cfg;
10523     }
10524     
10525     
10526 //    initEvents : function()
10527 //    {
10528 //        
10529 //        if(!this.store){
10530 //            return;
10531 //        }
10532 //        
10533 //        this.store = Roo.factory(this.store, Roo.data);
10534 //        this.store.on('load', this.onLoad, this);
10535 //        
10536 //        this.store.load();
10537 //        
10538 //    },
10539 //    
10540 //    onLoad: function () 
10541 //    {   
10542 //        this.fireEvent('load', this);
10543 //    }
10544 //    
10545 //   
10546 });
10547
10548  
10549
10550  /*
10551  * Based on:
10552  * Ext JS Library 1.1.1
10553  * Copyright(c) 2006-2007, Ext JS, LLC.
10554  *
10555  * Originally Released Under LGPL - original licence link has changed is not relivant.
10556  *
10557  * Fork - LGPL
10558  * <script type="text/javascript">
10559  */
10560
10561 // as we use this in bootstrap.
10562 Roo.namespace('Roo.form');
10563  /**
10564  * @class Roo.form.Action
10565  * Internal Class used to handle form actions
10566  * @constructor
10567  * @param {Roo.form.BasicForm} el The form element or its id
10568  * @param {Object} config Configuration options
10569  */
10570
10571  
10572  
10573 // define the action interface
10574 Roo.form.Action = function(form, options){
10575     this.form = form;
10576     this.options = options || {};
10577 };
10578 /**
10579  * Client Validation Failed
10580  * @const 
10581  */
10582 Roo.form.Action.CLIENT_INVALID = 'client';
10583 /**
10584  * Server Validation Failed
10585  * @const 
10586  */
10587 Roo.form.Action.SERVER_INVALID = 'server';
10588  /**
10589  * Connect to Server Failed
10590  * @const 
10591  */
10592 Roo.form.Action.CONNECT_FAILURE = 'connect';
10593 /**
10594  * Reading Data from Server Failed
10595  * @const 
10596  */
10597 Roo.form.Action.LOAD_FAILURE = 'load';
10598
10599 Roo.form.Action.prototype = {
10600     type : 'default',
10601     failureType : undefined,
10602     response : undefined,
10603     result : undefined,
10604
10605     // interface method
10606     run : function(options){
10607
10608     },
10609
10610     // interface method
10611     success : function(response){
10612
10613     },
10614
10615     // interface method
10616     handleResponse : function(response){
10617
10618     },
10619
10620     // default connection failure
10621     failure : function(response){
10622         
10623         this.response = response;
10624         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10625         this.form.afterAction(this, false);
10626     },
10627
10628     processResponse : function(response){
10629         this.response = response;
10630         if(!response.responseText){
10631             return true;
10632         }
10633         this.result = this.handleResponse(response);
10634         return this.result;
10635     },
10636
10637     // utility functions used internally
10638     getUrl : function(appendParams){
10639         var url = this.options.url || this.form.url || this.form.el.dom.action;
10640         if(appendParams){
10641             var p = this.getParams();
10642             if(p){
10643                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10644             }
10645         }
10646         return url;
10647     },
10648
10649     getMethod : function(){
10650         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10651     },
10652
10653     getParams : function(){
10654         var bp = this.form.baseParams;
10655         var p = this.options.params;
10656         if(p){
10657             if(typeof p == "object"){
10658                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10659             }else if(typeof p == 'string' && bp){
10660                 p += '&' + Roo.urlEncode(bp);
10661             }
10662         }else if(bp){
10663             p = Roo.urlEncode(bp);
10664         }
10665         return p;
10666     },
10667
10668     createCallback : function(){
10669         return {
10670             success: this.success,
10671             failure: this.failure,
10672             scope: this,
10673             timeout: (this.form.timeout*1000),
10674             upload: this.form.fileUpload ? this.success : undefined
10675         };
10676     }
10677 };
10678
10679 Roo.form.Action.Submit = function(form, options){
10680     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10681 };
10682
10683 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10684     type : 'submit',
10685
10686     haveProgress : false,
10687     uploadComplete : false,
10688     
10689     // uploadProgress indicator.
10690     uploadProgress : function()
10691     {
10692         if (!this.form.progressUrl) {
10693             return;
10694         }
10695         
10696         if (!this.haveProgress) {
10697             Roo.MessageBox.progress("Uploading", "Uploading");
10698         }
10699         if (this.uploadComplete) {
10700            Roo.MessageBox.hide();
10701            return;
10702         }
10703         
10704         this.haveProgress = true;
10705    
10706         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10707         
10708         var c = new Roo.data.Connection();
10709         c.request({
10710             url : this.form.progressUrl,
10711             params: {
10712                 id : uid
10713             },
10714             method: 'GET',
10715             success : function(req){
10716                //console.log(data);
10717                 var rdata = false;
10718                 var edata;
10719                 try  {
10720                    rdata = Roo.decode(req.responseText)
10721                 } catch (e) {
10722                     Roo.log("Invalid data from server..");
10723                     Roo.log(edata);
10724                     return;
10725                 }
10726                 if (!rdata || !rdata.success) {
10727                     Roo.log(rdata);
10728                     Roo.MessageBox.alert(Roo.encode(rdata));
10729                     return;
10730                 }
10731                 var data = rdata.data;
10732                 
10733                 if (this.uploadComplete) {
10734                    Roo.MessageBox.hide();
10735                    return;
10736                 }
10737                    
10738                 if (data){
10739                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10740                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10741                     );
10742                 }
10743                 this.uploadProgress.defer(2000,this);
10744             },
10745        
10746             failure: function(data) {
10747                 Roo.log('progress url failed ');
10748                 Roo.log(data);
10749             },
10750             scope : this
10751         });
10752            
10753     },
10754     
10755     
10756     run : function()
10757     {
10758         // run get Values on the form, so it syncs any secondary forms.
10759         this.form.getValues();
10760         
10761         var o = this.options;
10762         var method = this.getMethod();
10763         var isPost = method == 'POST';
10764         if(o.clientValidation === false || this.form.isValid()){
10765             
10766             if (this.form.progressUrl) {
10767                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10768                     (new Date() * 1) + '' + Math.random());
10769                     
10770             } 
10771             
10772             
10773             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10774                 form:this.form.el.dom,
10775                 url:this.getUrl(!isPost),
10776                 method: method,
10777                 params:isPost ? this.getParams() : null,
10778                 isUpload: this.form.fileUpload,
10779                 formData : this.form.formData
10780             }));
10781             
10782             this.uploadProgress();
10783
10784         }else if (o.clientValidation !== false){ // client validation failed
10785             this.failureType = Roo.form.Action.CLIENT_INVALID;
10786             this.form.afterAction(this, false);
10787         }
10788     },
10789
10790     success : function(response)
10791     {
10792         this.uploadComplete= true;
10793         if (this.haveProgress) {
10794             Roo.MessageBox.hide();
10795         }
10796         
10797         
10798         var result = this.processResponse(response);
10799         if(result === true || result.success){
10800             this.form.afterAction(this, true);
10801             return;
10802         }
10803         if(result.errors){
10804             this.form.markInvalid(result.errors);
10805             this.failureType = Roo.form.Action.SERVER_INVALID;
10806         }
10807         this.form.afterAction(this, false);
10808     },
10809     failure : function(response)
10810     {
10811         this.uploadComplete= true;
10812         if (this.haveProgress) {
10813             Roo.MessageBox.hide();
10814         }
10815         
10816         this.response = response;
10817         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10818         this.form.afterAction(this, false);
10819     },
10820     
10821     handleResponse : function(response){
10822         if(this.form.errorReader){
10823             var rs = this.form.errorReader.read(response);
10824             var errors = [];
10825             if(rs.records){
10826                 for(var i = 0, len = rs.records.length; i < len; i++) {
10827                     var r = rs.records[i];
10828                     errors[i] = r.data;
10829                 }
10830             }
10831             if(errors.length < 1){
10832                 errors = null;
10833             }
10834             return {
10835                 success : rs.success,
10836                 errors : errors
10837             };
10838         }
10839         var ret = false;
10840         try {
10841             ret = Roo.decode(response.responseText);
10842         } catch (e) {
10843             ret = {
10844                 success: false,
10845                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10846                 errors : []
10847             };
10848         }
10849         return ret;
10850         
10851     }
10852 });
10853
10854
10855 Roo.form.Action.Load = function(form, options){
10856     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10857     this.reader = this.form.reader;
10858 };
10859
10860 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10861     type : 'load',
10862
10863     run : function(){
10864         
10865         Roo.Ajax.request(Roo.apply(
10866                 this.createCallback(), {
10867                     method:this.getMethod(),
10868                     url:this.getUrl(false),
10869                     params:this.getParams()
10870         }));
10871     },
10872
10873     success : function(response){
10874         
10875         var result = this.processResponse(response);
10876         if(result === true || !result.success || !result.data){
10877             this.failureType = Roo.form.Action.LOAD_FAILURE;
10878             this.form.afterAction(this, false);
10879             return;
10880         }
10881         this.form.clearInvalid();
10882         this.form.setValues(result.data);
10883         this.form.afterAction(this, true);
10884     },
10885
10886     handleResponse : function(response){
10887         if(this.form.reader){
10888             var rs = this.form.reader.read(response);
10889             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10890             return {
10891                 success : rs.success,
10892                 data : data
10893             };
10894         }
10895         return Roo.decode(response.responseText);
10896     }
10897 });
10898
10899 Roo.form.Action.ACTION_TYPES = {
10900     'load' : Roo.form.Action.Load,
10901     'submit' : Roo.form.Action.Submit
10902 };/*
10903  * - LGPL
10904  *
10905  * form
10906  *
10907  */
10908
10909 /**
10910  * @class Roo.bootstrap.Form
10911  * @extends Roo.bootstrap.Component
10912  * Bootstrap Form class
10913  * @cfg {String} method  GET | POST (default POST)
10914  * @cfg {String} labelAlign top | left (default top)
10915  * @cfg {String} align left  | right - for navbars
10916  * @cfg {Boolean} loadMask load mask when submit (default true)
10917
10918  *
10919  * @constructor
10920  * Create a new Form
10921  * @param {Object} config The config object
10922  */
10923
10924
10925 Roo.bootstrap.Form = function(config){
10926     
10927     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10928     
10929     Roo.bootstrap.Form.popover.apply();
10930     
10931     this.addEvents({
10932         /**
10933          * @event clientvalidation
10934          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10935          * @param {Form} this
10936          * @param {Boolean} valid true if the form has passed client-side validation
10937          */
10938         clientvalidation: true,
10939         /**
10940          * @event beforeaction
10941          * Fires before any action is performed. Return false to cancel the action.
10942          * @param {Form} this
10943          * @param {Action} action The action to be performed
10944          */
10945         beforeaction: true,
10946         /**
10947          * @event actionfailed
10948          * Fires when an action fails.
10949          * @param {Form} this
10950          * @param {Action} action The action that failed
10951          */
10952         actionfailed : true,
10953         /**
10954          * @event actioncomplete
10955          * Fires when an action is completed.
10956          * @param {Form} this
10957          * @param {Action} action The action that completed
10958          */
10959         actioncomplete : true
10960     });
10961 };
10962
10963 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10964
10965      /**
10966      * @cfg {String} method
10967      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10968      */
10969     method : 'POST',
10970     /**
10971      * @cfg {String} url
10972      * The URL to use for form actions if one isn't supplied in the action options.
10973      */
10974     /**
10975      * @cfg {Boolean} fileUpload
10976      * Set to true if this form is a file upload.
10977      */
10978
10979     /**
10980      * @cfg {Object} baseParams
10981      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10982      */
10983
10984     /**
10985      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10986      */
10987     timeout: 30,
10988     /**
10989      * @cfg {Sting} align (left|right) for navbar forms
10990      */
10991     align : 'left',
10992
10993     // private
10994     activeAction : null,
10995
10996     /**
10997      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10998      * element by passing it or its id or mask the form itself by passing in true.
10999      * @type Mixed
11000      */
11001     waitMsgTarget : false,
11002
11003     loadMask : true,
11004     
11005     /**
11006      * @cfg {Boolean} errorMask (true|false) default false
11007      */
11008     errorMask : false,
11009     
11010     /**
11011      * @cfg {Number} maskOffset Default 100
11012      */
11013     maskOffset : 100,
11014     
11015     /**
11016      * @cfg {Boolean} maskBody
11017      */
11018     maskBody : false,
11019
11020     getAutoCreate : function(){
11021
11022         var cfg = {
11023             tag: 'form',
11024             method : this.method || 'POST',
11025             id : this.id || Roo.id(),
11026             cls : ''
11027         };
11028         if (this.parent().xtype.match(/^Nav/)) {
11029             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11030
11031         }
11032
11033         if (this.labelAlign == 'left' ) {
11034             cfg.cls += ' form-horizontal';
11035         }
11036
11037
11038         return cfg;
11039     },
11040     initEvents : function()
11041     {
11042         this.el.on('submit', this.onSubmit, this);
11043         // this was added as random key presses on the form where triggering form submit.
11044         this.el.on('keypress', function(e) {
11045             if (e.getCharCode() != 13) {
11046                 return true;
11047             }
11048             // we might need to allow it for textareas.. and some other items.
11049             // check e.getTarget().
11050
11051             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11052                 return true;
11053             }
11054
11055             Roo.log("keypress blocked");
11056
11057             e.preventDefault();
11058             return false;
11059         });
11060         
11061     },
11062     // private
11063     onSubmit : function(e){
11064         e.stopEvent();
11065     },
11066
11067      /**
11068      * Returns true if client-side validation on the form is successful.
11069      * @return Boolean
11070      */
11071     isValid : function(){
11072         var items = this.getItems();
11073         var valid = true;
11074         var target = false;
11075         
11076         items.each(function(f){
11077             
11078             if(f.validate()){
11079                 return;
11080             }
11081             
11082             Roo.log('invalid field: ' + f.name);
11083             
11084             valid = false;
11085
11086             if(!target && f.el.isVisible(true)){
11087                 target = f;
11088             }
11089            
11090         });
11091         
11092         if(this.errorMask && !valid){
11093             Roo.bootstrap.Form.popover.mask(this, target);
11094         }
11095         
11096         return valid;
11097     },
11098     
11099     /**
11100      * Returns true if any fields in this form have changed since their original load.
11101      * @return Boolean
11102      */
11103     isDirty : function(){
11104         var dirty = false;
11105         var items = this.getItems();
11106         items.each(function(f){
11107            if(f.isDirty()){
11108                dirty = true;
11109                return false;
11110            }
11111            return true;
11112         });
11113         return dirty;
11114     },
11115      /**
11116      * Performs a predefined action (submit or load) or custom actions you define on this form.
11117      * @param {String} actionName The name of the action type
11118      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11119      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11120      * accept other config options):
11121      * <pre>
11122 Property          Type             Description
11123 ----------------  ---------------  ----------------------------------------------------------------------------------
11124 url               String           The url for the action (defaults to the form's url)
11125 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11126 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11127 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11128                                    validate the form on the client (defaults to false)
11129      * </pre>
11130      * @return {BasicForm} this
11131      */
11132     doAction : function(action, options){
11133         if(typeof action == 'string'){
11134             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11135         }
11136         if(this.fireEvent('beforeaction', this, action) !== false){
11137             this.beforeAction(action);
11138             action.run.defer(100, action);
11139         }
11140         return this;
11141     },
11142
11143     // private
11144     beforeAction : function(action){
11145         var o = action.options;
11146         
11147         if(this.loadMask){
11148             
11149             if(this.maskBody){
11150                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11151             } else {
11152                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11153             }
11154         }
11155         // not really supported yet.. ??
11156
11157         //if(this.waitMsgTarget === true){
11158         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11159         //}else if(this.waitMsgTarget){
11160         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11161         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11162         //}else {
11163         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11164        // }
11165
11166     },
11167
11168     // private
11169     afterAction : function(action, success){
11170         this.activeAction = null;
11171         var o = action.options;
11172
11173         if(this.loadMask){
11174             
11175             if(this.maskBody){
11176                 Roo.get(document.body).unmask();
11177             } else {
11178                 this.el.unmask();
11179             }
11180         }
11181         
11182         //if(this.waitMsgTarget === true){
11183 //            this.el.unmask();
11184         //}else if(this.waitMsgTarget){
11185         //    this.waitMsgTarget.unmask();
11186         //}else{
11187         //    Roo.MessageBox.updateProgress(1);
11188         //    Roo.MessageBox.hide();
11189        // }
11190         //
11191         if(success){
11192             if(o.reset){
11193                 this.reset();
11194             }
11195             Roo.callback(o.success, o.scope, [this, action]);
11196             this.fireEvent('actioncomplete', this, action);
11197
11198         }else{
11199
11200             // failure condition..
11201             // we have a scenario where updates need confirming.
11202             // eg. if a locking scenario exists..
11203             // we look for { errors : { needs_confirm : true }} in the response.
11204             if (
11205                 (typeof(action.result) != 'undefined')  &&
11206                 (typeof(action.result.errors) != 'undefined')  &&
11207                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11208            ){
11209                 var _t = this;
11210                 Roo.log("not supported yet");
11211                  /*
11212
11213                 Roo.MessageBox.confirm(
11214                     "Change requires confirmation",
11215                     action.result.errorMsg,
11216                     function(r) {
11217                         if (r != 'yes') {
11218                             return;
11219                         }
11220                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11221                     }
11222
11223                 );
11224                 */
11225
11226
11227                 return;
11228             }
11229
11230             Roo.callback(o.failure, o.scope, [this, action]);
11231             // show an error message if no failed handler is set..
11232             if (!this.hasListener('actionfailed')) {
11233                 Roo.log("need to add dialog support");
11234                 /*
11235                 Roo.MessageBox.alert("Error",
11236                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11237                         action.result.errorMsg :
11238                         "Saving Failed, please check your entries or try again"
11239                 );
11240                 */
11241             }
11242
11243             this.fireEvent('actionfailed', this, action);
11244         }
11245
11246     },
11247     /**
11248      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11249      * @param {String} id The value to search for
11250      * @return Field
11251      */
11252     findField : function(id){
11253         var items = this.getItems();
11254         var field = items.get(id);
11255         if(!field){
11256              items.each(function(f){
11257                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11258                     field = f;
11259                     return false;
11260                 }
11261                 return true;
11262             });
11263         }
11264         return field || null;
11265     },
11266      /**
11267      * Mark fields in this form invalid in bulk.
11268      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11269      * @return {BasicForm} this
11270      */
11271     markInvalid : function(errors){
11272         if(errors instanceof Array){
11273             for(var i = 0, len = errors.length; i < len; i++){
11274                 var fieldError = errors[i];
11275                 var f = this.findField(fieldError.id);
11276                 if(f){
11277                     f.markInvalid(fieldError.msg);
11278                 }
11279             }
11280         }else{
11281             var field, id;
11282             for(id in errors){
11283                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11284                     field.markInvalid(errors[id]);
11285                 }
11286             }
11287         }
11288         //Roo.each(this.childForms || [], function (f) {
11289         //    f.markInvalid(errors);
11290         //});
11291
11292         return this;
11293     },
11294
11295     /**
11296      * Set values for fields in this form in bulk.
11297      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11298      * @return {BasicForm} this
11299      */
11300     setValues : function(values){
11301         if(values instanceof Array){ // array of objects
11302             for(var i = 0, len = values.length; i < len; i++){
11303                 var v = values[i];
11304                 var f = this.findField(v.id);
11305                 if(f){
11306                     f.setValue(v.value);
11307                     if(this.trackResetOnLoad){
11308                         f.originalValue = f.getValue();
11309                     }
11310                 }
11311             }
11312         }else{ // object hash
11313             var field, id;
11314             for(id in values){
11315                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11316
11317                     if (field.setFromData &&
11318                         field.valueField &&
11319                         field.displayField &&
11320                         // combos' with local stores can
11321                         // be queried via setValue()
11322                         // to set their value..
11323                         (field.store && !field.store.isLocal)
11324                         ) {
11325                         // it's a combo
11326                         var sd = { };
11327                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11328                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11329                         field.setFromData(sd);
11330
11331                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11332                         
11333                         field.setFromData(values);
11334                         
11335                     } else {
11336                         field.setValue(values[id]);
11337                     }
11338
11339
11340                     if(this.trackResetOnLoad){
11341                         field.originalValue = field.getValue();
11342                     }
11343                 }
11344             }
11345         }
11346
11347         //Roo.each(this.childForms || [], function (f) {
11348         //    f.setValues(values);
11349         //});
11350
11351         return this;
11352     },
11353
11354     /**
11355      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11356      * they are returned as an array.
11357      * @param {Boolean} asString
11358      * @return {Object}
11359      */
11360     getValues : function(asString){
11361         //if (this.childForms) {
11362             // copy values from the child forms
11363         //    Roo.each(this.childForms, function (f) {
11364         //        this.setValues(f.getValues());
11365         //    }, this);
11366         //}
11367
11368
11369
11370         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11371         if(asString === true){
11372             return fs;
11373         }
11374         return Roo.urlDecode(fs);
11375     },
11376
11377     /**
11378      * Returns the fields in this form as an object with key/value pairs.
11379      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11380      * @return {Object}
11381      */
11382     getFieldValues : function(with_hidden)
11383     {
11384         var items = this.getItems();
11385         var ret = {};
11386         items.each(function(f){
11387             
11388             if (!f.getName()) {
11389                 return;
11390             }
11391             
11392             var v = f.getValue();
11393             
11394             if (f.inputType =='radio') {
11395                 if (typeof(ret[f.getName()]) == 'undefined') {
11396                     ret[f.getName()] = ''; // empty..
11397                 }
11398
11399                 if (!f.el.dom.checked) {
11400                     return;
11401
11402                 }
11403                 v = f.el.dom.value;
11404
11405             }
11406             
11407             if(f.xtype == 'MoneyField'){
11408                 ret[f.currencyName] = f.getCurrency();
11409             }
11410
11411             // not sure if this supported any more..
11412             if ((typeof(v) == 'object') && f.getRawValue) {
11413                 v = f.getRawValue() ; // dates..
11414             }
11415             // combo boxes where name != hiddenName...
11416             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11417                 ret[f.name] = f.getRawValue();
11418             }
11419             ret[f.getName()] = v;
11420         });
11421
11422         return ret;
11423     },
11424
11425     /**
11426      * Clears all invalid messages in this form.
11427      * @return {BasicForm} this
11428      */
11429     clearInvalid : function(){
11430         var items = this.getItems();
11431
11432         items.each(function(f){
11433            f.clearInvalid();
11434         });
11435
11436         return this;
11437     },
11438
11439     /**
11440      * Resets this form.
11441      * @return {BasicForm} this
11442      */
11443     reset : function(){
11444         var items = this.getItems();
11445         items.each(function(f){
11446             f.reset();
11447         });
11448
11449         Roo.each(this.childForms || [], function (f) {
11450             f.reset();
11451         });
11452
11453
11454         return this;
11455     },
11456     
11457     getItems : function()
11458     {
11459         var r=new Roo.util.MixedCollection(false, function(o){
11460             return o.id || (o.id = Roo.id());
11461         });
11462         var iter = function(el) {
11463             if (el.inputEl) {
11464                 r.add(el);
11465             }
11466             if (!el.items) {
11467                 return;
11468             }
11469             Roo.each(el.items,function(e) {
11470                 iter(e);
11471             });
11472         };
11473
11474         iter(this);
11475         return r;
11476     },
11477     
11478     hideFields : function(items)
11479     {
11480         Roo.each(items, function(i){
11481             
11482             var f = this.findField(i);
11483             
11484             if(!f){
11485                 return;
11486             }
11487             
11488             f.hide();
11489             
11490         }, this);
11491     },
11492     
11493     showFields : function(items)
11494     {
11495         Roo.each(items, function(i){
11496             
11497             var f = this.findField(i);
11498             
11499             if(!f){
11500                 return;
11501             }
11502             
11503             f.show();
11504             
11505         }, this);
11506     }
11507
11508 });
11509
11510 Roo.apply(Roo.bootstrap.Form, {
11511     
11512     popover : {
11513         
11514         padding : 5,
11515         
11516         isApplied : false,
11517         
11518         isMasked : false,
11519         
11520         form : false,
11521         
11522         target : false,
11523         
11524         toolTip : false,
11525         
11526         intervalID : false,
11527         
11528         maskEl : false,
11529         
11530         apply : function()
11531         {
11532             if(this.isApplied){
11533                 return;
11534             }
11535             
11536             this.maskEl = {
11537                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11538                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11539                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11540                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11541             };
11542             
11543             this.maskEl.top.enableDisplayMode("block");
11544             this.maskEl.left.enableDisplayMode("block");
11545             this.maskEl.bottom.enableDisplayMode("block");
11546             this.maskEl.right.enableDisplayMode("block");
11547             
11548             this.toolTip = new Roo.bootstrap.Tooltip({
11549                 cls : 'roo-form-error-popover',
11550                 alignment : {
11551                     'left' : ['r-l', [-2,0], 'right'],
11552                     'right' : ['l-r', [2,0], 'left'],
11553                     'bottom' : ['tl-bl', [0,2], 'top'],
11554                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11555                 }
11556             });
11557             
11558             this.toolTip.render(Roo.get(document.body));
11559
11560             this.toolTip.el.enableDisplayMode("block");
11561             
11562             Roo.get(document.body).on('click', function(){
11563                 this.unmask();
11564             }, this);
11565             
11566             Roo.get(document.body).on('touchstart', function(){
11567                 this.unmask();
11568             }, this);
11569             
11570             this.isApplied = true
11571         },
11572         
11573         mask : function(form, target)
11574         {
11575             this.form = form;
11576             
11577             this.target = target;
11578             
11579             if(!this.form.errorMask || !target.el){
11580                 return;
11581             }
11582             
11583             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11584             
11585             Roo.log(scrollable);
11586             
11587             var ot = this.target.el.calcOffsetsTo(scrollable);
11588             
11589             var scrollTo = ot[1] - this.form.maskOffset;
11590             
11591             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11592             
11593             scrollable.scrollTo('top', scrollTo);
11594             
11595             var box = this.target.el.getBox();
11596             Roo.log(box);
11597             var zIndex = Roo.bootstrap.Modal.zIndex++;
11598
11599             
11600             this.maskEl.top.setStyle('position', 'absolute');
11601             this.maskEl.top.setStyle('z-index', zIndex);
11602             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11603             this.maskEl.top.setLeft(0);
11604             this.maskEl.top.setTop(0);
11605             this.maskEl.top.show();
11606             
11607             this.maskEl.left.setStyle('position', 'absolute');
11608             this.maskEl.left.setStyle('z-index', zIndex);
11609             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11610             this.maskEl.left.setLeft(0);
11611             this.maskEl.left.setTop(box.y - this.padding);
11612             this.maskEl.left.show();
11613
11614             this.maskEl.bottom.setStyle('position', 'absolute');
11615             this.maskEl.bottom.setStyle('z-index', zIndex);
11616             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11617             this.maskEl.bottom.setLeft(0);
11618             this.maskEl.bottom.setTop(box.bottom + this.padding);
11619             this.maskEl.bottom.show();
11620
11621             this.maskEl.right.setStyle('position', 'absolute');
11622             this.maskEl.right.setStyle('z-index', zIndex);
11623             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11624             this.maskEl.right.setLeft(box.right + this.padding);
11625             this.maskEl.right.setTop(box.y - this.padding);
11626             this.maskEl.right.show();
11627
11628             this.toolTip.bindEl = this.target.el;
11629
11630             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11631
11632             var tip = this.target.blankText;
11633
11634             if(this.target.getValue() !== '' ) {
11635                 
11636                 if (this.target.invalidText.length) {
11637                     tip = this.target.invalidText;
11638                 } else if (this.target.regexText.length){
11639                     tip = this.target.regexText;
11640                 }
11641             }
11642
11643             this.toolTip.show(tip);
11644
11645             this.intervalID = window.setInterval(function() {
11646                 Roo.bootstrap.Form.popover.unmask();
11647             }, 10000);
11648
11649             window.onwheel = function(){ return false;};
11650             
11651             (function(){ this.isMasked = true; }).defer(500, this);
11652             
11653         },
11654         
11655         unmask : function()
11656         {
11657             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11658                 return;
11659             }
11660             
11661             this.maskEl.top.setStyle('position', 'absolute');
11662             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11663             this.maskEl.top.hide();
11664
11665             this.maskEl.left.setStyle('position', 'absolute');
11666             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11667             this.maskEl.left.hide();
11668
11669             this.maskEl.bottom.setStyle('position', 'absolute');
11670             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11671             this.maskEl.bottom.hide();
11672
11673             this.maskEl.right.setStyle('position', 'absolute');
11674             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11675             this.maskEl.right.hide();
11676             
11677             this.toolTip.hide();
11678             
11679             this.toolTip.el.hide();
11680             
11681             window.onwheel = function(){ return true;};
11682             
11683             if(this.intervalID){
11684                 window.clearInterval(this.intervalID);
11685                 this.intervalID = false;
11686             }
11687             
11688             this.isMasked = false;
11689             
11690         }
11691         
11692     }
11693     
11694 });
11695
11696 /*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706 /**
11707  * @class Roo.form.VTypes
11708  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11709  * @singleton
11710  */
11711 Roo.form.VTypes = function(){
11712     // closure these in so they are only created once.
11713     var alpha = /^[a-zA-Z_]+$/;
11714     var alphanum = /^[a-zA-Z0-9_]+$/;
11715     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11716     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11717
11718     // All these messages and functions are configurable
11719     return {
11720         /**
11721          * The function used to validate email addresses
11722          * @param {String} value The email address
11723          */
11724         'email' : function(v){
11725             return email.test(v);
11726         },
11727         /**
11728          * The error text to display when the email validation function returns false
11729          * @type String
11730          */
11731         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11732         /**
11733          * The keystroke filter mask to be applied on email input
11734          * @type RegExp
11735          */
11736         'emailMask' : /[a-z0-9_\.\-@]/i,
11737
11738         /**
11739          * The function used to validate URLs
11740          * @param {String} value The URL
11741          */
11742         'url' : function(v){
11743             return url.test(v);
11744         },
11745         /**
11746          * The error text to display when the url validation function returns false
11747          * @type String
11748          */
11749         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11750         
11751         /**
11752          * The function used to validate alpha values
11753          * @param {String} value The value
11754          */
11755         'alpha' : function(v){
11756             return alpha.test(v);
11757         },
11758         /**
11759          * The error text to display when the alpha validation function returns false
11760          * @type String
11761          */
11762         'alphaText' : 'This field should only contain letters and _',
11763         /**
11764          * The keystroke filter mask to be applied on alpha input
11765          * @type RegExp
11766          */
11767         'alphaMask' : /[a-z_]/i,
11768
11769         /**
11770          * The function used to validate alphanumeric values
11771          * @param {String} value The value
11772          */
11773         'alphanum' : function(v){
11774             return alphanum.test(v);
11775         },
11776         /**
11777          * The error text to display when the alphanumeric validation function returns false
11778          * @type String
11779          */
11780         'alphanumText' : 'This field should only contain letters, numbers and _',
11781         /**
11782          * The keystroke filter mask to be applied on alphanumeric input
11783          * @type RegExp
11784          */
11785         'alphanumMask' : /[a-z0-9_]/i
11786     };
11787 }();/*
11788  * - LGPL
11789  *
11790  * Input
11791  * 
11792  */
11793
11794 /**
11795  * @class Roo.bootstrap.Input
11796  * @extends Roo.bootstrap.Component
11797  * Bootstrap Input class
11798  * @cfg {Boolean} disabled is it disabled
11799  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11800  * @cfg {String} name name of the input
11801  * @cfg {string} fieldLabel - the label associated
11802  * @cfg {string} placeholder - placeholder to put in text.
11803  * @cfg {string}  before - input group add on before
11804  * @cfg {string} after - input group add on after
11805  * @cfg {string} size - (lg|sm) or leave empty..
11806  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11807  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11808  * @cfg {Number} md colspan out of 12 for computer-sized screens
11809  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11810  * @cfg {string} value default value of the input
11811  * @cfg {Number} labelWidth set the width of label 
11812  * @cfg {Number} labellg set the width of label (1-12)
11813  * @cfg {Number} labelmd set the width of label (1-12)
11814  * @cfg {Number} labelsm set the width of label (1-12)
11815  * @cfg {Number} labelxs set the width of label (1-12)
11816  * @cfg {String} labelAlign (top|left)
11817  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11818  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11819  * @cfg {String} indicatorpos (left|right) default left
11820  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11821  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11822  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11823
11824  * @cfg {String} align (left|center|right) Default left
11825  * @cfg {Boolean} forceFeedback (true|false) Default false
11826  * 
11827  * @constructor
11828  * Create a new Input
11829  * @param {Object} config The config object
11830  */
11831
11832 Roo.bootstrap.Input = function(config){
11833     
11834     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11835     
11836     this.addEvents({
11837         /**
11838          * @event focus
11839          * Fires when this field receives input focus.
11840          * @param {Roo.form.Field} this
11841          */
11842         focus : true,
11843         /**
11844          * @event blur
11845          * Fires when this field loses input focus.
11846          * @param {Roo.form.Field} this
11847          */
11848         blur : true,
11849         /**
11850          * @event specialkey
11851          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11852          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11853          * @param {Roo.form.Field} this
11854          * @param {Roo.EventObject} e The event object
11855          */
11856         specialkey : true,
11857         /**
11858          * @event change
11859          * Fires just before the field blurs if the field value has changed.
11860          * @param {Roo.form.Field} this
11861          * @param {Mixed} newValue The new value
11862          * @param {Mixed} oldValue The original value
11863          */
11864         change : true,
11865         /**
11866          * @event invalid
11867          * Fires after the field has been marked as invalid.
11868          * @param {Roo.form.Field} this
11869          * @param {String} msg The validation message
11870          */
11871         invalid : true,
11872         /**
11873          * @event valid
11874          * Fires after the field has been validated with no errors.
11875          * @param {Roo.form.Field} this
11876          */
11877         valid : true,
11878          /**
11879          * @event keyup
11880          * Fires after the key up
11881          * @param {Roo.form.Field} this
11882          * @param {Roo.EventObject}  e The event Object
11883          */
11884         keyup : true,
11885         /**
11886          * @event paste
11887          * Fires after the user pastes into input
11888          * @param {Roo.form.Field} this
11889          * @param {Roo.EventObject}  e The event Object
11890          */
11891         paste : true
11892     });
11893 };
11894
11895 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11896      /**
11897      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11898       automatic validation (defaults to "keyup").
11899      */
11900     validationEvent : "keyup",
11901      /**
11902      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11903      */
11904     validateOnBlur : true,
11905     /**
11906      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11907      */
11908     validationDelay : 250,
11909      /**
11910      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11911      */
11912     focusClass : "x-form-focus",  // not needed???
11913     
11914        
11915     /**
11916      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11917      */
11918     invalidClass : "has-warning",
11919     
11920     /**
11921      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11922      */
11923     validClass : "has-success",
11924     
11925     /**
11926      * @cfg {Boolean} hasFeedback (true|false) default true
11927      */
11928     hasFeedback : true,
11929     
11930     /**
11931      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11932      */
11933     invalidFeedbackClass : "glyphicon-warning-sign",
11934     
11935     /**
11936      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11937      */
11938     validFeedbackClass : "glyphicon-ok",
11939     
11940     /**
11941      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11942      */
11943     selectOnFocus : false,
11944     
11945      /**
11946      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11947      */
11948     maskRe : null,
11949        /**
11950      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11951      */
11952     vtype : null,
11953     
11954       /**
11955      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11956      */
11957     disableKeyFilter : false,
11958     
11959        /**
11960      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11961      */
11962     disabled : false,
11963      /**
11964      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11965      */
11966     allowBlank : true,
11967     /**
11968      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11969      */
11970     blankText : "Please complete this mandatory field",
11971     
11972      /**
11973      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11974      */
11975     minLength : 0,
11976     /**
11977      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11978      */
11979     maxLength : Number.MAX_VALUE,
11980     /**
11981      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11982      */
11983     minLengthText : "The minimum length for this field is {0}",
11984     /**
11985      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11986      */
11987     maxLengthText : "The maximum length for this field is {0}",
11988   
11989     
11990     /**
11991      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11992      * If available, this function will be called only after the basic validators all return true, and will be passed the
11993      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11994      */
11995     validator : null,
11996     /**
11997      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11998      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11999      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12000      */
12001     regex : null,
12002     /**
12003      * @cfg {String} regexText -- Depricated - use Invalid Text
12004      */
12005     regexText : "",
12006     
12007     /**
12008      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12009      */
12010     invalidText : "",
12011     
12012     
12013     
12014     autocomplete: false,
12015     
12016     
12017     fieldLabel : '',
12018     inputType : 'text',
12019     
12020     name : false,
12021     placeholder: false,
12022     before : false,
12023     after : false,
12024     size : false,
12025     hasFocus : false,
12026     preventMark: false,
12027     isFormField : true,
12028     value : '',
12029     labelWidth : 2,
12030     labelAlign : false,
12031     readOnly : false,
12032     align : false,
12033     formatedValue : false,
12034     forceFeedback : false,
12035     
12036     indicatorpos : 'left',
12037     
12038     labellg : 0,
12039     labelmd : 0,
12040     labelsm : 0,
12041     labelxs : 0,
12042     
12043     capture : '',
12044     accept : '',
12045     
12046     parentLabelAlign : function()
12047     {
12048         var parent = this;
12049         while (parent.parent()) {
12050             parent = parent.parent();
12051             if (typeof(parent.labelAlign) !='undefined') {
12052                 return parent.labelAlign;
12053             }
12054         }
12055         return 'left';
12056         
12057     },
12058     
12059     getAutoCreate : function()
12060     {
12061         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12062         
12063         var id = Roo.id();
12064         
12065         var cfg = {};
12066         
12067         if(this.inputType != 'hidden'){
12068             cfg.cls = 'form-group' //input-group
12069         }
12070         
12071         var input =  {
12072             tag: 'input',
12073             id : id,
12074             type : this.inputType,
12075             value : this.value,
12076             cls : 'form-control',
12077             placeholder : this.placeholder || '',
12078             autocomplete : this.autocomplete || 'new-password'
12079         };
12080         if (this.inputType == 'file') {
12081             input.style = 'overflow:hidden'; // why not in CSS?
12082         }
12083         
12084         if(this.capture.length){
12085             input.capture = this.capture;
12086         }
12087         
12088         if(this.accept.length){
12089             input.accept = this.accept + "/*";
12090         }
12091         
12092         if(this.align){
12093             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12094         }
12095         
12096         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12097             input.maxLength = this.maxLength;
12098         }
12099         
12100         if (this.disabled) {
12101             input.disabled=true;
12102         }
12103         
12104         if (this.readOnly) {
12105             input.readonly=true;
12106         }
12107         
12108         if (this.name) {
12109             input.name = this.name;
12110         }
12111         
12112         if (this.size) {
12113             input.cls += ' input-' + this.size;
12114         }
12115         
12116         var settings=this;
12117         ['xs','sm','md','lg'].map(function(size){
12118             if (settings[size]) {
12119                 cfg.cls += ' col-' + size + '-' + settings[size];
12120             }
12121         });
12122         
12123         var inputblock = input;
12124         
12125         var feedback = {
12126             tag: 'span',
12127             cls: 'glyphicon form-control-feedback'
12128         };
12129             
12130         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12131             
12132             inputblock = {
12133                 cls : 'has-feedback',
12134                 cn :  [
12135                     input,
12136                     feedback
12137                 ] 
12138             };  
12139         }
12140         
12141         if (this.before || this.after) {
12142             
12143             inputblock = {
12144                 cls : 'input-group',
12145                 cn :  [] 
12146             };
12147             
12148             if (this.before && typeof(this.before) == 'string') {
12149                 
12150                 inputblock.cn.push({
12151                     tag :'span',
12152                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12153                     html : this.before
12154                 });
12155             }
12156             if (this.before && typeof(this.before) == 'object') {
12157                 this.before = Roo.factory(this.before);
12158                 
12159                 inputblock.cn.push({
12160                     tag :'span',
12161                     cls : 'roo-input-before input-group-prepend   input-group-' +
12162                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12163                 });
12164             }
12165             
12166             inputblock.cn.push(input);
12167             
12168             if (this.after && typeof(this.after) == 'string') {
12169                 inputblock.cn.push({
12170                     tag :'span',
12171                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12172                     html : this.after
12173                 });
12174             }
12175             if (this.after && typeof(this.after) == 'object') {
12176                 this.after = Roo.factory(this.after);
12177                 
12178                 inputblock.cn.push({
12179                     tag :'span',
12180                     cls : 'roo-input-after input-group-append  input-group-' +
12181                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12182                 });
12183             }
12184             
12185             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12186                 inputblock.cls += ' has-feedback';
12187                 inputblock.cn.push(feedback);
12188             }
12189         };
12190         var indicator = {
12191             tag : 'i',
12192             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12193             tooltip : 'This field is required'
12194         };
12195         if (this.allowBlank ) {
12196             indicator.style = this.allowBlank ? ' display:none' : '';
12197         }
12198         if (align ==='left' && this.fieldLabel.length) {
12199             
12200             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12201             
12202             cfg.cn = [
12203                 indicator,
12204                 {
12205                     tag: 'label',
12206                     'for' :  id,
12207                     cls : 'control-label col-form-label',
12208                     html : this.fieldLabel
12209
12210                 },
12211                 {
12212                     cls : "", 
12213                     cn: [
12214                         inputblock
12215                     ]
12216                 }
12217             ];
12218             
12219             var labelCfg = cfg.cn[1];
12220             var contentCfg = cfg.cn[2];
12221             
12222             if(this.indicatorpos == 'right'){
12223                 cfg.cn = [
12224                     {
12225                         tag: 'label',
12226                         'for' :  id,
12227                         cls : 'control-label col-form-label',
12228                         cn : [
12229                             {
12230                                 tag : 'span',
12231                                 html : this.fieldLabel
12232                             },
12233                             indicator
12234                         ]
12235                     },
12236                     {
12237                         cls : "",
12238                         cn: [
12239                             inputblock
12240                         ]
12241                     }
12242
12243                 ];
12244                 
12245                 labelCfg = cfg.cn[0];
12246                 contentCfg = cfg.cn[1];
12247             
12248             }
12249             
12250             if(this.labelWidth > 12){
12251                 labelCfg.style = "width: " + this.labelWidth + 'px';
12252             }
12253             
12254             if(this.labelWidth < 13 && this.labelmd == 0){
12255                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12256             }
12257             
12258             if(this.labellg > 0){
12259                 labelCfg.cls += ' col-lg-' + this.labellg;
12260                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12261             }
12262             
12263             if(this.labelmd > 0){
12264                 labelCfg.cls += ' col-md-' + this.labelmd;
12265                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12266             }
12267             
12268             if(this.labelsm > 0){
12269                 labelCfg.cls += ' col-sm-' + this.labelsm;
12270                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12271             }
12272             
12273             if(this.labelxs > 0){
12274                 labelCfg.cls += ' col-xs-' + this.labelxs;
12275                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12276             }
12277             
12278             
12279         } else if ( this.fieldLabel.length) {
12280                 
12281             
12282             
12283             cfg.cn = [
12284                 {
12285                     tag : 'i',
12286                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12287                     tooltip : 'This field is required',
12288                     style : this.allowBlank ? ' display:none' : '' 
12289                 },
12290                 {
12291                     tag: 'label',
12292                    //cls : 'input-group-addon',
12293                     html : this.fieldLabel
12294
12295                 },
12296
12297                inputblock
12298
12299            ];
12300            
12301            if(this.indicatorpos == 'right'){
12302        
12303                 cfg.cn = [
12304                     {
12305                         tag: 'label',
12306                        //cls : 'input-group-addon',
12307                         html : this.fieldLabel
12308
12309                     },
12310                     {
12311                         tag : 'i',
12312                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12313                         tooltip : 'This field is required',
12314                         style : this.allowBlank ? ' display:none' : '' 
12315                     },
12316
12317                    inputblock
12318
12319                ];
12320
12321             }
12322
12323         } else {
12324             
12325             cfg.cn = [
12326
12327                     inputblock
12328
12329             ];
12330                 
12331                 
12332         };
12333         
12334         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12335            cfg.cls += ' navbar-form';
12336         }
12337         
12338         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12339             // on BS4 we do this only if not form 
12340             cfg.cls += ' navbar-form';
12341             cfg.tag = 'li';
12342         }
12343         
12344         return cfg;
12345         
12346     },
12347     /**
12348      * return the real input element.
12349      */
12350     inputEl: function ()
12351     {
12352         return this.el.select('input.form-control',true).first();
12353     },
12354     
12355     tooltipEl : function()
12356     {
12357         return this.inputEl();
12358     },
12359     
12360     indicatorEl : function()
12361     {
12362         if (Roo.bootstrap.version == 4) {
12363             return false; // not enabled in v4 yet.
12364         }
12365         
12366         var indicator = this.el.select('i.roo-required-indicator',true).first();
12367         
12368         if(!indicator){
12369             return false;
12370         }
12371         
12372         return indicator;
12373         
12374     },
12375     
12376     setDisabled : function(v)
12377     {
12378         var i  = this.inputEl().dom;
12379         if (!v) {
12380             i.removeAttribute('disabled');
12381             return;
12382             
12383         }
12384         i.setAttribute('disabled','true');
12385     },
12386     initEvents : function()
12387     {
12388           
12389         this.inputEl().on("keydown" , this.fireKey,  this);
12390         this.inputEl().on("focus", this.onFocus,  this);
12391         this.inputEl().on("blur", this.onBlur,  this);
12392         
12393         this.inputEl().relayEvent('keyup', this);
12394         this.inputEl().relayEvent('paste', this);
12395         
12396         this.indicator = this.indicatorEl();
12397         
12398         if(this.indicator){
12399             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12400         }
12401  
12402         // reference to original value for reset
12403         this.originalValue = this.getValue();
12404         //Roo.form.TextField.superclass.initEvents.call(this);
12405         if(this.validationEvent == 'keyup'){
12406             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12407             this.inputEl().on('keyup', this.filterValidation, this);
12408         }
12409         else if(this.validationEvent !== false){
12410             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12411         }
12412         
12413         if(this.selectOnFocus){
12414             this.on("focus", this.preFocus, this);
12415             
12416         }
12417         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12418             this.inputEl().on("keypress", this.filterKeys, this);
12419         } else {
12420             this.inputEl().relayEvent('keypress', this);
12421         }
12422        /* if(this.grow){
12423             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12424             this.el.on("click", this.autoSize,  this);
12425         }
12426         */
12427         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12428             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12429         }
12430         
12431         if (typeof(this.before) == 'object') {
12432             this.before.render(this.el.select('.roo-input-before',true).first());
12433         }
12434         if (typeof(this.after) == 'object') {
12435             this.after.render(this.el.select('.roo-input-after',true).first());
12436         }
12437         
12438         this.inputEl().on('change', this.onChange, this);
12439         
12440     },
12441     filterValidation : function(e){
12442         if(!e.isNavKeyPress()){
12443             this.validationTask.delay(this.validationDelay);
12444         }
12445     },
12446      /**
12447      * Validates the field value
12448      * @return {Boolean} True if the value is valid, else false
12449      */
12450     validate : function(){
12451         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12452         if(this.disabled || this.validateValue(this.getRawValue())){
12453             this.markValid();
12454             return true;
12455         }
12456         
12457         this.markInvalid();
12458         return false;
12459     },
12460     
12461     
12462     /**
12463      * Validates a value according to the field's validation rules and marks the field as invalid
12464      * if the validation fails
12465      * @param {Mixed} value The value to validate
12466      * @return {Boolean} True if the value is valid, else false
12467      */
12468     validateValue : function(value)
12469     {
12470         if(this.getVisibilityEl().hasClass('hidden')){
12471             return true;
12472         }
12473         
12474         if(value.length < 1)  { // if it's blank
12475             if(this.allowBlank){
12476                 return true;
12477             }
12478             return false;
12479         }
12480         
12481         if(value.length < this.minLength){
12482             return false;
12483         }
12484         if(value.length > this.maxLength){
12485             return false;
12486         }
12487         if(this.vtype){
12488             var vt = Roo.form.VTypes;
12489             if(!vt[this.vtype](value, this)){
12490                 return false;
12491             }
12492         }
12493         if(typeof this.validator == "function"){
12494             var msg = this.validator(value);
12495             if(msg !== true){
12496                 return false;
12497             }
12498             if (typeof(msg) == 'string') {
12499                 this.invalidText = msg;
12500             }
12501         }
12502         
12503         if(this.regex && !this.regex.test(value)){
12504             return false;
12505         }
12506         
12507         return true;
12508     },
12509     
12510      // private
12511     fireKey : function(e){
12512         //Roo.log('field ' + e.getKey());
12513         if(e.isNavKeyPress()){
12514             this.fireEvent("specialkey", this, e);
12515         }
12516     },
12517     focus : function (selectText){
12518         if(this.rendered){
12519             this.inputEl().focus();
12520             if(selectText === true){
12521                 this.inputEl().dom.select();
12522             }
12523         }
12524         return this;
12525     } ,
12526     
12527     onFocus : function(){
12528         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12529            // this.el.addClass(this.focusClass);
12530         }
12531         if(!this.hasFocus){
12532             this.hasFocus = true;
12533             this.startValue = this.getValue();
12534             this.fireEvent("focus", this);
12535         }
12536     },
12537     
12538     beforeBlur : Roo.emptyFn,
12539
12540     
12541     // private
12542     onBlur : function(){
12543         this.beforeBlur();
12544         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12545             //this.el.removeClass(this.focusClass);
12546         }
12547         this.hasFocus = false;
12548         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12549             this.validate();
12550         }
12551         var v = this.getValue();
12552         if(String(v) !== String(this.startValue)){
12553             this.fireEvent('change', this, v, this.startValue);
12554         }
12555         this.fireEvent("blur", this);
12556     },
12557     
12558     onChange : function(e)
12559     {
12560         var v = this.getValue();
12561         if(String(v) !== String(this.startValue)){
12562             this.fireEvent('change', this, v, this.startValue);
12563         }
12564         
12565     },
12566     
12567     /**
12568      * Resets the current field value to the originally loaded value and clears any validation messages
12569      */
12570     reset : function(){
12571         this.setValue(this.originalValue);
12572         this.validate();
12573     },
12574      /**
12575      * Returns the name of the field
12576      * @return {Mixed} name The name field
12577      */
12578     getName: function(){
12579         return this.name;
12580     },
12581      /**
12582      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12583      * @return {Mixed} value The field value
12584      */
12585     getValue : function(){
12586         
12587         var v = this.inputEl().getValue();
12588         
12589         return v;
12590     },
12591     /**
12592      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12593      * @return {Mixed} value The field value
12594      */
12595     getRawValue : function(){
12596         var v = this.inputEl().getValue();
12597         
12598         return v;
12599     },
12600     
12601     /**
12602      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12603      * @param {Mixed} value The value to set
12604      */
12605     setRawValue : function(v){
12606         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12607     },
12608     
12609     selectText : function(start, end){
12610         var v = this.getRawValue();
12611         if(v.length > 0){
12612             start = start === undefined ? 0 : start;
12613             end = end === undefined ? v.length : end;
12614             var d = this.inputEl().dom;
12615             if(d.setSelectionRange){
12616                 d.setSelectionRange(start, end);
12617             }else if(d.createTextRange){
12618                 var range = d.createTextRange();
12619                 range.moveStart("character", start);
12620                 range.moveEnd("character", v.length-end);
12621                 range.select();
12622             }
12623         }
12624     },
12625     
12626     /**
12627      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12628      * @param {Mixed} value The value to set
12629      */
12630     setValue : function(v){
12631         this.value = v;
12632         if(this.rendered){
12633             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12634             this.validate();
12635         }
12636     },
12637     
12638     /*
12639     processValue : function(value){
12640         if(this.stripCharsRe){
12641             var newValue = value.replace(this.stripCharsRe, '');
12642             if(newValue !== value){
12643                 this.setRawValue(newValue);
12644                 return newValue;
12645             }
12646         }
12647         return value;
12648     },
12649   */
12650     preFocus : function(){
12651         
12652         if(this.selectOnFocus){
12653             this.inputEl().dom.select();
12654         }
12655     },
12656     filterKeys : function(e){
12657         var k = e.getKey();
12658         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12659             return;
12660         }
12661         var c = e.getCharCode(), cc = String.fromCharCode(c);
12662         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12663             return;
12664         }
12665         if(!this.maskRe.test(cc)){
12666             e.stopEvent();
12667         }
12668     },
12669      /**
12670      * Clear any invalid styles/messages for this field
12671      */
12672     clearInvalid : function(){
12673         
12674         if(!this.el || this.preventMark){ // not rendered
12675             return;
12676         }
12677         
12678         
12679         this.el.removeClass([this.invalidClass, 'is-invalid']);
12680         
12681         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12682             
12683             var feedback = this.el.select('.form-control-feedback', true).first();
12684             
12685             if(feedback){
12686                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12687             }
12688             
12689         }
12690         
12691         if(this.indicator){
12692             this.indicator.removeClass('visible');
12693             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12694         }
12695         
12696         this.fireEvent('valid', this);
12697     },
12698     
12699      /**
12700      * Mark this field as valid
12701      */
12702     markValid : function()
12703     {
12704         if(!this.el  || this.preventMark){ // not rendered...
12705             return;
12706         }
12707         
12708         this.el.removeClass([this.invalidClass, this.validClass]);
12709         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12710
12711         var feedback = this.el.select('.form-control-feedback', true).first();
12712             
12713         if(feedback){
12714             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12715         }
12716         
12717         if(this.indicator){
12718             this.indicator.removeClass('visible');
12719             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12720         }
12721         
12722         if(this.disabled){
12723             return;
12724         }
12725         
12726            
12727         if(this.allowBlank && !this.getRawValue().length){
12728             return;
12729         }
12730         if (Roo.bootstrap.version == 3) {
12731             this.el.addClass(this.validClass);
12732         } else {
12733             this.inputEl().addClass('is-valid');
12734         }
12735
12736         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12737             
12738             var feedback = this.el.select('.form-control-feedback', true).first();
12739             
12740             if(feedback){
12741                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12742                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12743             }
12744             
12745         }
12746         
12747         this.fireEvent('valid', this);
12748     },
12749     
12750      /**
12751      * Mark this field as invalid
12752      * @param {String} msg The validation message
12753      */
12754     markInvalid : function(msg)
12755     {
12756         if(!this.el  || this.preventMark){ // not rendered
12757             return;
12758         }
12759         
12760         this.el.removeClass([this.invalidClass, this.validClass]);
12761         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12762         
12763         var feedback = this.el.select('.form-control-feedback', true).first();
12764             
12765         if(feedback){
12766             this.el.select('.form-control-feedback', true).first().removeClass(
12767                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12768         }
12769
12770         if(this.disabled){
12771             return;
12772         }
12773         
12774         if(this.allowBlank && !this.getRawValue().length){
12775             return;
12776         }
12777         
12778         if(this.indicator){
12779             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12780             this.indicator.addClass('visible');
12781         }
12782         if (Roo.bootstrap.version == 3) {
12783             this.el.addClass(this.invalidClass);
12784         } else {
12785             this.inputEl().addClass('is-invalid');
12786         }
12787         
12788         
12789         
12790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12791             
12792             var feedback = this.el.select('.form-control-feedback', true).first();
12793             
12794             if(feedback){
12795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12796                 
12797                 if(this.getValue().length || this.forceFeedback){
12798                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12799                 }
12800                 
12801             }
12802             
12803         }
12804         
12805         this.fireEvent('invalid', this, msg);
12806     },
12807     // private
12808     SafariOnKeyDown : function(event)
12809     {
12810         // this is a workaround for a password hang bug on chrome/ webkit.
12811         if (this.inputEl().dom.type != 'password') {
12812             return;
12813         }
12814         
12815         var isSelectAll = false;
12816         
12817         if(this.inputEl().dom.selectionEnd > 0){
12818             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12819         }
12820         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12821             event.preventDefault();
12822             this.setValue('');
12823             return;
12824         }
12825         
12826         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12827             
12828             event.preventDefault();
12829             // this is very hacky as keydown always get's upper case.
12830             //
12831             var cc = String.fromCharCode(event.getCharCode());
12832             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12833             
12834         }
12835     },
12836     adjustWidth : function(tag, w){
12837         tag = tag.toLowerCase();
12838         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12839             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12840                 if(tag == 'input'){
12841                     return w + 2;
12842                 }
12843                 if(tag == 'textarea'){
12844                     return w-2;
12845                 }
12846             }else if(Roo.isOpera){
12847                 if(tag == 'input'){
12848                     return w + 2;
12849                 }
12850                 if(tag == 'textarea'){
12851                     return w-2;
12852                 }
12853             }
12854         }
12855         return w;
12856     },
12857     
12858     setFieldLabel : function(v)
12859     {
12860         if(!this.rendered){
12861             return;
12862         }
12863         
12864         if(this.indicatorEl()){
12865             var ar = this.el.select('label > span',true);
12866             
12867             if (ar.elements.length) {
12868                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12869                 this.fieldLabel = v;
12870                 return;
12871             }
12872             
12873             var br = this.el.select('label',true);
12874             
12875             if(br.elements.length) {
12876                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12877                 this.fieldLabel = v;
12878                 return;
12879             }
12880             
12881             Roo.log('Cannot Found any of label > span || label in input');
12882             return;
12883         }
12884         
12885         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12886         this.fieldLabel = v;
12887         
12888         
12889     }
12890 });
12891
12892  
12893 /*
12894  * - LGPL
12895  *
12896  * Input
12897  * 
12898  */
12899
12900 /**
12901  * @class Roo.bootstrap.TextArea
12902  * @extends Roo.bootstrap.Input
12903  * Bootstrap TextArea class
12904  * @cfg {Number} cols Specifies the visible width of a text area
12905  * @cfg {Number} rows Specifies the visible number of lines in a text area
12906  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12907  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12908  * @cfg {string} html text
12909  * 
12910  * @constructor
12911  * Create a new TextArea
12912  * @param {Object} config The config object
12913  */
12914
12915 Roo.bootstrap.TextArea = function(config){
12916     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12917    
12918 };
12919
12920 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12921      
12922     cols : false,
12923     rows : 5,
12924     readOnly : false,
12925     warp : 'soft',
12926     resize : false,
12927     value: false,
12928     html: false,
12929     
12930     getAutoCreate : function(){
12931         
12932         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12933         
12934         var id = Roo.id();
12935         
12936         var cfg = {};
12937         
12938         if(this.inputType != 'hidden'){
12939             cfg.cls = 'form-group' //input-group
12940         }
12941         
12942         var input =  {
12943             tag: 'textarea',
12944             id : id,
12945             warp : this.warp,
12946             rows : this.rows,
12947             value : this.value || '',
12948             html: this.html || '',
12949             cls : 'form-control',
12950             placeholder : this.placeholder || '' 
12951             
12952         };
12953         
12954         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12955             input.maxLength = this.maxLength;
12956         }
12957         
12958         if(this.resize){
12959             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12960         }
12961         
12962         if(this.cols){
12963             input.cols = this.cols;
12964         }
12965         
12966         if (this.readOnly) {
12967             input.readonly = true;
12968         }
12969         
12970         if (this.name) {
12971             input.name = this.name;
12972         }
12973         
12974         if (this.size) {
12975             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12976         }
12977         
12978         var settings=this;
12979         ['xs','sm','md','lg'].map(function(size){
12980             if (settings[size]) {
12981                 cfg.cls += ' col-' + size + '-' + settings[size];
12982             }
12983         });
12984         
12985         var inputblock = input;
12986         
12987         if(this.hasFeedback && !this.allowBlank){
12988             
12989             var feedback = {
12990                 tag: 'span',
12991                 cls: 'glyphicon form-control-feedback'
12992             };
12993
12994             inputblock = {
12995                 cls : 'has-feedback',
12996                 cn :  [
12997                     input,
12998                     feedback
12999                 ] 
13000             };  
13001         }
13002         
13003         
13004         if (this.before || this.after) {
13005             
13006             inputblock = {
13007                 cls : 'input-group',
13008                 cn :  [] 
13009             };
13010             if (this.before) {
13011                 inputblock.cn.push({
13012                     tag :'span',
13013                     cls : 'input-group-addon',
13014                     html : this.before
13015                 });
13016             }
13017             
13018             inputblock.cn.push(input);
13019             
13020             if(this.hasFeedback && !this.allowBlank){
13021                 inputblock.cls += ' has-feedback';
13022                 inputblock.cn.push(feedback);
13023             }
13024             
13025             if (this.after) {
13026                 inputblock.cn.push({
13027                     tag :'span',
13028                     cls : 'input-group-addon',
13029                     html : this.after
13030                 });
13031             }
13032             
13033         }
13034         
13035         if (align ==='left' && this.fieldLabel.length) {
13036             cfg.cn = [
13037                 {
13038                     tag: 'label',
13039                     'for' :  id,
13040                     cls : 'control-label',
13041                     html : this.fieldLabel
13042                 },
13043                 {
13044                     cls : "",
13045                     cn: [
13046                         inputblock
13047                     ]
13048                 }
13049
13050             ];
13051             
13052             if(this.labelWidth > 12){
13053                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13054             }
13055
13056             if(this.labelWidth < 13 && this.labelmd == 0){
13057                 this.labelmd = this.labelWidth;
13058             }
13059
13060             if(this.labellg > 0){
13061                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13062                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13063             }
13064
13065             if(this.labelmd > 0){
13066                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13067                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13068             }
13069
13070             if(this.labelsm > 0){
13071                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13072                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13073             }
13074
13075             if(this.labelxs > 0){
13076                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13077                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13078             }
13079             
13080         } else if ( this.fieldLabel.length) {
13081             cfg.cn = [
13082
13083                {
13084                    tag: 'label',
13085                    //cls : 'input-group-addon',
13086                    html : this.fieldLabel
13087
13088                },
13089
13090                inputblock
13091
13092            ];
13093
13094         } else {
13095
13096             cfg.cn = [
13097
13098                 inputblock
13099
13100             ];
13101                 
13102         }
13103         
13104         if (this.disabled) {
13105             input.disabled=true;
13106         }
13107         
13108         return cfg;
13109         
13110     },
13111     /**
13112      * return the real textarea element.
13113      */
13114     inputEl: function ()
13115     {
13116         return this.el.select('textarea.form-control',true).first();
13117     },
13118     
13119     /**
13120      * Clear any invalid styles/messages for this field
13121      */
13122     clearInvalid : function()
13123     {
13124         
13125         if(!this.el || this.preventMark){ // not rendered
13126             return;
13127         }
13128         
13129         var label = this.el.select('label', true).first();
13130         var icon = this.el.select('i.fa-star', true).first();
13131         
13132         if(label && icon){
13133             icon.remove();
13134         }
13135         this.el.removeClass( this.validClass);
13136         this.inputEl().removeClass('is-invalid');
13137          
13138         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13139             
13140             var feedback = this.el.select('.form-control-feedback', true).first();
13141             
13142             if(feedback){
13143                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13144             }
13145             
13146         }
13147         
13148         this.fireEvent('valid', this);
13149     },
13150     
13151      /**
13152      * Mark this field as valid
13153      */
13154     markValid : function()
13155     {
13156         if(!this.el  || this.preventMark){ // not rendered
13157             return;
13158         }
13159         
13160         this.el.removeClass([this.invalidClass, this.validClass]);
13161         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13162         
13163         var feedback = this.el.select('.form-control-feedback', true).first();
13164             
13165         if(feedback){
13166             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13167         }
13168
13169         if(this.disabled || this.allowBlank){
13170             return;
13171         }
13172         
13173         var label = this.el.select('label', true).first();
13174         var icon = this.el.select('i.fa-star', true).first();
13175         
13176         if(label && icon){
13177             icon.remove();
13178         }
13179         if (Roo.bootstrap.version == 3) {
13180             this.el.addClass(this.validClass);
13181         } else {
13182             this.inputEl().addClass('is-valid');
13183         }
13184         
13185         
13186         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13187             
13188             var feedback = this.el.select('.form-control-feedback', true).first();
13189             
13190             if(feedback){
13191                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13192                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13193             }
13194             
13195         }
13196         
13197         this.fireEvent('valid', this);
13198     },
13199     
13200      /**
13201      * Mark this field as invalid
13202      * @param {String} msg The validation message
13203      */
13204     markInvalid : function(msg)
13205     {
13206         if(!this.el  || this.preventMark){ // not rendered
13207             return;
13208         }
13209         
13210         this.el.removeClass([this.invalidClass, this.validClass]);
13211         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13212         
13213         var feedback = this.el.select('.form-control-feedback', true).first();
13214             
13215         if(feedback){
13216             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13217         }
13218
13219         if(this.disabled || this.allowBlank){
13220             return;
13221         }
13222         
13223         var label = this.el.select('label', true).first();
13224         var icon = this.el.select('i.fa-star', true).first();
13225         
13226         if(!this.getValue().length && label && !icon){
13227             this.el.createChild({
13228                 tag : 'i',
13229                 cls : 'text-danger fa fa-lg fa-star',
13230                 tooltip : 'This field is required',
13231                 style : 'margin-right:5px;'
13232             }, label, true);
13233         }
13234         
13235         if (Roo.bootstrap.version == 3) {
13236             this.el.addClass(this.invalidClass);
13237         } else {
13238             this.inputEl().addClass('is-invalid');
13239         }
13240         
13241         // fixme ... this may be depricated need to test..
13242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13243             
13244             var feedback = this.el.select('.form-control-feedback', true).first();
13245             
13246             if(feedback){
13247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13248                 
13249                 if(this.getValue().length || this.forceFeedback){
13250                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13251                 }
13252                 
13253             }
13254             
13255         }
13256         
13257         this.fireEvent('invalid', this, msg);
13258     }
13259 });
13260
13261  
13262 /*
13263  * - LGPL
13264  *
13265  * trigger field - base class for combo..
13266  * 
13267  */
13268  
13269 /**
13270  * @class Roo.bootstrap.TriggerField
13271  * @extends Roo.bootstrap.Input
13272  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13273  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13274  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13275  * for which you can provide a custom implementation.  For example:
13276  * <pre><code>
13277 var trigger = new Roo.bootstrap.TriggerField();
13278 trigger.onTriggerClick = myTriggerFn;
13279 trigger.applyTo('my-field');
13280 </code></pre>
13281  *
13282  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13283  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13284  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13285  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13286  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13287
13288  * @constructor
13289  * Create a new TriggerField.
13290  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13291  * to the base TextField)
13292  */
13293 Roo.bootstrap.TriggerField = function(config){
13294     this.mimicing = false;
13295     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13296 };
13297
13298 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13299     /**
13300      * @cfg {String} triggerClass A CSS class to apply to the trigger
13301      */
13302      /**
13303      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13304      */
13305     hideTrigger:false,
13306
13307     /**
13308      * @cfg {Boolean} removable (true|false) special filter default false
13309      */
13310     removable : false,
13311     
13312     /** @cfg {Boolean} grow @hide */
13313     /** @cfg {Number} growMin @hide */
13314     /** @cfg {Number} growMax @hide */
13315
13316     /**
13317      * @hide 
13318      * @method
13319      */
13320     autoSize: Roo.emptyFn,
13321     // private
13322     monitorTab : true,
13323     // private
13324     deferHeight : true,
13325
13326     
13327     actionMode : 'wrap',
13328     
13329     caret : false,
13330     
13331     
13332     getAutoCreate : function(){
13333        
13334         var align = this.labelAlign || this.parentLabelAlign();
13335         
13336         var id = Roo.id();
13337         
13338         var cfg = {
13339             cls: 'form-group' //input-group
13340         };
13341         
13342         
13343         var input =  {
13344             tag: 'input',
13345             id : id,
13346             type : this.inputType,
13347             cls : 'form-control',
13348             autocomplete: 'new-password',
13349             placeholder : this.placeholder || '' 
13350             
13351         };
13352         if (this.name) {
13353             input.name = this.name;
13354         }
13355         if (this.size) {
13356             input.cls += ' input-' + this.size;
13357         }
13358         
13359         if (this.disabled) {
13360             input.disabled=true;
13361         }
13362         
13363         var inputblock = input;
13364         
13365         if(this.hasFeedback && !this.allowBlank){
13366             
13367             var feedback = {
13368                 tag: 'span',
13369                 cls: 'glyphicon form-control-feedback'
13370             };
13371             
13372             if(this.removable && !this.editable  ){
13373                 inputblock = {
13374                     cls : 'has-feedback',
13375                     cn :  [
13376                         inputblock,
13377                         {
13378                             tag: 'button',
13379                             html : 'x',
13380                             cls : 'roo-combo-removable-btn close'
13381                         },
13382                         feedback
13383                     ] 
13384                 };
13385             } else {
13386                 inputblock = {
13387                     cls : 'has-feedback',
13388                     cn :  [
13389                         inputblock,
13390                         feedback
13391                     ] 
13392                 };
13393             }
13394
13395         } else {
13396             if(this.removable && !this.editable ){
13397                 inputblock = {
13398                     cls : 'roo-removable',
13399                     cn :  [
13400                         inputblock,
13401                         {
13402                             tag: 'button',
13403                             html : 'x',
13404                             cls : 'roo-combo-removable-btn close'
13405                         }
13406                     ] 
13407                 };
13408             }
13409         }
13410         
13411         if (this.before || this.after) {
13412             
13413             inputblock = {
13414                 cls : 'input-group',
13415                 cn :  [] 
13416             };
13417             if (this.before) {
13418                 inputblock.cn.push({
13419                     tag :'span',
13420                     cls : 'input-group-addon input-group-prepend input-group-text',
13421                     html : this.before
13422                 });
13423             }
13424             
13425             inputblock.cn.push(input);
13426             
13427             if(this.hasFeedback && !this.allowBlank){
13428                 inputblock.cls += ' has-feedback';
13429                 inputblock.cn.push(feedback);
13430             }
13431             
13432             if (this.after) {
13433                 inputblock.cn.push({
13434                     tag :'span',
13435                     cls : 'input-group-addon input-group-append input-group-text',
13436                     html : this.after
13437                 });
13438             }
13439             
13440         };
13441         
13442       
13443         
13444         var ibwrap = inputblock;
13445         
13446         if(this.multiple){
13447             ibwrap = {
13448                 tag: 'ul',
13449                 cls: 'roo-select2-choices',
13450                 cn:[
13451                     {
13452                         tag: 'li',
13453                         cls: 'roo-select2-search-field',
13454                         cn: [
13455
13456                             inputblock
13457                         ]
13458                     }
13459                 ]
13460             };
13461                 
13462         }
13463         
13464         var combobox = {
13465             cls: 'roo-select2-container input-group',
13466             cn: [
13467                  {
13468                     tag: 'input',
13469                     type : 'hidden',
13470                     cls: 'form-hidden-field'
13471                 },
13472                 ibwrap
13473             ]
13474         };
13475         
13476         if(!this.multiple && this.showToggleBtn){
13477             
13478             var caret = {
13479                         tag: 'span',
13480                         cls: 'caret'
13481              };
13482             if (this.caret != false) {
13483                 caret = {
13484                      tag: 'i',
13485                      cls: 'fa fa-' + this.caret
13486                 };
13487                 
13488             }
13489             
13490             combobox.cn.push({
13491                 tag :'span',
13492                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13493                 cn : [
13494                     Roo.bootstrap.version == 3 ? caret : '',
13495                     {
13496                         tag: 'span',
13497                         cls: 'combobox-clear',
13498                         cn  : [
13499                             {
13500                                 tag : 'i',
13501                                 cls: 'icon-remove'
13502                             }
13503                         ]
13504                     }
13505                 ]
13506
13507             })
13508         }
13509         
13510         if(this.multiple){
13511             combobox.cls += ' roo-select2-container-multi';
13512         }
13513          var indicator = {
13514             tag : 'i',
13515             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13516             tooltip : 'This field is required'
13517         };
13518         if (Roo.bootstrap.version == 4) {
13519             indicator = {
13520                 tag : 'i',
13521                 style : 'display:none'
13522             };
13523         }
13524         
13525         
13526         if (align ==='left' && this.fieldLabel.length) {
13527             
13528             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13529
13530             cfg.cn = [
13531                 indicator,
13532                 {
13533                     tag: 'label',
13534                     'for' :  id,
13535                     cls : 'control-label',
13536                     html : this.fieldLabel
13537
13538                 },
13539                 {
13540                     cls : "", 
13541                     cn: [
13542                         combobox
13543                     ]
13544                 }
13545
13546             ];
13547             
13548             var labelCfg = cfg.cn[1];
13549             var contentCfg = cfg.cn[2];
13550             
13551             if(this.indicatorpos == 'right'){
13552                 cfg.cn = [
13553                     {
13554                         tag: 'label',
13555                         'for' :  id,
13556                         cls : 'control-label',
13557                         cn : [
13558                             {
13559                                 tag : 'span',
13560                                 html : this.fieldLabel
13561                             },
13562                             indicator
13563                         ]
13564                     },
13565                     {
13566                         cls : "", 
13567                         cn: [
13568                             combobox
13569                         ]
13570                     }
13571
13572                 ];
13573                 
13574                 labelCfg = cfg.cn[0];
13575                 contentCfg = cfg.cn[1];
13576             }
13577             
13578             if(this.labelWidth > 12){
13579                 labelCfg.style = "width: " + this.labelWidth + 'px';
13580             }
13581             
13582             if(this.labelWidth < 13 && this.labelmd == 0){
13583                 this.labelmd = this.labelWidth;
13584             }
13585             
13586             if(this.labellg > 0){
13587                 labelCfg.cls += ' col-lg-' + this.labellg;
13588                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13589             }
13590             
13591             if(this.labelmd > 0){
13592                 labelCfg.cls += ' col-md-' + this.labelmd;
13593                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13594             }
13595             
13596             if(this.labelsm > 0){
13597                 labelCfg.cls += ' col-sm-' + this.labelsm;
13598                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13599             }
13600             
13601             if(this.labelxs > 0){
13602                 labelCfg.cls += ' col-xs-' + this.labelxs;
13603                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13604             }
13605             
13606         } else if ( this.fieldLabel.length) {
13607 //                Roo.log(" label");
13608             cfg.cn = [
13609                 indicator,
13610                {
13611                    tag: 'label',
13612                    //cls : 'input-group-addon',
13613                    html : this.fieldLabel
13614
13615                },
13616
13617                combobox
13618
13619             ];
13620             
13621             if(this.indicatorpos == 'right'){
13622                 
13623                 cfg.cn = [
13624                     {
13625                        tag: 'label',
13626                        cn : [
13627                            {
13628                                tag : 'span',
13629                                html : this.fieldLabel
13630                            },
13631                            indicator
13632                        ]
13633
13634                     },
13635                     combobox
13636
13637                 ];
13638
13639             }
13640
13641         } else {
13642             
13643 //                Roo.log(" no label && no align");
13644                 cfg = combobox
13645                      
13646                 
13647         }
13648         
13649         var settings=this;
13650         ['xs','sm','md','lg'].map(function(size){
13651             if (settings[size]) {
13652                 cfg.cls += ' col-' + size + '-' + settings[size];
13653             }
13654         });
13655         
13656         return cfg;
13657         
13658     },
13659     
13660     
13661     
13662     // private
13663     onResize : function(w, h){
13664 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13665 //        if(typeof w == 'number'){
13666 //            var x = w - this.trigger.getWidth();
13667 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13668 //            this.trigger.setStyle('left', x+'px');
13669 //        }
13670     },
13671
13672     // private
13673     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13674
13675     // private
13676     getResizeEl : function(){
13677         return this.inputEl();
13678     },
13679
13680     // private
13681     getPositionEl : function(){
13682         return this.inputEl();
13683     },
13684
13685     // private
13686     alignErrorIcon : function(){
13687         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13688     },
13689
13690     // private
13691     initEvents : function(){
13692         
13693         this.createList();
13694         
13695         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13696         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13697         if(!this.multiple && this.showToggleBtn){
13698             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13699             if(this.hideTrigger){
13700                 this.trigger.setDisplayed(false);
13701             }
13702             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13703         }
13704         
13705         if(this.multiple){
13706             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13707         }
13708         
13709         if(this.removable && !this.editable && !this.tickable){
13710             var close = this.closeTriggerEl();
13711             
13712             if(close){
13713                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13714                 close.on('click', this.removeBtnClick, this, close);
13715             }
13716         }
13717         
13718         //this.trigger.addClassOnOver('x-form-trigger-over');
13719         //this.trigger.addClassOnClick('x-form-trigger-click');
13720         
13721         //if(!this.width){
13722         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13723         //}
13724     },
13725     
13726     closeTriggerEl : function()
13727     {
13728         var close = this.el.select('.roo-combo-removable-btn', true).first();
13729         return close ? close : false;
13730     },
13731     
13732     removeBtnClick : function(e, h, el)
13733     {
13734         e.preventDefault();
13735         
13736         if(this.fireEvent("remove", this) !== false){
13737             this.reset();
13738             this.fireEvent("afterremove", this)
13739         }
13740     },
13741     
13742     createList : function()
13743     {
13744         this.list = Roo.get(document.body).createChild({
13745             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13746             cls: 'typeahead typeahead-long dropdown-menu shadow',
13747             style: 'display:none'
13748         });
13749         
13750         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13751         
13752     },
13753
13754     // private
13755     initTrigger : function(){
13756        
13757     },
13758
13759     // private
13760     onDestroy : function(){
13761         if(this.trigger){
13762             this.trigger.removeAllListeners();
13763           //  this.trigger.remove();
13764         }
13765         //if(this.wrap){
13766         //    this.wrap.remove();
13767         //}
13768         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13769     },
13770
13771     // private
13772     onFocus : function(){
13773         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13774         /*
13775         if(!this.mimicing){
13776             this.wrap.addClass('x-trigger-wrap-focus');
13777             this.mimicing = true;
13778             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13779             if(this.monitorTab){
13780                 this.el.on("keydown", this.checkTab, this);
13781             }
13782         }
13783         */
13784     },
13785
13786     // private
13787     checkTab : function(e){
13788         if(e.getKey() == e.TAB){
13789             this.triggerBlur();
13790         }
13791     },
13792
13793     // private
13794     onBlur : function(){
13795         // do nothing
13796     },
13797
13798     // private
13799     mimicBlur : function(e, t){
13800         /*
13801         if(!this.wrap.contains(t) && this.validateBlur()){
13802             this.triggerBlur();
13803         }
13804         */
13805     },
13806
13807     // private
13808     triggerBlur : function(){
13809         this.mimicing = false;
13810         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13811         if(this.monitorTab){
13812             this.el.un("keydown", this.checkTab, this);
13813         }
13814         //this.wrap.removeClass('x-trigger-wrap-focus');
13815         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13816     },
13817
13818     // private
13819     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13820     validateBlur : function(e, t){
13821         return true;
13822     },
13823
13824     // private
13825     onDisable : function(){
13826         this.inputEl().dom.disabled = true;
13827         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13828         //if(this.wrap){
13829         //    this.wrap.addClass('x-item-disabled');
13830         //}
13831     },
13832
13833     // private
13834     onEnable : function(){
13835         this.inputEl().dom.disabled = false;
13836         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13837         //if(this.wrap){
13838         //    this.el.removeClass('x-item-disabled');
13839         //}
13840     },
13841
13842     // private
13843     onShow : function(){
13844         var ae = this.getActionEl();
13845         
13846         if(ae){
13847             ae.dom.style.display = '';
13848             ae.dom.style.visibility = 'visible';
13849         }
13850     },
13851
13852     // private
13853     
13854     onHide : function(){
13855         var ae = this.getActionEl();
13856         ae.dom.style.display = 'none';
13857     },
13858
13859     /**
13860      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13861      * by an implementing function.
13862      * @method
13863      * @param {EventObject} e
13864      */
13865     onTriggerClick : Roo.emptyFn
13866 });
13867  
13868 /*
13869 * Licence: LGPL
13870 */
13871
13872 /**
13873  * @class Roo.bootstrap.CardUploader
13874  * @extends Roo.bootstrap.Button
13875  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13876  * @cfg {Number} errorTimeout default 3000
13877  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13878  * @cfg {Array}  html The button text.
13879
13880  *
13881  * @constructor
13882  * Create a new CardUploader
13883  * @param {Object} config The config object
13884  */
13885
13886 Roo.bootstrap.CardUploader = function(config){
13887     
13888  
13889     
13890     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13891     
13892     
13893     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13894         return r.data.id
13895      });
13896     
13897      this.addEvents({
13898          // raw events
13899         /**
13900          * @event preview
13901          * When a image is clicked on - and needs to display a slideshow or similar..
13902          * @param {Roo.bootstrap.Card} this
13903          * @param {Object} The image information data 
13904          *
13905          */
13906         'preview' : true,
13907          /**
13908          * @event download
13909          * When a the download link is clicked
13910          * @param {Roo.bootstrap.Card} this
13911          * @param {Object} The image information data  contains 
13912          */
13913         'download' : true
13914         
13915     });
13916 };
13917  
13918 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13919     
13920      
13921     errorTimeout : 3000,
13922      
13923     images : false,
13924    
13925     fileCollection : false,
13926     allowBlank : true,
13927     
13928     getAutoCreate : function()
13929     {
13930         
13931         var cfg =  {
13932             cls :'form-group' ,
13933             cn : [
13934                
13935                 {
13936                     tag: 'label',
13937                    //cls : 'input-group-addon',
13938                     html : this.fieldLabel
13939
13940                 },
13941
13942                 {
13943                     tag: 'input',
13944                     type : 'hidden',
13945                     name : this.name,
13946                     value : this.value,
13947                     cls : 'd-none  form-control'
13948                 },
13949                 
13950                 {
13951                     tag: 'input',
13952                     multiple : 'multiple',
13953                     type : 'file',
13954                     cls : 'd-none  roo-card-upload-selector'
13955                 },
13956                 
13957                 {
13958                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13959                 },
13960                 {
13961                     cls : 'card-columns roo-card-uploader-container'
13962                 }
13963
13964             ]
13965         };
13966            
13967          
13968         return cfg;
13969     },
13970     
13971     getChildContainer : function() /// what children are added to.
13972     {
13973         return this.containerEl;
13974     },
13975    
13976     getButtonContainer : function() /// what children are added to.
13977     {
13978         return this.el.select(".roo-card-uploader-button-container").first();
13979     },
13980    
13981     initEvents : function()
13982     {
13983         
13984         Roo.bootstrap.Input.prototype.initEvents.call(this);
13985         
13986         var t = this;
13987         this.addxtype({
13988             xns: Roo.bootstrap,
13989
13990             xtype : 'Button',
13991             container_method : 'getButtonContainer' ,            
13992             html :  this.html, // fix changable?
13993             cls : 'w-100 ',
13994             listeners : {
13995                 'click' : function(btn, e) {
13996                     t.onClick(e);
13997                 }
13998             }
13999         });
14000         
14001         
14002         
14003         
14004         this.urlAPI = (window.createObjectURL && window) || 
14005                                 (window.URL && URL.revokeObjectURL && URL) || 
14006                                 (window.webkitURL && webkitURL);
14007                         
14008          
14009          
14010          
14011         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14012         
14013         this.selectorEl.on('change', this.onFileSelected, this);
14014         if (this.images) {
14015             var t = this;
14016             this.images.forEach(function(img) {
14017                 t.addCard(img)
14018             });
14019             this.images = false;
14020         }
14021         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14022          
14023        
14024     },
14025     
14026    
14027     onClick : function(e)
14028     {
14029         e.preventDefault();
14030          
14031         this.selectorEl.dom.click();
14032          
14033     },
14034     
14035     onFileSelected : function(e)
14036     {
14037         e.preventDefault();
14038         
14039         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14040             return;
14041         }
14042         
14043         Roo.each(this.selectorEl.dom.files, function(file){    
14044             this.addFile(file);
14045         }, this);
14046          
14047     },
14048     
14049       
14050     
14051       
14052     
14053     addFile : function(file)
14054     {
14055            
14056         if(typeof(file) === 'string'){
14057             throw "Add file by name?"; // should not happen
14058             return;
14059         }
14060         
14061         if(!file || !this.urlAPI){
14062             return;
14063         }
14064         
14065         // file;
14066         // file.type;
14067         
14068         var _this = this;
14069         
14070         
14071         var url = _this.urlAPI.createObjectURL( file);
14072            
14073         this.addCard({
14074             id : Roo.bootstrap.CardUploader.ID--,
14075             is_uploaded : false,
14076             src : url,
14077             srcfile : file,
14078             title : file.name,
14079             mimetype : file.type,
14080             preview : false,
14081             is_deleted : 0
14082         });
14083         
14084     },
14085     
14086     /**
14087      * addCard - add an Attachment to the uploader
14088      * @param data - the data about the image to upload
14089      *
14090      * {
14091           id : 123
14092           title : "Title of file",
14093           is_uploaded : false,
14094           src : "http://.....",
14095           srcfile : { the File upload object },
14096           mimetype : file.type,
14097           preview : false,
14098           is_deleted : 0
14099           .. any other data...
14100         }
14101      *
14102      * 
14103     */
14104     
14105     addCard : function (data)
14106     {
14107         // hidden input element?
14108         // if the file is not an image...
14109         //then we need to use something other that and header_image
14110         var t = this;
14111         //   remove.....
14112         var footer = [
14113             {
14114                 xns : Roo.bootstrap,
14115                 xtype : 'CardFooter',
14116                  items: [
14117                     {
14118                         xns : Roo.bootstrap,
14119                         xtype : 'Element',
14120                         cls : 'd-flex',
14121                         items : [
14122                             
14123                             {
14124                                 xns : Roo.bootstrap,
14125                                 xtype : 'Button',
14126                                 html : String.format("<small>{0}</small>", data.title),
14127                                 cls : 'col-10 text-left',
14128                                 size: 'sm',
14129                                 weight: 'link',
14130                                 fa : 'download',
14131                                 listeners : {
14132                                     click : function() {
14133                                      
14134                                         t.fireEvent( "download", t, data );
14135                                     }
14136                                 }
14137                             },
14138                           
14139                             {
14140                                 xns : Roo.bootstrap,
14141                                 xtype : 'Button',
14142                                 style: 'max-height: 28px; ',
14143                                 size : 'sm',
14144                                 weight: 'danger',
14145                                 cls : 'col-2',
14146                                 fa : 'times',
14147                                 listeners : {
14148                                     click : function() {
14149                                         t.removeCard(data.id)
14150                                     }
14151                                 }
14152                             }
14153                         ]
14154                     }
14155                     
14156                 ] 
14157             }
14158             
14159         ];
14160         
14161         var cn = this.addxtype(
14162             {
14163                  
14164                 xns : Roo.bootstrap,
14165                 xtype : 'Card',
14166                 closeable : true,
14167                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14168                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14169                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14170                 data : data,
14171                 html : false,
14172                  
14173                 items : footer,
14174                 initEvents : function() {
14175                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14176                     var card = this;
14177                     this.imgEl = this.el.select('.card-img-top').first();
14178                     if (this.imgEl) {
14179                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14180                         this.imgEl.set({ 'pointer' : 'cursor' });
14181                                   
14182                     }
14183                     this.getCardFooter().addClass('p-1');
14184                     
14185                   
14186                 }
14187                 
14188             }
14189         );
14190         // dont' really need ot update items.
14191         // this.items.push(cn);
14192         this.fileCollection.add(cn);
14193         
14194         if (!data.srcfile) {
14195             this.updateInput();
14196             return;
14197         }
14198             
14199         var _t = this;
14200         var reader = new FileReader();
14201         reader.addEventListener("load", function() {  
14202             data.srcdata =  reader.result;
14203             _t.updateInput();
14204         });
14205         reader.readAsDataURL(data.srcfile);
14206         
14207         
14208         
14209     },
14210     removeCard : function(id)
14211     {
14212         
14213         var card  = this.fileCollection.get(id);
14214         card.data.is_deleted = 1;
14215         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14216         //this.fileCollection.remove(card);
14217         //this.items = this.items.filter(function(e) { return e != card });
14218         // dont' really need ot update items.
14219         card.el.dom.parentNode.removeChild(card.el.dom);
14220         this.updateInput();
14221
14222         
14223     },
14224     reset: function()
14225     {
14226         this.fileCollection.each(function(card) {
14227             if (card.el.dom && card.el.dom.parentNode) {
14228                 card.el.dom.parentNode.removeChild(card.el.dom);
14229             }
14230         });
14231         this.fileCollection.clear();
14232         this.updateInput();
14233     },
14234     
14235     updateInput : function()
14236     {
14237          var data = [];
14238         this.fileCollection.each(function(e) {
14239             data.push(e.data);
14240             
14241         });
14242         this.inputEl().dom.value = JSON.stringify(data);
14243         
14244         
14245         
14246     }
14247     
14248     
14249 });
14250
14251
14252 Roo.bootstrap.CardUploader.ID = -1;/*
14253  * Based on:
14254  * Ext JS Library 1.1.1
14255  * Copyright(c) 2006-2007, Ext JS, LLC.
14256  *
14257  * Originally Released Under LGPL - original licence link has changed is not relivant.
14258  *
14259  * Fork - LGPL
14260  * <script type="text/javascript">
14261  */
14262
14263
14264 /**
14265  * @class Roo.data.SortTypes
14266  * @singleton
14267  * Defines the default sorting (casting?) comparison functions used when sorting data.
14268  */
14269 Roo.data.SortTypes = {
14270     /**
14271      * Default sort that does nothing
14272      * @param {Mixed} s The value being converted
14273      * @return {Mixed} The comparison value
14274      */
14275     none : function(s){
14276         return s;
14277     },
14278     
14279     /**
14280      * The regular expression used to strip tags
14281      * @type {RegExp}
14282      * @property
14283      */
14284     stripTagsRE : /<\/?[^>]+>/gi,
14285     
14286     /**
14287      * Strips all HTML tags to sort on text only
14288      * @param {Mixed} s The value being converted
14289      * @return {String} The comparison value
14290      */
14291     asText : function(s){
14292         return String(s).replace(this.stripTagsRE, "");
14293     },
14294     
14295     /**
14296      * Strips all HTML tags to sort on text only - Case insensitive
14297      * @param {Mixed} s The value being converted
14298      * @return {String} The comparison value
14299      */
14300     asUCText : function(s){
14301         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14302     },
14303     
14304     /**
14305      * Case insensitive string
14306      * @param {Mixed} s The value being converted
14307      * @return {String} The comparison value
14308      */
14309     asUCString : function(s) {
14310         return String(s).toUpperCase();
14311     },
14312     
14313     /**
14314      * Date sorting
14315      * @param {Mixed} s The value being converted
14316      * @return {Number} The comparison value
14317      */
14318     asDate : function(s) {
14319         if(!s){
14320             return 0;
14321         }
14322         if(s instanceof Date){
14323             return s.getTime();
14324         }
14325         return Date.parse(String(s));
14326     },
14327     
14328     /**
14329      * Float sorting
14330      * @param {Mixed} s The value being converted
14331      * @return {Float} The comparison value
14332      */
14333     asFloat : function(s) {
14334         var val = parseFloat(String(s).replace(/,/g, ""));
14335         if(isNaN(val)) {
14336             val = 0;
14337         }
14338         return val;
14339     },
14340     
14341     /**
14342      * Integer sorting
14343      * @param {Mixed} s The value being converted
14344      * @return {Number} The comparison value
14345      */
14346     asInt : function(s) {
14347         var val = parseInt(String(s).replace(/,/g, ""));
14348         if(isNaN(val)) {
14349             val = 0;
14350         }
14351         return val;
14352     }
14353 };/*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363
14364 /**
14365 * @class Roo.data.Record
14366  * Instances of this class encapsulate both record <em>definition</em> information, and record
14367  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14368  * to access Records cached in an {@link Roo.data.Store} object.<br>
14369  * <p>
14370  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14371  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14372  * objects.<br>
14373  * <p>
14374  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14375  * @constructor
14376  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14377  * {@link #create}. The parameters are the same.
14378  * @param {Array} data An associative Array of data values keyed by the field name.
14379  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14380  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14381  * not specified an integer id is generated.
14382  */
14383 Roo.data.Record = function(data, id){
14384     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14385     this.data = data;
14386 };
14387
14388 /**
14389  * Generate a constructor for a specific record layout.
14390  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14391  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14392  * Each field definition object may contain the following properties: <ul>
14393  * <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,
14394  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14395  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14396  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14397  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14398  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14399  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14400  * this may be omitted.</p></li>
14401  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14402  * <ul><li>auto (Default, implies no conversion)</li>
14403  * <li>string</li>
14404  * <li>int</li>
14405  * <li>float</li>
14406  * <li>boolean</li>
14407  * <li>date</li></ul></p></li>
14408  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14409  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14410  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14411  * by the Reader into an object that will be stored in the Record. It is passed the
14412  * following parameters:<ul>
14413  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14414  * </ul></p></li>
14415  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14416  * </ul>
14417  * <br>usage:<br><pre><code>
14418 var TopicRecord = Roo.data.Record.create(
14419     {name: 'title', mapping: 'topic_title'},
14420     {name: 'author', mapping: 'username'},
14421     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14422     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14423     {name: 'lastPoster', mapping: 'user2'},
14424     {name: 'excerpt', mapping: 'post_text'}
14425 );
14426
14427 var myNewRecord = new TopicRecord({
14428     title: 'Do my job please',
14429     author: 'noobie',
14430     totalPosts: 1,
14431     lastPost: new Date(),
14432     lastPoster: 'Animal',
14433     excerpt: 'No way dude!'
14434 });
14435 myStore.add(myNewRecord);
14436 </code></pre>
14437  * @method create
14438  * @static
14439  */
14440 Roo.data.Record.create = function(o){
14441     var f = function(){
14442         f.superclass.constructor.apply(this, arguments);
14443     };
14444     Roo.extend(f, Roo.data.Record);
14445     var p = f.prototype;
14446     p.fields = new Roo.util.MixedCollection(false, function(field){
14447         return field.name;
14448     });
14449     for(var i = 0, len = o.length; i < len; i++){
14450         p.fields.add(new Roo.data.Field(o[i]));
14451     }
14452     f.getField = function(name){
14453         return p.fields.get(name);  
14454     };
14455     return f;
14456 };
14457
14458 Roo.data.Record.AUTO_ID = 1000;
14459 Roo.data.Record.EDIT = 'edit';
14460 Roo.data.Record.REJECT = 'reject';
14461 Roo.data.Record.COMMIT = 'commit';
14462
14463 Roo.data.Record.prototype = {
14464     /**
14465      * Readonly flag - true if this record has been modified.
14466      * @type Boolean
14467      */
14468     dirty : false,
14469     editing : false,
14470     error: null,
14471     modified: null,
14472
14473     // private
14474     join : function(store){
14475         this.store = store;
14476     },
14477
14478     /**
14479      * Set the named field to the specified value.
14480      * @param {String} name The name of the field to set.
14481      * @param {Object} value The value to set the field to.
14482      */
14483     set : function(name, value){
14484         if(this.data[name] == value){
14485             return;
14486         }
14487         this.dirty = true;
14488         if(!this.modified){
14489             this.modified = {};
14490         }
14491         if(typeof this.modified[name] == 'undefined'){
14492             this.modified[name] = this.data[name];
14493         }
14494         this.data[name] = value;
14495         if(!this.editing && this.store){
14496             this.store.afterEdit(this);
14497         }       
14498     },
14499
14500     /**
14501      * Get the value of the named field.
14502      * @param {String} name The name of the field to get the value of.
14503      * @return {Object} The value of the field.
14504      */
14505     get : function(name){
14506         return this.data[name]; 
14507     },
14508
14509     // private
14510     beginEdit : function(){
14511         this.editing = true;
14512         this.modified = {}; 
14513     },
14514
14515     // private
14516     cancelEdit : function(){
14517         this.editing = false;
14518         delete this.modified;
14519     },
14520
14521     // private
14522     endEdit : function(){
14523         this.editing = false;
14524         if(this.dirty && this.store){
14525             this.store.afterEdit(this);
14526         }
14527     },
14528
14529     /**
14530      * Usually called by the {@link Roo.data.Store} which owns the Record.
14531      * Rejects all changes made to the Record since either creation, or the last commit operation.
14532      * Modified fields are reverted to their original values.
14533      * <p>
14534      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14535      * of reject operations.
14536      */
14537     reject : function(){
14538         var m = this.modified;
14539         for(var n in m){
14540             if(typeof m[n] != "function"){
14541                 this.data[n] = m[n];
14542             }
14543         }
14544         this.dirty = false;
14545         delete this.modified;
14546         this.editing = false;
14547         if(this.store){
14548             this.store.afterReject(this);
14549         }
14550     },
14551
14552     /**
14553      * Usually called by the {@link Roo.data.Store} which owns the Record.
14554      * Commits all changes made to the Record since either creation, or the last commit operation.
14555      * <p>
14556      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14557      * of commit operations.
14558      */
14559     commit : function(){
14560         this.dirty = false;
14561         delete this.modified;
14562         this.editing = false;
14563         if(this.store){
14564             this.store.afterCommit(this);
14565         }
14566     },
14567
14568     // private
14569     hasError : function(){
14570         return this.error != null;
14571     },
14572
14573     // private
14574     clearError : function(){
14575         this.error = null;
14576     },
14577
14578     /**
14579      * Creates a copy of this record.
14580      * @param {String} id (optional) A new record id if you don't want to use this record's id
14581      * @return {Record}
14582      */
14583     copy : function(newId) {
14584         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14585     }
14586 };/*
14587  * Based on:
14588  * Ext JS Library 1.1.1
14589  * Copyright(c) 2006-2007, Ext JS, LLC.
14590  *
14591  * Originally Released Under LGPL - original licence link has changed is not relivant.
14592  *
14593  * Fork - LGPL
14594  * <script type="text/javascript">
14595  */
14596
14597
14598
14599 /**
14600  * @class Roo.data.Store
14601  * @extends Roo.util.Observable
14602  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14603  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14604  * <p>
14605  * 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
14606  * has no knowledge of the format of the data returned by the Proxy.<br>
14607  * <p>
14608  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14609  * instances from the data object. These records are cached and made available through accessor functions.
14610  * @constructor
14611  * Creates a new Store.
14612  * @param {Object} config A config object containing the objects needed for the Store to access data,
14613  * and read the data into Records.
14614  */
14615 Roo.data.Store = function(config){
14616     this.data = new Roo.util.MixedCollection(false);
14617     this.data.getKey = function(o){
14618         return o.id;
14619     };
14620     this.baseParams = {};
14621     // private
14622     this.paramNames = {
14623         "start" : "start",
14624         "limit" : "limit",
14625         "sort" : "sort",
14626         "dir" : "dir",
14627         "multisort" : "_multisort"
14628     };
14629
14630     if(config && config.data){
14631         this.inlineData = config.data;
14632         delete config.data;
14633     }
14634
14635     Roo.apply(this, config);
14636     
14637     if(this.reader){ // reader passed
14638         this.reader = Roo.factory(this.reader, Roo.data);
14639         this.reader.xmodule = this.xmodule || false;
14640         if(!this.recordType){
14641             this.recordType = this.reader.recordType;
14642         }
14643         if(this.reader.onMetaChange){
14644             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14645         }
14646     }
14647
14648     if(this.recordType){
14649         this.fields = this.recordType.prototype.fields;
14650     }
14651     this.modified = [];
14652
14653     this.addEvents({
14654         /**
14655          * @event datachanged
14656          * Fires when the data cache has changed, and a widget which is using this Store
14657          * as a Record cache should refresh its view.
14658          * @param {Store} this
14659          */
14660         datachanged : true,
14661         /**
14662          * @event metachange
14663          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14664          * @param {Store} this
14665          * @param {Object} meta The JSON metadata
14666          */
14667         metachange : true,
14668         /**
14669          * @event add
14670          * Fires when Records have been added to the Store
14671          * @param {Store} this
14672          * @param {Roo.data.Record[]} records The array of Records added
14673          * @param {Number} index The index at which the record(s) were added
14674          */
14675         add : true,
14676         /**
14677          * @event remove
14678          * Fires when a Record has been removed from the Store
14679          * @param {Store} this
14680          * @param {Roo.data.Record} record The Record that was removed
14681          * @param {Number} index The index at which the record was removed
14682          */
14683         remove : true,
14684         /**
14685          * @event update
14686          * Fires when a Record has been updated
14687          * @param {Store} this
14688          * @param {Roo.data.Record} record The Record that was updated
14689          * @param {String} operation The update operation being performed.  Value may be one of:
14690          * <pre><code>
14691  Roo.data.Record.EDIT
14692  Roo.data.Record.REJECT
14693  Roo.data.Record.COMMIT
14694          * </code></pre>
14695          */
14696         update : true,
14697         /**
14698          * @event clear
14699          * Fires when the data cache has been cleared.
14700          * @param {Store} this
14701          */
14702         clear : true,
14703         /**
14704          * @event beforeload
14705          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14706          * the load action will be canceled.
14707          * @param {Store} this
14708          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14709          */
14710         beforeload : true,
14711         /**
14712          * @event beforeloadadd
14713          * Fires after a new set of Records has been loaded.
14714          * @param {Store} this
14715          * @param {Roo.data.Record[]} records The Records that were loaded
14716          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717          */
14718         beforeloadadd : true,
14719         /**
14720          * @event load
14721          * Fires after a new set of Records has been loaded, before they are added to the store.
14722          * @param {Store} this
14723          * @param {Roo.data.Record[]} records The Records that were loaded
14724          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14725          * @params {Object} return from reader
14726          */
14727         load : true,
14728         /**
14729          * @event loadexception
14730          * Fires if an exception occurs in the Proxy during loading.
14731          * Called with the signature of the Proxy's "loadexception" event.
14732          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14733          * 
14734          * @param {Proxy} 
14735          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14736          * @param {Object} load options 
14737          * @param {Object} jsonData from your request (normally this contains the Exception)
14738          */
14739         loadexception : true
14740     });
14741     
14742     if(this.proxy){
14743         this.proxy = Roo.factory(this.proxy, Roo.data);
14744         this.proxy.xmodule = this.xmodule || false;
14745         this.relayEvents(this.proxy,  ["loadexception"]);
14746     }
14747     this.sortToggle = {};
14748     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14749
14750     Roo.data.Store.superclass.constructor.call(this);
14751
14752     if(this.inlineData){
14753         this.loadData(this.inlineData);
14754         delete this.inlineData;
14755     }
14756 };
14757
14758 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14759      /**
14760     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14761     * without a remote query - used by combo/forms at present.
14762     */
14763     
14764     /**
14765     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14766     */
14767     /**
14768     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14769     */
14770     /**
14771     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14772     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14773     */
14774     /**
14775     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14776     * on any HTTP request
14777     */
14778     /**
14779     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14780     */
14781     /**
14782     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14783     */
14784     multiSort: false,
14785     /**
14786     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14787     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14788     */
14789     remoteSort : false,
14790
14791     /**
14792     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14793      * loaded or when a record is removed. (defaults to false).
14794     */
14795     pruneModifiedRecords : false,
14796
14797     // private
14798     lastOptions : null,
14799
14800     /**
14801      * Add Records to the Store and fires the add event.
14802      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14803      */
14804     add : function(records){
14805         records = [].concat(records);
14806         for(var i = 0, len = records.length; i < len; i++){
14807             records[i].join(this);
14808         }
14809         var index = this.data.length;
14810         this.data.addAll(records);
14811         this.fireEvent("add", this, records, index);
14812     },
14813
14814     /**
14815      * Remove a Record from the Store and fires the remove event.
14816      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14817      */
14818     remove : function(record){
14819         var index = this.data.indexOf(record);
14820         this.data.removeAt(index);
14821  
14822         if(this.pruneModifiedRecords){
14823             this.modified.remove(record);
14824         }
14825         this.fireEvent("remove", this, record, index);
14826     },
14827
14828     /**
14829      * Remove all Records from the Store and fires the clear event.
14830      */
14831     removeAll : function(){
14832         this.data.clear();
14833         if(this.pruneModifiedRecords){
14834             this.modified = [];
14835         }
14836         this.fireEvent("clear", this);
14837     },
14838
14839     /**
14840      * Inserts Records to the Store at the given index and fires the add event.
14841      * @param {Number} index The start index at which to insert the passed Records.
14842      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14843      */
14844     insert : function(index, records){
14845         records = [].concat(records);
14846         for(var i = 0, len = records.length; i < len; i++){
14847             this.data.insert(index, records[i]);
14848             records[i].join(this);
14849         }
14850         this.fireEvent("add", this, records, index);
14851     },
14852
14853     /**
14854      * Get the index within the cache of the passed Record.
14855      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14856      * @return {Number} The index of the passed Record. Returns -1 if not found.
14857      */
14858     indexOf : function(record){
14859         return this.data.indexOf(record);
14860     },
14861
14862     /**
14863      * Get the index within the cache of the Record with the passed id.
14864      * @param {String} id The id of the Record to find.
14865      * @return {Number} The index of the Record. Returns -1 if not found.
14866      */
14867     indexOfId : function(id){
14868         return this.data.indexOfKey(id);
14869     },
14870
14871     /**
14872      * Get the Record with the specified id.
14873      * @param {String} id The id of the Record to find.
14874      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14875      */
14876     getById : function(id){
14877         return this.data.key(id);
14878     },
14879
14880     /**
14881      * Get the Record at the specified index.
14882      * @param {Number} index The index of the Record to find.
14883      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14884      */
14885     getAt : function(index){
14886         return this.data.itemAt(index);
14887     },
14888
14889     /**
14890      * Returns a range of Records between specified indices.
14891      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14892      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14893      * @return {Roo.data.Record[]} An array of Records
14894      */
14895     getRange : function(start, end){
14896         return this.data.getRange(start, end);
14897     },
14898
14899     // private
14900     storeOptions : function(o){
14901         o = Roo.apply({}, o);
14902         delete o.callback;
14903         delete o.scope;
14904         this.lastOptions = o;
14905     },
14906
14907     /**
14908      * Loads the Record cache from the configured Proxy using the configured Reader.
14909      * <p>
14910      * If using remote paging, then the first load call must specify the <em>start</em>
14911      * and <em>limit</em> properties in the options.params property to establish the initial
14912      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14913      * <p>
14914      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14915      * and this call will return before the new data has been loaded. Perform any post-processing
14916      * in a callback function, or in a "load" event handler.</strong>
14917      * <p>
14918      * @param {Object} options An object containing properties which control loading options:<ul>
14919      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14920      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14921      * passed the following arguments:<ul>
14922      * <li>r : Roo.data.Record[]</li>
14923      * <li>options: Options object from the load call</li>
14924      * <li>success: Boolean success indicator</li></ul></li>
14925      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14926      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14927      * </ul>
14928      */
14929     load : function(options){
14930         options = options || {};
14931         if(this.fireEvent("beforeload", this, options) !== false){
14932             this.storeOptions(options);
14933             var p = Roo.apply(options.params || {}, this.baseParams);
14934             // if meta was not loaded from remote source.. try requesting it.
14935             if (!this.reader.metaFromRemote) {
14936                 p._requestMeta = 1;
14937             }
14938             if(this.sortInfo && this.remoteSort){
14939                 var pn = this.paramNames;
14940                 p[pn["sort"]] = this.sortInfo.field;
14941                 p[pn["dir"]] = this.sortInfo.direction;
14942             }
14943             if (this.multiSort) {
14944                 var pn = this.paramNames;
14945                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14946             }
14947             
14948             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14949         }
14950     },
14951
14952     /**
14953      * Reloads the Record cache from the configured Proxy using the configured Reader and
14954      * the options from the last load operation performed.
14955      * @param {Object} options (optional) An object containing properties which may override the options
14956      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14957      * the most recently used options are reused).
14958      */
14959     reload : function(options){
14960         this.load(Roo.applyIf(options||{}, this.lastOptions));
14961     },
14962
14963     // private
14964     // Called as a callback by the Reader during a load operation.
14965     loadRecords : function(o, options, success){
14966         if(!o || success === false){
14967             if(success !== false){
14968                 this.fireEvent("load", this, [], options, o);
14969             }
14970             if(options.callback){
14971                 options.callback.call(options.scope || this, [], options, false);
14972             }
14973             return;
14974         }
14975         // if data returned failure - throw an exception.
14976         if (o.success === false) {
14977             // show a message if no listener is registered.
14978             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14979                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14980             }
14981             // loadmask wil be hooked into this..
14982             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14983             return;
14984         }
14985         var r = o.records, t = o.totalRecords || r.length;
14986         
14987         this.fireEvent("beforeloadadd", this, r, options, o);
14988         
14989         if(!options || options.add !== true){
14990             if(this.pruneModifiedRecords){
14991                 this.modified = [];
14992             }
14993             for(var i = 0, len = r.length; i < len; i++){
14994                 r[i].join(this);
14995             }
14996             if(this.snapshot){
14997                 this.data = this.snapshot;
14998                 delete this.snapshot;
14999             }
15000             this.data.clear();
15001             this.data.addAll(r);
15002             this.totalLength = t;
15003             this.applySort();
15004             this.fireEvent("datachanged", this);
15005         }else{
15006             this.totalLength = Math.max(t, this.data.length+r.length);
15007             this.add(r);
15008         }
15009         
15010         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15011                 
15012             var e = new Roo.data.Record({});
15013
15014             e.set(this.parent.displayField, this.parent.emptyTitle);
15015             e.set(this.parent.valueField, '');
15016
15017             this.insert(0, e);
15018         }
15019             
15020         this.fireEvent("load", this, r, options, o);
15021         if(options.callback){
15022             options.callback.call(options.scope || this, r, options, true);
15023         }
15024     },
15025
15026
15027     /**
15028      * Loads data from a passed data block. A Reader which understands the format of the data
15029      * must have been configured in the constructor.
15030      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15031      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15032      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15033      */
15034     loadData : function(o, append){
15035         var r = this.reader.readRecords(o);
15036         this.loadRecords(r, {add: append}, true);
15037     },
15038     
15039      /**
15040      * using 'cn' the nested child reader read the child array into it's child stores.
15041      * @param {Object} rec The record with a 'children array
15042      */
15043     loadDataFromChildren : function(rec)
15044     {
15045         this.loadData(this.reader.toLoadData(rec));
15046     },
15047     
15048
15049     /**
15050      * Gets the number of cached records.
15051      * <p>
15052      * <em>If using paging, this may not be the total size of the dataset. If the data object
15053      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15054      * the data set size</em>
15055      */
15056     getCount : function(){
15057         return this.data.length || 0;
15058     },
15059
15060     /**
15061      * Gets the total number of records in the dataset as returned by the server.
15062      * <p>
15063      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15064      * the dataset size</em>
15065      */
15066     getTotalCount : function(){
15067         return this.totalLength || 0;
15068     },
15069
15070     /**
15071      * Returns the sort state of the Store as an object with two properties:
15072      * <pre><code>
15073  field {String} The name of the field by which the Records are sorted
15074  direction {String} The sort order, "ASC" or "DESC"
15075      * </code></pre>
15076      */
15077     getSortState : function(){
15078         return this.sortInfo;
15079     },
15080
15081     // private
15082     applySort : function(){
15083         if(this.sortInfo && !this.remoteSort){
15084             var s = this.sortInfo, f = s.field;
15085             var st = this.fields.get(f).sortType;
15086             var fn = function(r1, r2){
15087                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15088                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15089             };
15090             this.data.sort(s.direction, fn);
15091             if(this.snapshot && this.snapshot != this.data){
15092                 this.snapshot.sort(s.direction, fn);
15093             }
15094         }
15095     },
15096
15097     /**
15098      * Sets the default sort column and order to be used by the next load operation.
15099      * @param {String} fieldName The name of the field to sort by.
15100      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15101      */
15102     setDefaultSort : function(field, dir){
15103         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15104     },
15105
15106     /**
15107      * Sort the Records.
15108      * If remote sorting is used, the sort is performed on the server, and the cache is
15109      * reloaded. If local sorting is used, the cache is sorted internally.
15110      * @param {String} fieldName The name of the field to sort by.
15111      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15112      */
15113     sort : function(fieldName, dir){
15114         var f = this.fields.get(fieldName);
15115         if(!dir){
15116             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15117             
15118             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15119                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15120             }else{
15121                 dir = f.sortDir;
15122             }
15123         }
15124         this.sortToggle[f.name] = dir;
15125         this.sortInfo = {field: f.name, direction: dir};
15126         if(!this.remoteSort){
15127             this.applySort();
15128             this.fireEvent("datachanged", this);
15129         }else{
15130             this.load(this.lastOptions);
15131         }
15132     },
15133
15134     /**
15135      * Calls the specified function for each of the Records in the cache.
15136      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15137      * Returning <em>false</em> aborts and exits the iteration.
15138      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15139      */
15140     each : function(fn, scope){
15141         this.data.each(fn, scope);
15142     },
15143
15144     /**
15145      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15146      * (e.g., during paging).
15147      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15148      */
15149     getModifiedRecords : function(){
15150         return this.modified;
15151     },
15152
15153     // private
15154     createFilterFn : function(property, value, anyMatch){
15155         if(!value.exec){ // not a regex
15156             value = String(value);
15157             if(value.length == 0){
15158                 return false;
15159             }
15160             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15161         }
15162         return function(r){
15163             return value.test(r.data[property]);
15164         };
15165     },
15166
15167     /**
15168      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15169      * @param {String} property A field on your records
15170      * @param {Number} start The record index to start at (defaults to 0)
15171      * @param {Number} end The last record index to include (defaults to length - 1)
15172      * @return {Number} The sum
15173      */
15174     sum : function(property, start, end){
15175         var rs = this.data.items, v = 0;
15176         start = start || 0;
15177         end = (end || end === 0) ? end : rs.length-1;
15178
15179         for(var i = start; i <= end; i++){
15180             v += (rs[i].data[property] || 0);
15181         }
15182         return v;
15183     },
15184
15185     /**
15186      * Filter the records by a specified property.
15187      * @param {String} field A field on your records
15188      * @param {String/RegExp} value Either a string that the field
15189      * should start with or a RegExp to test against the field
15190      * @param {Boolean} anyMatch True to match any part not just the beginning
15191      */
15192     filter : function(property, value, anyMatch){
15193         var fn = this.createFilterFn(property, value, anyMatch);
15194         return fn ? this.filterBy(fn) : this.clearFilter();
15195     },
15196
15197     /**
15198      * Filter by a function. The specified function will be called with each
15199      * record in this data source. If the function returns true the record is included,
15200      * otherwise it is filtered.
15201      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15202      * @param {Object} scope (optional) The scope of the function (defaults to this)
15203      */
15204     filterBy : function(fn, scope){
15205         this.snapshot = this.snapshot || this.data;
15206         this.data = this.queryBy(fn, scope||this);
15207         this.fireEvent("datachanged", this);
15208     },
15209
15210     /**
15211      * Query the records by a specified property.
15212      * @param {String} field A field on your records
15213      * @param {String/RegExp} value Either a string that the field
15214      * should start with or a RegExp to test against the field
15215      * @param {Boolean} anyMatch True to match any part not just the beginning
15216      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15217      */
15218     query : function(property, value, anyMatch){
15219         var fn = this.createFilterFn(property, value, anyMatch);
15220         return fn ? this.queryBy(fn) : this.data.clone();
15221     },
15222
15223     /**
15224      * Query by a function. The specified function will be called with each
15225      * record in this data source. If the function returns true the record is included
15226      * in the results.
15227      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15228      * @param {Object} scope (optional) The scope of the function (defaults to this)
15229       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15230      **/
15231     queryBy : function(fn, scope){
15232         var data = this.snapshot || this.data;
15233         return data.filterBy(fn, scope||this);
15234     },
15235
15236     /**
15237      * Collects unique values for a particular dataIndex from this store.
15238      * @param {String} dataIndex The property to collect
15239      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15240      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15241      * @return {Array} An array of the unique values
15242      **/
15243     collect : function(dataIndex, allowNull, bypassFilter){
15244         var d = (bypassFilter === true && this.snapshot) ?
15245                 this.snapshot.items : this.data.items;
15246         var v, sv, r = [], l = {};
15247         for(var i = 0, len = d.length; i < len; i++){
15248             v = d[i].data[dataIndex];
15249             sv = String(v);
15250             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15251                 l[sv] = true;
15252                 r[r.length] = v;
15253             }
15254         }
15255         return r;
15256     },
15257
15258     /**
15259      * Revert to a view of the Record cache with no filtering applied.
15260      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15261      */
15262     clearFilter : function(suppressEvent){
15263         if(this.snapshot && this.snapshot != this.data){
15264             this.data = this.snapshot;
15265             delete this.snapshot;
15266             if(suppressEvent !== true){
15267                 this.fireEvent("datachanged", this);
15268             }
15269         }
15270     },
15271
15272     // private
15273     afterEdit : function(record){
15274         if(this.modified.indexOf(record) == -1){
15275             this.modified.push(record);
15276         }
15277         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15278     },
15279     
15280     // private
15281     afterReject : function(record){
15282         this.modified.remove(record);
15283         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15284     },
15285
15286     // private
15287     afterCommit : function(record){
15288         this.modified.remove(record);
15289         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15290     },
15291
15292     /**
15293      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15294      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15295      */
15296     commitChanges : function(){
15297         var m = this.modified.slice(0);
15298         this.modified = [];
15299         for(var i = 0, len = m.length; i < len; i++){
15300             m[i].commit();
15301         }
15302     },
15303
15304     /**
15305      * Cancel outstanding changes on all changed records.
15306      */
15307     rejectChanges : function(){
15308         var m = this.modified.slice(0);
15309         this.modified = [];
15310         for(var i = 0, len = m.length; i < len; i++){
15311             m[i].reject();
15312         }
15313     },
15314
15315     onMetaChange : function(meta, rtype, o){
15316         this.recordType = rtype;
15317         this.fields = rtype.prototype.fields;
15318         delete this.snapshot;
15319         this.sortInfo = meta.sortInfo || this.sortInfo;
15320         this.modified = [];
15321         this.fireEvent('metachange', this, this.reader.meta);
15322     },
15323     
15324     moveIndex : function(data, type)
15325     {
15326         var index = this.indexOf(data);
15327         
15328         var newIndex = index + type;
15329         
15330         this.remove(data);
15331         
15332         this.insert(newIndex, data);
15333         
15334     }
15335 });/*
15336  * Based on:
15337  * Ext JS Library 1.1.1
15338  * Copyright(c) 2006-2007, Ext JS, LLC.
15339  *
15340  * Originally Released Under LGPL - original licence link has changed is not relivant.
15341  *
15342  * Fork - LGPL
15343  * <script type="text/javascript">
15344  */
15345
15346 /**
15347  * @class Roo.data.SimpleStore
15348  * @extends Roo.data.Store
15349  * Small helper class to make creating Stores from Array data easier.
15350  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15351  * @cfg {Array} fields An array of field definition objects, or field name strings.
15352  * @cfg {Object} an existing reader (eg. copied from another store)
15353  * @cfg {Array} data The multi-dimensional array of data
15354  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15355  * @cfg {Roo.data.Reader} reader  [not-required] 
15356  * @constructor
15357  * @param {Object} config
15358  */
15359 Roo.data.SimpleStore = function(config)
15360 {
15361     Roo.data.SimpleStore.superclass.constructor.call(this, {
15362         isLocal : true,
15363         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15364                 id: config.id
15365             },
15366             Roo.data.Record.create(config.fields)
15367         ),
15368         proxy : new Roo.data.MemoryProxy(config.data)
15369     });
15370     this.load();
15371 };
15372 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15373  * Based on:
15374  * Ext JS Library 1.1.1
15375  * Copyright(c) 2006-2007, Ext JS, LLC.
15376  *
15377  * Originally Released Under LGPL - original licence link has changed is not relivant.
15378  *
15379  * Fork - LGPL
15380  * <script type="text/javascript">
15381  */
15382
15383 /**
15384 /**
15385  * @extends Roo.data.Store
15386  * @class Roo.data.JsonStore
15387  * Small helper class to make creating Stores for JSON data easier. <br/>
15388 <pre><code>
15389 var store = new Roo.data.JsonStore({
15390     url: 'get-images.php',
15391     root: 'images',
15392     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15393 });
15394 </code></pre>
15395  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15396  * JsonReader and HttpProxy (unless inline data is provided).</b>
15397  * @cfg {Array} fields An array of field definition objects, or field name strings.
15398  * @constructor
15399  * @param {Object} config
15400  */
15401 Roo.data.JsonStore = function(c){
15402     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15403         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15404         reader: new Roo.data.JsonReader(c, c.fields)
15405     }));
15406 };
15407 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15408  * Based on:
15409  * Ext JS Library 1.1.1
15410  * Copyright(c) 2006-2007, Ext JS, LLC.
15411  *
15412  * Originally Released Under LGPL - original licence link has changed is not relivant.
15413  *
15414  * Fork - LGPL
15415  * <script type="text/javascript">
15416  */
15417
15418  
15419 Roo.data.Field = function(config){
15420     if(typeof config == "string"){
15421         config = {name: config};
15422     }
15423     Roo.apply(this, config);
15424     
15425     if(!this.type){
15426         this.type = "auto";
15427     }
15428     
15429     var st = Roo.data.SortTypes;
15430     // named sortTypes are supported, here we look them up
15431     if(typeof this.sortType == "string"){
15432         this.sortType = st[this.sortType];
15433     }
15434     
15435     // set default sortType for strings and dates
15436     if(!this.sortType){
15437         switch(this.type){
15438             case "string":
15439                 this.sortType = st.asUCString;
15440                 break;
15441             case "date":
15442                 this.sortType = st.asDate;
15443                 break;
15444             default:
15445                 this.sortType = st.none;
15446         }
15447     }
15448
15449     // define once
15450     var stripRe = /[\$,%]/g;
15451
15452     // prebuilt conversion function for this field, instead of
15453     // switching every time we're reading a value
15454     if(!this.convert){
15455         var cv, dateFormat = this.dateFormat;
15456         switch(this.type){
15457             case "":
15458             case "auto":
15459             case undefined:
15460                 cv = function(v){ return v; };
15461                 break;
15462             case "string":
15463                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15464                 break;
15465             case "int":
15466                 cv = function(v){
15467                     return v !== undefined && v !== null && v !== '' ?
15468                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15469                     };
15470                 break;
15471             case "float":
15472                 cv = function(v){
15473                     return v !== undefined && v !== null && v !== '' ?
15474                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15475                     };
15476                 break;
15477             case "bool":
15478             case "boolean":
15479                 cv = function(v){ return v === true || v === "true" || v == 1; };
15480                 break;
15481             case "date":
15482                 cv = function(v){
15483                     if(!v){
15484                         return '';
15485                     }
15486                     if(v instanceof Date){
15487                         return v;
15488                     }
15489                     if(dateFormat){
15490                         if(dateFormat == "timestamp"){
15491                             return new Date(v*1000);
15492                         }
15493                         return Date.parseDate(v, dateFormat);
15494                     }
15495                     var parsed = Date.parse(v);
15496                     return parsed ? new Date(parsed) : null;
15497                 };
15498              break;
15499             
15500         }
15501         this.convert = cv;
15502     }
15503 };
15504
15505 Roo.data.Field.prototype = {
15506     dateFormat: null,
15507     defaultValue: "",
15508     mapping: null,
15509     sortType : null,
15510     sortDir : "ASC"
15511 };/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521  
15522 // Base class for reading structured data from a data source.  This class is intended to be
15523 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15524
15525 /**
15526  * @class Roo.data.DataReader
15527  * @abstract
15528  * Base class for reading structured data from a data source.  This class is intended to be
15529  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15530  */
15531
15532 Roo.data.DataReader = function(meta, recordType){
15533     
15534     this.meta = meta;
15535     
15536     this.recordType = recordType instanceof Array ? 
15537         Roo.data.Record.create(recordType) : recordType;
15538 };
15539
15540 Roo.data.DataReader.prototype = {
15541     
15542     
15543     readerType : 'Data',
15544      /**
15545      * Create an empty record
15546      * @param {Object} data (optional) - overlay some values
15547      * @return {Roo.data.Record} record created.
15548      */
15549     newRow :  function(d) {
15550         var da =  {};
15551         this.recordType.prototype.fields.each(function(c) {
15552             switch( c.type) {
15553                 case 'int' : da[c.name] = 0; break;
15554                 case 'date' : da[c.name] = new Date(); break;
15555                 case 'float' : da[c.name] = 0.0; break;
15556                 case 'boolean' : da[c.name] = false; break;
15557                 default : da[c.name] = ""; break;
15558             }
15559             
15560         });
15561         return new this.recordType(Roo.apply(da, d));
15562     }
15563     
15564     
15565 };/*
15566  * Based on:
15567  * Ext JS Library 1.1.1
15568  * Copyright(c) 2006-2007, Ext JS, LLC.
15569  *
15570  * Originally Released Under LGPL - original licence link has changed is not relivant.
15571  *
15572  * Fork - LGPL
15573  * <script type="text/javascript">
15574  */
15575
15576 /**
15577  * @class Roo.data.DataProxy
15578  * @extends Roo.data.Observable
15579  * @abstract
15580  * This class is an abstract base class for implementations which provide retrieval of
15581  * unformatted data objects.<br>
15582  * <p>
15583  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15584  * (of the appropriate type which knows how to parse the data object) to provide a block of
15585  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15586  * <p>
15587  * Custom implementations must implement the load method as described in
15588  * {@link Roo.data.HttpProxy#load}.
15589  */
15590 Roo.data.DataProxy = function(){
15591     this.addEvents({
15592         /**
15593          * @event beforeload
15594          * Fires before a network request is made to retrieve a data object.
15595          * @param {Object} This DataProxy object.
15596          * @param {Object} params The params parameter to the load function.
15597          */
15598         beforeload : true,
15599         /**
15600          * @event load
15601          * Fires before the load method's callback is called.
15602          * @param {Object} This DataProxy object.
15603          * @param {Object} o The data object.
15604          * @param {Object} arg The callback argument object passed to the load function.
15605          */
15606         load : true,
15607         /**
15608          * @event loadexception
15609          * Fires if an Exception occurs during data retrieval.
15610          * @param {Object} This DataProxy object.
15611          * @param {Object} o The data object.
15612          * @param {Object} arg The callback argument object passed to the load function.
15613          * @param {Object} e The Exception.
15614          */
15615         loadexception : true
15616     });
15617     Roo.data.DataProxy.superclass.constructor.call(this);
15618 };
15619
15620 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15621
15622     /**
15623      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15624      */
15625 /*
15626  * Based on:
15627  * Ext JS Library 1.1.1
15628  * Copyright(c) 2006-2007, Ext JS, LLC.
15629  *
15630  * Originally Released Under LGPL - original licence link has changed is not relivant.
15631  *
15632  * Fork - LGPL
15633  * <script type="text/javascript">
15634  */
15635 /**
15636  * @class Roo.data.MemoryProxy
15637  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15638  * to the Reader when its load method is called.
15639  * @constructor
15640  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15641  */
15642 Roo.data.MemoryProxy = function(data){
15643     if (data.data) {
15644         data = data.data;
15645     }
15646     Roo.data.MemoryProxy.superclass.constructor.call(this);
15647     this.data = data;
15648 };
15649
15650 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15651     
15652     /**
15653      * Load data from the requested source (in this case an in-memory
15654      * data object passed to the constructor), read the data object into
15655      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15656      * process that block using the passed callback.
15657      * @param {Object} params This parameter is not used by the MemoryProxy class.
15658      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15659      * object into a block of Roo.data.Records.
15660      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15661      * The function must be passed <ul>
15662      * <li>The Record block object</li>
15663      * <li>The "arg" argument from the load function</li>
15664      * <li>A boolean success indicator</li>
15665      * </ul>
15666      * @param {Object} scope The scope in which to call the callback
15667      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15668      */
15669     load : function(params, reader, callback, scope, arg){
15670         params = params || {};
15671         var result;
15672         try {
15673             result = reader.readRecords(params.data ? params.data :this.data);
15674         }catch(e){
15675             this.fireEvent("loadexception", this, arg, null, e);
15676             callback.call(scope, null, arg, false);
15677             return;
15678         }
15679         callback.call(scope, result, arg, true);
15680     },
15681     
15682     // private
15683     update : function(params, records){
15684         
15685     }
15686 });/*
15687  * Based on:
15688  * Ext JS Library 1.1.1
15689  * Copyright(c) 2006-2007, Ext JS, LLC.
15690  *
15691  * Originally Released Under LGPL - original licence link has changed is not relivant.
15692  *
15693  * Fork - LGPL
15694  * <script type="text/javascript">
15695  */
15696 /**
15697  * @class Roo.data.HttpProxy
15698  * @extends Roo.data.DataProxy
15699  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15700  * configured to reference a certain URL.<br><br>
15701  * <p>
15702  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15703  * from which the running page was served.<br><br>
15704  * <p>
15705  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15706  * <p>
15707  * Be aware that to enable the browser to parse an XML document, the server must set
15708  * the Content-Type header in the HTTP response to "text/xml".
15709  * @constructor
15710  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15711  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15712  * will be used to make the request.
15713  */
15714 Roo.data.HttpProxy = function(conn){
15715     Roo.data.HttpProxy.superclass.constructor.call(this);
15716     // is conn a conn config or a real conn?
15717     this.conn = conn;
15718     this.useAjax = !conn || !conn.events;
15719   
15720 };
15721
15722 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15723     // thse are take from connection...
15724     
15725     /**
15726      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15727      */
15728     /**
15729      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15730      * extra parameters to each request made by this object. (defaults to undefined)
15731      */
15732     /**
15733      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15734      *  to each request made by this object. (defaults to undefined)
15735      */
15736     /**
15737      * @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)
15738      */
15739     /**
15740      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15741      */
15742      /**
15743      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15744      * @type Boolean
15745      */
15746   
15747
15748     /**
15749      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15750      * @type Boolean
15751      */
15752     /**
15753      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15754      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15755      * a finer-grained basis than the DataProxy events.
15756      */
15757     getConnection : function(){
15758         return this.useAjax ? Roo.Ajax : this.conn;
15759     },
15760
15761     /**
15762      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15763      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15764      * process that block using the passed callback.
15765      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15766      * for the request to the remote server.
15767      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15768      * object into a block of Roo.data.Records.
15769      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15770      * The function must be passed <ul>
15771      * <li>The Record block object</li>
15772      * <li>The "arg" argument from the load function</li>
15773      * <li>A boolean success indicator</li>
15774      * </ul>
15775      * @param {Object} scope The scope in which to call the callback
15776      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15777      */
15778     load : function(params, reader, callback, scope, arg){
15779         if(this.fireEvent("beforeload", this, params) !== false){
15780             var  o = {
15781                 params : params || {},
15782                 request: {
15783                     callback : callback,
15784                     scope : scope,
15785                     arg : arg
15786                 },
15787                 reader: reader,
15788                 callback : this.loadResponse,
15789                 scope: this
15790             };
15791             if(this.useAjax){
15792                 Roo.applyIf(o, this.conn);
15793                 if(this.activeRequest){
15794                     Roo.Ajax.abort(this.activeRequest);
15795                 }
15796                 this.activeRequest = Roo.Ajax.request(o);
15797             }else{
15798                 this.conn.request(o);
15799             }
15800         }else{
15801             callback.call(scope||this, null, arg, false);
15802         }
15803     },
15804
15805     // private
15806     loadResponse : function(o, success, response){
15807         delete this.activeRequest;
15808         if(!success){
15809             this.fireEvent("loadexception", this, o, response);
15810             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15811             return;
15812         }
15813         var result;
15814         try {
15815             result = o.reader.read(response);
15816         }catch(e){
15817             this.fireEvent("loadexception", this, o, response, e);
15818             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15819             return;
15820         }
15821         
15822         this.fireEvent("load", this, o, o.request.arg);
15823         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15824     },
15825
15826     // private
15827     update : function(dataSet){
15828
15829     },
15830
15831     // private
15832     updateResponse : function(dataSet){
15833
15834     }
15835 });/*
15836  * Based on:
15837  * Ext JS Library 1.1.1
15838  * Copyright(c) 2006-2007, Ext JS, LLC.
15839  *
15840  * Originally Released Under LGPL - original licence link has changed is not relivant.
15841  *
15842  * Fork - LGPL
15843  * <script type="text/javascript">
15844  */
15845
15846 /**
15847  * @class Roo.data.ScriptTagProxy
15848  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15849  * other than the originating domain of the running page.<br><br>
15850  * <p>
15851  * <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
15852  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15853  * <p>
15854  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15855  * source code that is used as the source inside a &lt;script> tag.<br><br>
15856  * <p>
15857  * In order for the browser to process the returned data, the server must wrap the data object
15858  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15859  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15860  * depending on whether the callback name was passed:
15861  * <p>
15862  * <pre><code>
15863 boolean scriptTag = false;
15864 String cb = request.getParameter("callback");
15865 if (cb != null) {
15866     scriptTag = true;
15867     response.setContentType("text/javascript");
15868 } else {
15869     response.setContentType("application/x-json");
15870 }
15871 Writer out = response.getWriter();
15872 if (scriptTag) {
15873     out.write(cb + "(");
15874 }
15875 out.print(dataBlock.toJsonString());
15876 if (scriptTag) {
15877     out.write(");");
15878 }
15879 </pre></code>
15880  *
15881  * @constructor
15882  * @param {Object} config A configuration object.
15883  */
15884 Roo.data.ScriptTagProxy = function(config){
15885     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15886     Roo.apply(this, config);
15887     this.head = document.getElementsByTagName("head")[0];
15888 };
15889
15890 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15891
15892 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15893     /**
15894      * @cfg {String} url The URL from which to request the data object.
15895      */
15896     /**
15897      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15898      */
15899     timeout : 30000,
15900     /**
15901      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15902      * the server the name of the callback function set up by the load call to process the returned data object.
15903      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15904      * javascript output which calls this named function passing the data object as its only parameter.
15905      */
15906     callbackParam : "callback",
15907     /**
15908      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15909      * name to the request.
15910      */
15911     nocache : true,
15912
15913     /**
15914      * Load data from the configured URL, read the data object into
15915      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15916      * process that block using the passed callback.
15917      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15918      * for the request to the remote server.
15919      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15920      * object into a block of Roo.data.Records.
15921      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15922      * The function must be passed <ul>
15923      * <li>The Record block object</li>
15924      * <li>The "arg" argument from the load function</li>
15925      * <li>A boolean success indicator</li>
15926      * </ul>
15927      * @param {Object} scope The scope in which to call the callback
15928      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15929      */
15930     load : function(params, reader, callback, scope, arg){
15931         if(this.fireEvent("beforeload", this, params) !== false){
15932
15933             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15934
15935             var url = this.url;
15936             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15937             if(this.nocache){
15938                 url += "&_dc=" + (new Date().getTime());
15939             }
15940             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15941             var trans = {
15942                 id : transId,
15943                 cb : "stcCallback"+transId,
15944                 scriptId : "stcScript"+transId,
15945                 params : params,
15946                 arg : arg,
15947                 url : url,
15948                 callback : callback,
15949                 scope : scope,
15950                 reader : reader
15951             };
15952             var conn = this;
15953
15954             window[trans.cb] = function(o){
15955                 conn.handleResponse(o, trans);
15956             };
15957
15958             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15959
15960             if(this.autoAbort !== false){
15961                 this.abort();
15962             }
15963
15964             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15965
15966             var script = document.createElement("script");
15967             script.setAttribute("src", url);
15968             script.setAttribute("type", "text/javascript");
15969             script.setAttribute("id", trans.scriptId);
15970             this.head.appendChild(script);
15971
15972             this.trans = trans;
15973         }else{
15974             callback.call(scope||this, null, arg, false);
15975         }
15976     },
15977
15978     // private
15979     isLoading : function(){
15980         return this.trans ? true : false;
15981     },
15982
15983     /**
15984      * Abort the current server request.
15985      */
15986     abort : function(){
15987         if(this.isLoading()){
15988             this.destroyTrans(this.trans);
15989         }
15990     },
15991
15992     // private
15993     destroyTrans : function(trans, isLoaded){
15994         this.head.removeChild(document.getElementById(trans.scriptId));
15995         clearTimeout(trans.timeoutId);
15996         if(isLoaded){
15997             window[trans.cb] = undefined;
15998             try{
15999                 delete window[trans.cb];
16000             }catch(e){}
16001         }else{
16002             // if hasn't been loaded, wait for load to remove it to prevent script error
16003             window[trans.cb] = function(){
16004                 window[trans.cb] = undefined;
16005                 try{
16006                     delete window[trans.cb];
16007                 }catch(e){}
16008             };
16009         }
16010     },
16011
16012     // private
16013     handleResponse : function(o, trans){
16014         this.trans = false;
16015         this.destroyTrans(trans, true);
16016         var result;
16017         try {
16018             result = trans.reader.readRecords(o);
16019         }catch(e){
16020             this.fireEvent("loadexception", this, o, trans.arg, e);
16021             trans.callback.call(trans.scope||window, null, trans.arg, false);
16022             return;
16023         }
16024         this.fireEvent("load", this, o, trans.arg);
16025         trans.callback.call(trans.scope||window, result, trans.arg, true);
16026     },
16027
16028     // private
16029     handleFailure : function(trans){
16030         this.trans = false;
16031         this.destroyTrans(trans, false);
16032         this.fireEvent("loadexception", this, null, trans.arg);
16033         trans.callback.call(trans.scope||window, null, trans.arg, false);
16034     }
16035 });/*
16036  * Based on:
16037  * Ext JS Library 1.1.1
16038  * Copyright(c) 2006-2007, Ext JS, LLC.
16039  *
16040  * Originally Released Under LGPL - original licence link has changed is not relivant.
16041  *
16042  * Fork - LGPL
16043  * <script type="text/javascript">
16044  */
16045
16046 /**
16047  * @class Roo.data.JsonReader
16048  * @extends Roo.data.DataReader
16049  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16050  * based on mappings in a provided Roo.data.Record constructor.
16051  * 
16052  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16053  * in the reply previously. 
16054  * 
16055  * <p>
16056  * Example code:
16057  * <pre><code>
16058 var RecordDef = Roo.data.Record.create([
16059     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16060     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16061 ]);
16062 var myReader = new Roo.data.JsonReader({
16063     totalProperty: "results",    // The property which contains the total dataset size (optional)
16064     root: "rows",                // The property which contains an Array of row objects
16065     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16066 }, RecordDef);
16067 </code></pre>
16068  * <p>
16069  * This would consume a JSON file like this:
16070  * <pre><code>
16071 { 'results': 2, 'rows': [
16072     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16073     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16074 }
16075 </code></pre>
16076  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16077  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16078  * paged from the remote server.
16079  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16080  * @cfg {String} root name of the property which contains the Array of row objects.
16081  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16082  * @cfg {Array} fields Array of field definition objects
16083  * @constructor
16084  * Create a new JsonReader
16085  * @param {Object} meta Metadata configuration options
16086  * @param {Object} recordType Either an Array of field definition objects,
16087  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16088  */
16089 Roo.data.JsonReader = function(meta, recordType){
16090     
16091     meta = meta || {};
16092     // set some defaults:
16093     Roo.applyIf(meta, {
16094         totalProperty: 'total',
16095         successProperty : 'success',
16096         root : 'data',
16097         id : 'id'
16098     });
16099     
16100     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16101 };
16102 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16103     
16104     readerType : 'Json',
16105     
16106     /**
16107      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16108      * Used by Store query builder to append _requestMeta to params.
16109      * 
16110      */
16111     metaFromRemote : false,
16112     /**
16113      * This method is only used by a DataProxy which has retrieved data from a remote server.
16114      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16115      * @return {Object} data A data block which is used by an Roo.data.Store object as
16116      * a cache of Roo.data.Records.
16117      */
16118     read : function(response){
16119         var json = response.responseText;
16120        
16121         var o = /* eval:var:o */ eval("("+json+")");
16122         if(!o) {
16123             throw {message: "JsonReader.read: Json object not found"};
16124         }
16125         
16126         if(o.metaData){
16127             
16128             delete this.ef;
16129             this.metaFromRemote = true;
16130             this.meta = o.metaData;
16131             this.recordType = Roo.data.Record.create(o.metaData.fields);
16132             this.onMetaChange(this.meta, this.recordType, o);
16133         }
16134         return this.readRecords(o);
16135     },
16136
16137     // private function a store will implement
16138     onMetaChange : function(meta, recordType, o){
16139
16140     },
16141
16142     /**
16143          * @ignore
16144          */
16145     simpleAccess: function(obj, subsc) {
16146         return obj[subsc];
16147     },
16148
16149         /**
16150          * @ignore
16151          */
16152     getJsonAccessor: function(){
16153         var re = /[\[\.]/;
16154         return function(expr) {
16155             try {
16156                 return(re.test(expr))
16157                     ? new Function("obj", "return obj." + expr)
16158                     : function(obj){
16159                         return obj[expr];
16160                     };
16161             } catch(e){}
16162             return Roo.emptyFn;
16163         };
16164     }(),
16165
16166     /**
16167      * Create a data block containing Roo.data.Records from an XML document.
16168      * @param {Object} o An object which contains an Array of row objects in the property specified
16169      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16170      * which contains the total size of the dataset.
16171      * @return {Object} data A data block which is used by an Roo.data.Store object as
16172      * a cache of Roo.data.Records.
16173      */
16174     readRecords : function(o){
16175         /**
16176          * After any data loads, the raw JSON data is available for further custom processing.
16177          * @type Object
16178          */
16179         this.o = o;
16180         var s = this.meta, Record = this.recordType,
16181             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16182
16183 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16184         if (!this.ef) {
16185             if(s.totalProperty) {
16186                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16187                 }
16188                 if(s.successProperty) {
16189                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16190                 }
16191                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16192                 if (s.id) {
16193                         var g = this.getJsonAccessor(s.id);
16194                         this.getId = function(rec) {
16195                                 var r = g(rec);  
16196                                 return (r === undefined || r === "") ? null : r;
16197                         };
16198                 } else {
16199                         this.getId = function(){return null;};
16200                 }
16201             this.ef = [];
16202             for(var jj = 0; jj < fl; jj++){
16203                 f = fi[jj];
16204                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16205                 this.ef[jj] = this.getJsonAccessor(map);
16206             }
16207         }
16208
16209         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16210         if(s.totalProperty){
16211             var vt = parseInt(this.getTotal(o), 10);
16212             if(!isNaN(vt)){
16213                 totalRecords = vt;
16214             }
16215         }
16216         if(s.successProperty){
16217             var vs = this.getSuccess(o);
16218             if(vs === false || vs === 'false'){
16219                 success = false;
16220             }
16221         }
16222         var records = [];
16223         for(var i = 0; i < c; i++){
16224                 var n = root[i];
16225             var values = {};
16226             var id = this.getId(n);
16227             for(var j = 0; j < fl; j++){
16228                 f = fi[j];
16229             var v = this.ef[j](n);
16230             if (!f.convert) {
16231                 Roo.log('missing convert for ' + f.name);
16232                 Roo.log(f);
16233                 continue;
16234             }
16235             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16236             }
16237             var record = new Record(values, id);
16238             record.json = n;
16239             records[i] = record;
16240         }
16241         return {
16242             raw : o,
16243             success : success,
16244             records : records,
16245             totalRecords : totalRecords
16246         };
16247     },
16248     // used when loading children.. @see loadDataFromChildren
16249     toLoadData: function(rec)
16250     {
16251         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16252         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16253         return { data : data, total : data.length };
16254         
16255     }
16256 });/*
16257  * Based on:
16258  * Ext JS Library 1.1.1
16259  * Copyright(c) 2006-2007, Ext JS, LLC.
16260  *
16261  * Originally Released Under LGPL - original licence link has changed is not relivant.
16262  *
16263  * Fork - LGPL
16264  * <script type="text/javascript">
16265  */
16266
16267 /**
16268  * @class Roo.data.ArrayReader
16269  * @extends Roo.data.DataReader
16270  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16271  * Each element of that Array represents a row of data fields. The
16272  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16273  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16274  * <p>
16275  * Example code:.
16276  * <pre><code>
16277 var RecordDef = Roo.data.Record.create([
16278     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16279     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16280 ]);
16281 var myReader = new Roo.data.ArrayReader({
16282     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16283 }, RecordDef);
16284 </code></pre>
16285  * <p>
16286  * This would consume an Array like this:
16287  * <pre><code>
16288 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16289   </code></pre>
16290  
16291  * @constructor
16292  * Create a new JsonReader
16293  * @param {Object} meta Metadata configuration options.
16294  * @param {Object|Array} recordType Either an Array of field definition objects
16295  * 
16296  * @cfg {Array} fields Array of field definition objects
16297  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16298  * as specified to {@link Roo.data.Record#create},
16299  * or an {@link Roo.data.Record} object
16300  *
16301  * 
16302  * created using {@link Roo.data.Record#create}.
16303  */
16304 Roo.data.ArrayReader = function(meta, recordType)
16305 {    
16306     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16307 };
16308
16309 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16310     
16311       /**
16312      * Create a data block containing Roo.data.Records from an XML document.
16313      * @param {Object} o An Array of row objects which represents the dataset.
16314      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16315      * a cache of Roo.data.Records.
16316      */
16317     readRecords : function(o)
16318     {
16319         var sid = this.meta ? this.meta.id : null;
16320         var recordType = this.recordType, fields = recordType.prototype.fields;
16321         var records = [];
16322         var root = o;
16323         for(var i = 0; i < root.length; i++){
16324             var n = root[i];
16325             var values = {};
16326             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16327             for(var j = 0, jlen = fields.length; j < jlen; j++){
16328                 var f = fields.items[j];
16329                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16330                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16331                 v = f.convert(v);
16332                 values[f.name] = v;
16333             }
16334             var record = new recordType(values, id);
16335             record.json = n;
16336             records[records.length] = record;
16337         }
16338         return {
16339             records : records,
16340             totalRecords : records.length
16341         };
16342     },
16343     // used when loading children.. @see loadDataFromChildren
16344     toLoadData: function(rec)
16345     {
16346         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16347         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16348         
16349     }
16350     
16351     
16352 });/*
16353  * - LGPL
16354  * * 
16355  */
16356
16357 /**
16358  * @class Roo.bootstrap.ComboBox
16359  * @extends Roo.bootstrap.TriggerField
16360  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16361  * @cfg {Boolean} append (true|false) default false
16362  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16363  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16364  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16365  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16366  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16367  * @cfg {Boolean} animate default true
16368  * @cfg {Boolean} emptyResultText only for touch device
16369  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16370  * @cfg {String} emptyTitle default ''
16371  * @cfg {Number} width fixed with? experimental
16372  * @constructor
16373  * Create a new ComboBox.
16374  * @param {Object} config Configuration options
16375  */
16376 Roo.bootstrap.ComboBox = function(config){
16377     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16378     this.addEvents({
16379         /**
16380          * @event expand
16381          * Fires when the dropdown list is expanded
16382         * @param {Roo.bootstrap.ComboBox} combo This combo box
16383         */
16384         'expand' : true,
16385         /**
16386          * @event collapse
16387          * Fires when the dropdown list is collapsed
16388         * @param {Roo.bootstrap.ComboBox} combo This combo box
16389         */
16390         'collapse' : true,
16391         /**
16392          * @event beforeselect
16393          * Fires before a list item is selected. Return false to cancel the selection.
16394         * @param {Roo.bootstrap.ComboBox} combo This combo box
16395         * @param {Roo.data.Record} record The data record returned from the underlying store
16396         * @param {Number} index The index of the selected item in the dropdown list
16397         */
16398         'beforeselect' : true,
16399         /**
16400          * @event select
16401          * Fires when a list item is selected
16402         * @param {Roo.bootstrap.ComboBox} combo This combo box
16403         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16404         * @param {Number} index The index of the selected item in the dropdown list
16405         */
16406         'select' : true,
16407         /**
16408          * @event beforequery
16409          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16410          * The event object passed has these properties:
16411         * @param {Roo.bootstrap.ComboBox} combo This combo box
16412         * @param {String} query The query
16413         * @param {Boolean} forceAll true to force "all" query
16414         * @param {Boolean} cancel true to cancel the query
16415         * @param {Object} e The query event object
16416         */
16417         'beforequery': true,
16418          /**
16419          * @event add
16420          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16421         * @param {Roo.bootstrap.ComboBox} combo This combo box
16422         */
16423         'add' : true,
16424         /**
16425          * @event edit
16426          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16427         * @param {Roo.bootstrap.ComboBox} combo This combo box
16428         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16429         */
16430         'edit' : true,
16431         /**
16432          * @event remove
16433          * Fires when the remove value from the combobox array
16434         * @param {Roo.bootstrap.ComboBox} combo This combo box
16435         */
16436         'remove' : true,
16437         /**
16438          * @event afterremove
16439          * Fires when the remove value from the combobox array
16440         * @param {Roo.bootstrap.ComboBox} combo This combo box
16441         */
16442         'afterremove' : true,
16443         /**
16444          * @event specialfilter
16445          * Fires when specialfilter
16446             * @param {Roo.bootstrap.ComboBox} combo This combo box
16447             */
16448         'specialfilter' : true,
16449         /**
16450          * @event tick
16451          * Fires when tick the element
16452             * @param {Roo.bootstrap.ComboBox} combo This combo box
16453             */
16454         'tick' : true,
16455         /**
16456          * @event touchviewdisplay
16457          * Fires when touch view require special display (default is using displayField)
16458             * @param {Roo.bootstrap.ComboBox} combo This combo box
16459             * @param {Object} cfg set html .
16460             */
16461         'touchviewdisplay' : true
16462         
16463     });
16464     
16465     this.item = [];
16466     this.tickItems = [];
16467     
16468     this.selectedIndex = -1;
16469     if(this.mode == 'local'){
16470         if(config.queryDelay === undefined){
16471             this.queryDelay = 10;
16472         }
16473         if(config.minChars === undefined){
16474             this.minChars = 0;
16475         }
16476     }
16477 };
16478
16479 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16480      
16481     /**
16482      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16483      * rendering into an Roo.Editor, defaults to false)
16484      */
16485     /**
16486      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16487      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16488      */
16489     /**
16490      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16491      */
16492     /**
16493      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16494      * the dropdown list (defaults to undefined, with no header element)
16495      */
16496
16497      /**
16498      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16499      */
16500      
16501      /**
16502      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16503      */
16504     listWidth: undefined,
16505     /**
16506      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16507      * mode = 'remote' or 'text' if mode = 'local')
16508      */
16509     displayField: undefined,
16510     
16511     /**
16512      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16513      * mode = 'remote' or 'value' if mode = 'local'). 
16514      * Note: use of a valueField requires the user make a selection
16515      * in order for a value to be mapped.
16516      */
16517     valueField: undefined,
16518     /**
16519      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16520      */
16521     modalTitle : '',
16522     
16523     /**
16524      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16525      * field's data value (defaults to the underlying DOM element's name)
16526      */
16527     hiddenName: undefined,
16528     /**
16529      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16530      */
16531     listClass: '',
16532     /**
16533      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16534      */
16535     selectedClass: 'active',
16536     
16537     /**
16538      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16539      */
16540     shadow:'sides',
16541     /**
16542      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16543      * anchor positions (defaults to 'tl-bl')
16544      */
16545     listAlign: 'tl-bl?',
16546     /**
16547      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16548      */
16549     maxHeight: 300,
16550     /**
16551      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16552      * query specified by the allQuery config option (defaults to 'query')
16553      */
16554     triggerAction: 'query',
16555     /**
16556      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16557      * (defaults to 4, does not apply if editable = false)
16558      */
16559     minChars : 4,
16560     /**
16561      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16562      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16563      */
16564     typeAhead: false,
16565     /**
16566      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16567      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16568      */
16569     queryDelay: 500,
16570     /**
16571      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16572      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16573      */
16574     pageSize: 0,
16575     /**
16576      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16577      * when editable = true (defaults to false)
16578      */
16579     selectOnFocus:false,
16580     /**
16581      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16582      */
16583     queryParam: 'query',
16584     /**
16585      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16586      * when mode = 'remote' (defaults to 'Loading...')
16587      */
16588     loadingText: 'Loading...',
16589     /**
16590      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16591      */
16592     resizable: false,
16593     /**
16594      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16595      */
16596     handleHeight : 8,
16597     /**
16598      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16599      * traditional select (defaults to true)
16600      */
16601     editable: true,
16602     /**
16603      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16604      */
16605     allQuery: '',
16606     /**
16607      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16608      */
16609     mode: 'remote',
16610     /**
16611      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16612      * listWidth has a higher value)
16613      */
16614     minListWidth : 70,
16615     /**
16616      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16617      * allow the user to set arbitrary text into the field (defaults to false)
16618      */
16619     forceSelection:false,
16620     /**
16621      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16622      * if typeAhead = true (defaults to 250)
16623      */
16624     typeAheadDelay : 250,
16625     /**
16626      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16627      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16628      */
16629     valueNotFoundText : undefined,
16630     /**
16631      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16632      */
16633     blockFocus : false,
16634     
16635     /**
16636      * @cfg {Boolean} disableClear Disable showing of clear button.
16637      */
16638     disableClear : false,
16639     /**
16640      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16641      */
16642     alwaysQuery : false,
16643     
16644     /**
16645      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16646      */
16647     multiple : false,
16648     
16649     /**
16650      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16651      */
16652     invalidClass : "has-warning",
16653     
16654     /**
16655      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16656      */
16657     validClass : "has-success",
16658     
16659     /**
16660      * @cfg {Boolean} specialFilter (true|false) special filter default false
16661      */
16662     specialFilter : false,
16663     
16664     /**
16665      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16666      */
16667     mobileTouchView : true,
16668     
16669     /**
16670      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16671      */
16672     useNativeIOS : false,
16673     
16674     /**
16675      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16676      */
16677     mobile_restrict_height : false,
16678     
16679     ios_options : false,
16680     
16681     //private
16682     addicon : false,
16683     editicon: false,
16684     
16685     page: 0,
16686     hasQuery: false,
16687     append: false,
16688     loadNext: false,
16689     autoFocus : true,
16690     tickable : false,
16691     btnPosition : 'right',
16692     triggerList : true,
16693     showToggleBtn : true,
16694     animate : true,
16695     emptyResultText: 'Empty',
16696     triggerText : 'Select',
16697     emptyTitle : '',
16698     width : false,
16699     
16700     // element that contains real text value.. (when hidden is used..)
16701     
16702     getAutoCreate : function()
16703     {   
16704         var cfg = false;
16705         //render
16706         /*
16707          * Render classic select for iso
16708          */
16709         
16710         if(Roo.isIOS && this.useNativeIOS){
16711             cfg = this.getAutoCreateNativeIOS();
16712             return cfg;
16713         }
16714         
16715         /*
16716          * Touch Devices
16717          */
16718         
16719         if(Roo.isTouch && this.mobileTouchView){
16720             cfg = this.getAutoCreateTouchView();
16721             return cfg;;
16722         }
16723         
16724         /*
16725          *  Normal ComboBox
16726          */
16727         if(!this.tickable){
16728             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16729             return cfg;
16730         }
16731         
16732         /*
16733          *  ComboBox with tickable selections
16734          */
16735              
16736         var align = this.labelAlign || this.parentLabelAlign();
16737         
16738         cfg = {
16739             cls : 'form-group roo-combobox-tickable' //input-group
16740         };
16741         
16742         var btn_text_select = '';
16743         var btn_text_done = '';
16744         var btn_text_cancel = '';
16745         
16746         if (this.btn_text_show) {
16747             btn_text_select = 'Select';
16748             btn_text_done = 'Done';
16749             btn_text_cancel = 'Cancel'; 
16750         }
16751         
16752         var buttons = {
16753             tag : 'div',
16754             cls : 'tickable-buttons',
16755             cn : [
16756                 {
16757                     tag : 'button',
16758                     type : 'button',
16759                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16760                     //html : this.triggerText
16761                     html: btn_text_select
16762                 },
16763                 {
16764                     tag : 'button',
16765                     type : 'button',
16766                     name : 'ok',
16767                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16768                     //html : 'Done'
16769                     html: btn_text_done
16770                 },
16771                 {
16772                     tag : 'button',
16773                     type : 'button',
16774                     name : 'cancel',
16775                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16776                     //html : 'Cancel'
16777                     html: btn_text_cancel
16778                 }
16779             ]
16780         };
16781         
16782         if(this.editable){
16783             buttons.cn.unshift({
16784                 tag: 'input',
16785                 cls: 'roo-select2-search-field-input'
16786             });
16787         }
16788         
16789         var _this = this;
16790         
16791         Roo.each(buttons.cn, function(c){
16792             if (_this.size) {
16793                 c.cls += ' btn-' + _this.size;
16794             }
16795
16796             if (_this.disabled) {
16797                 c.disabled = true;
16798             }
16799         });
16800         
16801         var box = {
16802             tag: 'div',
16803             style : 'display: contents',
16804             cn: [
16805                 {
16806                     tag: 'input',
16807                     type : 'hidden',
16808                     cls: 'form-hidden-field'
16809                 },
16810                 {
16811                     tag: 'ul',
16812                     cls: 'roo-select2-choices',
16813                     cn:[
16814                         {
16815                             tag: 'li',
16816                             cls: 'roo-select2-search-field',
16817                             cn: [
16818                                 buttons
16819                             ]
16820                         }
16821                     ]
16822                 }
16823             ]
16824         };
16825         
16826         var combobox = {
16827             cls: 'roo-select2-container input-group roo-select2-container-multi',
16828             cn: [
16829                 
16830                 box
16831 //                {
16832 //                    tag: 'ul',
16833 //                    cls: 'typeahead typeahead-long dropdown-menu',
16834 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16835 //                }
16836             ]
16837         };
16838         
16839         if(this.hasFeedback && !this.allowBlank){
16840             
16841             var feedback = {
16842                 tag: 'span',
16843                 cls: 'glyphicon form-control-feedback'
16844             };
16845
16846             combobox.cn.push(feedback);
16847         }
16848         
16849         
16850         
16851         var indicator = {
16852             tag : 'i',
16853             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16854             tooltip : 'This field is required'
16855         };
16856         if (Roo.bootstrap.version == 4) {
16857             indicator = {
16858                 tag : 'i',
16859                 style : 'display:none'
16860             };
16861         }
16862         if (align ==='left' && this.fieldLabel.length) {
16863             
16864             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16865             
16866             cfg.cn = [
16867                 indicator,
16868                 {
16869                     tag: 'label',
16870                     'for' :  id,
16871                     cls : 'control-label col-form-label',
16872                     html : this.fieldLabel
16873
16874                 },
16875                 {
16876                     cls : "", 
16877                     cn: [
16878                         combobox
16879                     ]
16880                 }
16881
16882             ];
16883             
16884             var labelCfg = cfg.cn[1];
16885             var contentCfg = cfg.cn[2];
16886             
16887
16888             if(this.indicatorpos == 'right'){
16889                 
16890                 cfg.cn = [
16891                     {
16892                         tag: 'label',
16893                         'for' :  id,
16894                         cls : 'control-label col-form-label',
16895                         cn : [
16896                             {
16897                                 tag : 'span',
16898                                 html : this.fieldLabel
16899                             },
16900                             indicator
16901                         ]
16902                     },
16903                     {
16904                         cls : "",
16905                         cn: [
16906                             combobox
16907                         ]
16908                     }
16909
16910                 ];
16911                 
16912                 
16913                 
16914                 labelCfg = cfg.cn[0];
16915                 contentCfg = cfg.cn[1];
16916             
16917             }
16918             
16919             if(this.labelWidth > 12){
16920                 labelCfg.style = "width: " + this.labelWidth + 'px';
16921             }
16922             if(this.width * 1 > 0){
16923                 contentCfg.style = "width: " + this.width + 'px';
16924             }
16925             if(this.labelWidth < 13 && this.labelmd == 0){
16926                 this.labelmd = this.labelWidth;
16927             }
16928             
16929             if(this.labellg > 0){
16930                 labelCfg.cls += ' col-lg-' + this.labellg;
16931                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16932             }
16933             
16934             if(this.labelmd > 0){
16935                 labelCfg.cls += ' col-md-' + this.labelmd;
16936                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16937             }
16938             
16939             if(this.labelsm > 0){
16940                 labelCfg.cls += ' col-sm-' + this.labelsm;
16941                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16942             }
16943             
16944             if(this.labelxs > 0){
16945                 labelCfg.cls += ' col-xs-' + this.labelxs;
16946                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16947             }
16948                 
16949                 
16950         } else if ( this.fieldLabel.length) {
16951 //                Roo.log(" label");
16952                  cfg.cn = [
16953                    indicator,
16954                     {
16955                         tag: 'label',
16956                         //cls : 'input-group-addon',
16957                         html : this.fieldLabel
16958                     },
16959                     combobox
16960                 ];
16961                 
16962                 if(this.indicatorpos == 'right'){
16963                     cfg.cn = [
16964                         {
16965                             tag: 'label',
16966                             //cls : 'input-group-addon',
16967                             html : this.fieldLabel
16968                         },
16969                         indicator,
16970                         combobox
16971                     ];
16972                     
16973                 }
16974
16975         } else {
16976             
16977 //                Roo.log(" no label && no align");
16978                 cfg = combobox
16979                      
16980                 
16981         }
16982          
16983         var settings=this;
16984         ['xs','sm','md','lg'].map(function(size){
16985             if (settings[size]) {
16986                 cfg.cls += ' col-' + size + '-' + settings[size];
16987             }
16988         });
16989         
16990         return cfg;
16991         
16992     },
16993     
16994     _initEventsCalled : false,
16995     
16996     // private
16997     initEvents: function()
16998     {   
16999         if (this._initEventsCalled) { // as we call render... prevent looping...
17000             return;
17001         }
17002         this._initEventsCalled = true;
17003         
17004         if (!this.store) {
17005             throw "can not find store for combo";
17006         }
17007         
17008         this.indicator = this.indicatorEl();
17009         
17010         this.store = Roo.factory(this.store, Roo.data);
17011         this.store.parent = this;
17012         
17013         // if we are building from html. then this element is so complex, that we can not really
17014         // use the rendered HTML.
17015         // so we have to trash and replace the previous code.
17016         if (Roo.XComponent.build_from_html) {
17017             // remove this element....
17018             var e = this.el.dom, k=0;
17019             while (e ) { e = e.previousSibling;  ++k;}
17020
17021             this.el.remove();
17022             
17023             this.el=false;
17024             this.rendered = false;
17025             
17026             this.render(this.parent().getChildContainer(true), k);
17027         }
17028         
17029         if(Roo.isIOS && this.useNativeIOS){
17030             this.initIOSView();
17031             return;
17032         }
17033         
17034         /*
17035          * Touch Devices
17036          */
17037         
17038         if(Roo.isTouch && this.mobileTouchView){
17039             this.initTouchView();
17040             return;
17041         }
17042         
17043         if(this.tickable){
17044             this.initTickableEvents();
17045             return;
17046         }
17047         
17048         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17049         
17050         if(this.hiddenName){
17051             
17052             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17053             
17054             this.hiddenField.dom.value =
17055                 this.hiddenValue !== undefined ? this.hiddenValue :
17056                 this.value !== undefined ? this.value : '';
17057
17058             // prevent input submission
17059             this.el.dom.removeAttribute('name');
17060             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17061              
17062              
17063         }
17064         //if(Roo.isGecko){
17065         //    this.el.dom.setAttribute('autocomplete', 'off');
17066         //}
17067         
17068         var cls = 'x-combo-list';
17069         
17070         //this.list = new Roo.Layer({
17071         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17072         //});
17073         
17074         var _this = this;
17075         
17076         (function(){
17077             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17078             _this.list.setWidth(lw);
17079         }).defer(100);
17080         
17081         this.list.on('mouseover', this.onViewOver, this);
17082         this.list.on('mousemove', this.onViewMove, this);
17083         this.list.on('scroll', this.onViewScroll, this);
17084         
17085         /*
17086         this.list.swallowEvent('mousewheel');
17087         this.assetHeight = 0;
17088
17089         if(this.title){
17090             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17091             this.assetHeight += this.header.getHeight();
17092         }
17093
17094         this.innerList = this.list.createChild({cls:cls+'-inner'});
17095         this.innerList.on('mouseover', this.onViewOver, this);
17096         this.innerList.on('mousemove', this.onViewMove, this);
17097         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17098         
17099         if(this.allowBlank && !this.pageSize && !this.disableClear){
17100             this.footer = this.list.createChild({cls:cls+'-ft'});
17101             this.pageTb = new Roo.Toolbar(this.footer);
17102            
17103         }
17104         if(this.pageSize){
17105             this.footer = this.list.createChild({cls:cls+'-ft'});
17106             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17107                     {pageSize: this.pageSize});
17108             
17109         }
17110         
17111         if (this.pageTb && this.allowBlank && !this.disableClear) {
17112             var _this = this;
17113             this.pageTb.add(new Roo.Toolbar.Fill(), {
17114                 cls: 'x-btn-icon x-btn-clear',
17115                 text: '&#160;',
17116                 handler: function()
17117                 {
17118                     _this.collapse();
17119                     _this.clearValue();
17120                     _this.onSelect(false, -1);
17121                 }
17122             });
17123         }
17124         if (this.footer) {
17125             this.assetHeight += this.footer.getHeight();
17126         }
17127         */
17128             
17129         if(!this.tpl){
17130             this.tpl = Roo.bootstrap.version == 4 ?
17131                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17132                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17133         }
17134
17135         this.view = new Roo.View(this.list, this.tpl, {
17136             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17137         });
17138         //this.view.wrapEl.setDisplayed(false);
17139         this.view.on('click', this.onViewClick, this);
17140         
17141         
17142         this.store.on('beforeload', this.onBeforeLoad, this);
17143         this.store.on('load', this.onLoad, this);
17144         this.store.on('loadexception', this.onLoadException, this);
17145         /*
17146         if(this.resizable){
17147             this.resizer = new Roo.Resizable(this.list,  {
17148                pinned:true, handles:'se'
17149             });
17150             this.resizer.on('resize', function(r, w, h){
17151                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17152                 this.listWidth = w;
17153                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17154                 this.restrictHeight();
17155             }, this);
17156             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17157         }
17158         */
17159         if(!this.editable){
17160             this.editable = true;
17161             this.setEditable(false);
17162         }
17163         
17164         /*
17165         
17166         if (typeof(this.events.add.listeners) != 'undefined') {
17167             
17168             this.addicon = this.wrap.createChild(
17169                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17170        
17171             this.addicon.on('click', function(e) {
17172                 this.fireEvent('add', this);
17173             }, this);
17174         }
17175         if (typeof(this.events.edit.listeners) != 'undefined') {
17176             
17177             this.editicon = this.wrap.createChild(
17178                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17179             if (this.addicon) {
17180                 this.editicon.setStyle('margin-left', '40px');
17181             }
17182             this.editicon.on('click', function(e) {
17183                 
17184                 // we fire even  if inothing is selected..
17185                 this.fireEvent('edit', this, this.lastData );
17186                 
17187             }, this);
17188         }
17189         */
17190         
17191         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17192             "up" : function(e){
17193                 this.inKeyMode = true;
17194                 this.selectPrev();
17195             },
17196
17197             "down" : function(e){
17198                 if(!this.isExpanded()){
17199                     this.onTriggerClick();
17200                 }else{
17201                     this.inKeyMode = true;
17202                     this.selectNext();
17203                 }
17204             },
17205
17206             "enter" : function(e){
17207 //                this.onViewClick();
17208                 //return true;
17209                 this.collapse();
17210                 
17211                 if(this.fireEvent("specialkey", this, e)){
17212                     this.onViewClick(false);
17213                 }
17214                 
17215                 return true;
17216             },
17217
17218             "esc" : function(e){
17219                 this.collapse();
17220             },
17221
17222             "tab" : function(e){
17223                 this.collapse();
17224                 
17225                 if(this.fireEvent("specialkey", this, e)){
17226                     this.onViewClick(false);
17227                 }
17228                 
17229                 return true;
17230             },
17231
17232             scope : this,
17233
17234             doRelay : function(foo, bar, hname){
17235                 if(hname == 'down' || this.scope.isExpanded()){
17236                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17237                 }
17238                 return true;
17239             },
17240
17241             forceKeyDown: true
17242         });
17243         
17244         
17245         this.queryDelay = Math.max(this.queryDelay || 10,
17246                 this.mode == 'local' ? 10 : 250);
17247         
17248         
17249         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17250         
17251         if(this.typeAhead){
17252             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17253         }
17254         if(this.editable !== false){
17255             this.inputEl().on("keyup", this.onKeyUp, this);
17256         }
17257         if(this.forceSelection){
17258             this.inputEl().on('blur', this.doForce, this);
17259         }
17260         
17261         if(this.multiple){
17262             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17263             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17264         }
17265     },
17266     
17267     initTickableEvents: function()
17268     {   
17269         this.createList();
17270         
17271         if(this.hiddenName){
17272             
17273             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17274             
17275             this.hiddenField.dom.value =
17276                 this.hiddenValue !== undefined ? this.hiddenValue :
17277                 this.value !== undefined ? this.value : '';
17278
17279             // prevent input submission
17280             this.el.dom.removeAttribute('name');
17281             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17282              
17283              
17284         }
17285         
17286 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17287         
17288         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17289         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17290         if(this.triggerList){
17291             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17292         }
17293          
17294         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17295         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17296         
17297         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17298         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17299         
17300         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17301         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17302         
17303         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17304         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17305         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17306         
17307         this.okBtn.hide();
17308         this.cancelBtn.hide();
17309         
17310         var _this = this;
17311         
17312         (function(){
17313             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17314             _this.list.setWidth(lw);
17315         }).defer(100);
17316         
17317         this.list.on('mouseover', this.onViewOver, this);
17318         this.list.on('mousemove', this.onViewMove, this);
17319         
17320         this.list.on('scroll', this.onViewScroll, this);
17321         
17322         if(!this.tpl){
17323             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17324                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17325         }
17326
17327         this.view = new Roo.View(this.list, this.tpl, {
17328             singleSelect:true,
17329             tickable:true,
17330             parent:this,
17331             store: this.store,
17332             selectedClass: this.selectedClass
17333         });
17334         
17335         //this.view.wrapEl.setDisplayed(false);
17336         this.view.on('click', this.onViewClick, this);
17337         
17338         
17339         
17340         this.store.on('beforeload', this.onBeforeLoad, this);
17341         this.store.on('load', this.onLoad, this);
17342         this.store.on('loadexception', this.onLoadException, this);
17343         
17344         if(this.editable){
17345             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17346                 "up" : function(e){
17347                     this.inKeyMode = true;
17348                     this.selectPrev();
17349                 },
17350
17351                 "down" : function(e){
17352                     this.inKeyMode = true;
17353                     this.selectNext();
17354                 },
17355
17356                 "enter" : function(e){
17357                     if(this.fireEvent("specialkey", this, e)){
17358                         this.onViewClick(false);
17359                     }
17360                     
17361                     return true;
17362                 },
17363
17364                 "esc" : function(e){
17365                     this.onTickableFooterButtonClick(e, false, false);
17366                 },
17367
17368                 "tab" : function(e){
17369                     this.fireEvent("specialkey", this, e);
17370                     
17371                     this.onTickableFooterButtonClick(e, false, false);
17372                     
17373                     return true;
17374                 },
17375
17376                 scope : this,
17377
17378                 doRelay : function(e, fn, key){
17379                     if(this.scope.isExpanded()){
17380                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17381                     }
17382                     return true;
17383                 },
17384
17385                 forceKeyDown: true
17386             });
17387         }
17388         
17389         this.queryDelay = Math.max(this.queryDelay || 10,
17390                 this.mode == 'local' ? 10 : 250);
17391         
17392         
17393         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17394         
17395         if(this.typeAhead){
17396             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17397         }
17398         
17399         if(this.editable !== false){
17400             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17401         }
17402         
17403         this.indicator = this.indicatorEl();
17404         
17405         if(this.indicator){
17406             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17407             this.indicator.hide();
17408         }
17409         
17410     },
17411
17412     onDestroy : function(){
17413         if(this.view){
17414             this.view.setStore(null);
17415             this.view.el.removeAllListeners();
17416             this.view.el.remove();
17417             this.view.purgeListeners();
17418         }
17419         if(this.list){
17420             this.list.dom.innerHTML  = '';
17421         }
17422         
17423         if(this.store){
17424             this.store.un('beforeload', this.onBeforeLoad, this);
17425             this.store.un('load', this.onLoad, this);
17426             this.store.un('loadexception', this.onLoadException, this);
17427         }
17428         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17429     },
17430
17431     // private
17432     fireKey : function(e){
17433         if(e.isNavKeyPress() && !this.list.isVisible()){
17434             this.fireEvent("specialkey", this, e);
17435         }
17436     },
17437
17438     // private
17439     onResize: function(w, h)
17440     {
17441         
17442         
17443 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17444 //        
17445 //        if(typeof w != 'number'){
17446 //            // we do not handle it!?!?
17447 //            return;
17448 //        }
17449 //        var tw = this.trigger.getWidth();
17450 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17451 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17452 //        var x = w - tw;
17453 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17454 //            
17455 //        //this.trigger.setStyle('left', x+'px');
17456 //        
17457 //        if(this.list && this.listWidth === undefined){
17458 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17459 //            this.list.setWidth(lw);
17460 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17461 //        }
17462         
17463     
17464         
17465     },
17466
17467     /**
17468      * Allow or prevent the user from directly editing the field text.  If false is passed,
17469      * the user will only be able to select from the items defined in the dropdown list.  This method
17470      * is the runtime equivalent of setting the 'editable' config option at config time.
17471      * @param {Boolean} value True to allow the user to directly edit the field text
17472      */
17473     setEditable : function(value){
17474         if(value == this.editable){
17475             return;
17476         }
17477         this.editable = value;
17478         if(!value){
17479             this.inputEl().dom.setAttribute('readOnly', true);
17480             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17481             this.inputEl().addClass('x-combo-noedit');
17482         }else{
17483             this.inputEl().dom.removeAttribute('readOnly');
17484             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17485             this.inputEl().removeClass('x-combo-noedit');
17486         }
17487     },
17488
17489     // private
17490     
17491     onBeforeLoad : function(combo,opts){
17492         if(!this.hasFocus){
17493             return;
17494         }
17495          if (!opts.add) {
17496             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17497          }
17498         this.restrictHeight();
17499         this.selectedIndex = -1;
17500     },
17501
17502     // private
17503     onLoad : function(){
17504         
17505         this.hasQuery = false;
17506         
17507         if(!this.hasFocus){
17508             return;
17509         }
17510         
17511         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17512             this.loading.hide();
17513         }
17514         
17515         if(this.store.getCount() > 0){
17516             
17517             this.expand();
17518             this.restrictHeight();
17519             if(this.lastQuery == this.allQuery){
17520                 if(this.editable && !this.tickable){
17521                     this.inputEl().dom.select();
17522                 }
17523                 
17524                 if(
17525                     !this.selectByValue(this.value, true) &&
17526                     this.autoFocus && 
17527                     (
17528                         !this.store.lastOptions ||
17529                         typeof(this.store.lastOptions.add) == 'undefined' || 
17530                         this.store.lastOptions.add != true
17531                     )
17532                 ){
17533                     this.select(0, true);
17534                 }
17535             }else{
17536                 if(this.autoFocus){
17537                     this.selectNext();
17538                 }
17539                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17540                     this.taTask.delay(this.typeAheadDelay);
17541                 }
17542             }
17543         }else{
17544             this.onEmptyResults();
17545         }
17546         
17547         //this.el.focus();
17548     },
17549     // private
17550     onLoadException : function()
17551     {
17552         this.hasQuery = false;
17553         
17554         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17555             this.loading.hide();
17556         }
17557         
17558         if(this.tickable && this.editable){
17559             return;
17560         }
17561         
17562         this.collapse();
17563         // only causes errors at present
17564         //Roo.log(this.store.reader.jsonData);
17565         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17566             // fixme
17567             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17568         //}
17569         
17570         
17571     },
17572     // private
17573     onTypeAhead : function(){
17574         if(this.store.getCount() > 0){
17575             var r = this.store.getAt(0);
17576             var newValue = r.data[this.displayField];
17577             var len = newValue.length;
17578             var selStart = this.getRawValue().length;
17579             
17580             if(selStart != len){
17581                 this.setRawValue(newValue);
17582                 this.selectText(selStart, newValue.length);
17583             }
17584         }
17585     },
17586
17587     // private
17588     onSelect : function(record, index){
17589         
17590         if(this.fireEvent('beforeselect', this, record, index) !== false){
17591         
17592             this.setFromData(index > -1 ? record.data : false);
17593             
17594             this.collapse();
17595             this.fireEvent('select', this, record, index);
17596         }
17597     },
17598
17599     /**
17600      * Returns the currently selected field value or empty string if no value is set.
17601      * @return {String} value The selected value
17602      */
17603     getValue : function()
17604     {
17605         if(Roo.isIOS && this.useNativeIOS){
17606             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17607         }
17608         
17609         if(this.multiple){
17610             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17611         }
17612         
17613         if(this.valueField){
17614             return typeof this.value != 'undefined' ? this.value : '';
17615         }else{
17616             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17617         }
17618     },
17619     
17620     getRawValue : function()
17621     {
17622         if(Roo.isIOS && this.useNativeIOS){
17623             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17624         }
17625         
17626         var v = this.inputEl().getValue();
17627         
17628         return v;
17629     },
17630
17631     /**
17632      * Clears any text/value currently set in the field
17633      */
17634     clearValue : function(){
17635         
17636         if(this.hiddenField){
17637             this.hiddenField.dom.value = '';
17638         }
17639         this.value = '';
17640         this.setRawValue('');
17641         this.lastSelectionText = '';
17642         this.lastData = false;
17643         
17644         var close = this.closeTriggerEl();
17645         
17646         if(close){
17647             close.hide();
17648         }
17649         
17650         this.validate();
17651         
17652     },
17653
17654     /**
17655      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17656      * will be displayed in the field.  If the value does not match the data value of an existing item,
17657      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17658      * Otherwise the field will be blank (although the value will still be set).
17659      * @param {String} value The value to match
17660      */
17661     setValue : function(v)
17662     {
17663         if(Roo.isIOS && this.useNativeIOS){
17664             this.setIOSValue(v);
17665             return;
17666         }
17667         
17668         if(this.multiple){
17669             this.syncValue();
17670             return;
17671         }
17672         
17673         var text = v;
17674         if(this.valueField){
17675             var r = this.findRecord(this.valueField, v);
17676             if(r){
17677                 text = r.data[this.displayField];
17678             }else if(this.valueNotFoundText !== undefined){
17679                 text = this.valueNotFoundText;
17680             }
17681         }
17682         this.lastSelectionText = text;
17683         if(this.hiddenField){
17684             this.hiddenField.dom.value = v;
17685         }
17686         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17687         this.value = v;
17688         
17689         var close = this.closeTriggerEl();
17690         
17691         if(close){
17692             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17693         }
17694         
17695         this.validate();
17696     },
17697     /**
17698      * @property {Object} the last set data for the element
17699      */
17700     
17701     lastData : false,
17702     /**
17703      * Sets the value of the field based on a object which is related to the record format for the store.
17704      * @param {Object} value the value to set as. or false on reset?
17705      */
17706     setFromData : function(o){
17707         
17708         if(this.multiple){
17709             this.addItem(o);
17710             return;
17711         }
17712             
17713         var dv = ''; // display value
17714         var vv = ''; // value value..
17715         this.lastData = o;
17716         if (this.displayField) {
17717             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17718         } else {
17719             // this is an error condition!!!
17720             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17721         }
17722         
17723         if(this.valueField){
17724             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17725         }
17726         
17727         var close = this.closeTriggerEl();
17728         
17729         if(close){
17730             if(dv.length || vv * 1 > 0){
17731                 close.show() ;
17732                 this.blockFocus=true;
17733             } else {
17734                 close.hide();
17735             }             
17736         }
17737         
17738         if(this.hiddenField){
17739             this.hiddenField.dom.value = vv;
17740             
17741             this.lastSelectionText = dv;
17742             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17743             this.value = vv;
17744             return;
17745         }
17746         // no hidden field.. - we store the value in 'value', but still display
17747         // display field!!!!
17748         this.lastSelectionText = dv;
17749         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17750         this.value = vv;
17751         
17752         
17753         
17754     },
17755     // private
17756     reset : function(){
17757         // overridden so that last data is reset..
17758         
17759         if(this.multiple){
17760             this.clearItem();
17761             return;
17762         }
17763         
17764         this.setValue(this.originalValue);
17765         //this.clearInvalid();
17766         this.lastData = false;
17767         if (this.view) {
17768             this.view.clearSelections();
17769         }
17770         
17771         this.validate();
17772     },
17773     // private
17774     findRecord : function(prop, value){
17775         var record;
17776         if(this.store.getCount() > 0){
17777             this.store.each(function(r){
17778                 if(r.data[prop] == value){
17779                     record = r;
17780                     return false;
17781                 }
17782                 return true;
17783             });
17784         }
17785         return record;
17786     },
17787     
17788     getName: function()
17789     {
17790         // returns hidden if it's set..
17791         if (!this.rendered) {return ''};
17792         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17793         
17794     },
17795     // private
17796     onViewMove : function(e, t){
17797         this.inKeyMode = false;
17798     },
17799
17800     // private
17801     onViewOver : function(e, t){
17802         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17803             return;
17804         }
17805         var item = this.view.findItemFromChild(t);
17806         
17807         if(item){
17808             var index = this.view.indexOf(item);
17809             this.select(index, false);
17810         }
17811     },
17812
17813     // private
17814     onViewClick : function(view, doFocus, el, e)
17815     {
17816         var index = this.view.getSelectedIndexes()[0];
17817         
17818         var r = this.store.getAt(index);
17819         
17820         if(this.tickable){
17821             
17822             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17823                 return;
17824             }
17825             
17826             var rm = false;
17827             var _this = this;
17828             
17829             Roo.each(this.tickItems, function(v,k){
17830                 
17831                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17832                     Roo.log(v);
17833                     _this.tickItems.splice(k, 1);
17834                     
17835                     if(typeof(e) == 'undefined' && view == false){
17836                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17837                     }
17838                     
17839                     rm = true;
17840                     return;
17841                 }
17842             });
17843             
17844             if(rm){
17845                 return;
17846             }
17847             
17848             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17849                 this.tickItems.push(r.data);
17850             }
17851             
17852             if(typeof(e) == 'undefined' && view == false){
17853                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17854             }
17855                     
17856             return;
17857         }
17858         
17859         if(r){
17860             this.onSelect(r, index);
17861         }
17862         if(doFocus !== false && !this.blockFocus){
17863             this.inputEl().focus();
17864         }
17865     },
17866
17867     // private
17868     restrictHeight : function(){
17869         //this.innerList.dom.style.height = '';
17870         //var inner = this.innerList.dom;
17871         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17872         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17873         //this.list.beginUpdate();
17874         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17875         this.list.alignTo(this.inputEl(), this.listAlign);
17876         this.list.alignTo(this.inputEl(), this.listAlign);
17877         //this.list.endUpdate();
17878     },
17879
17880     // private
17881     onEmptyResults : function(){
17882         
17883         if(this.tickable && this.editable){
17884             this.hasFocus = false;
17885             this.restrictHeight();
17886             return;
17887         }
17888         
17889         this.collapse();
17890     },
17891
17892     /**
17893      * Returns true if the dropdown list is expanded, else false.
17894      */
17895     isExpanded : function(){
17896         return this.list.isVisible();
17897     },
17898
17899     /**
17900      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17901      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17902      * @param {String} value The data value of the item to select
17903      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17904      * selected item if it is not currently in view (defaults to true)
17905      * @return {Boolean} True if the value matched an item in the list, else false
17906      */
17907     selectByValue : function(v, scrollIntoView){
17908         if(v !== undefined && v !== null){
17909             var r = this.findRecord(this.valueField || this.displayField, v);
17910             if(r){
17911                 this.select(this.store.indexOf(r), scrollIntoView);
17912                 return true;
17913             }
17914         }
17915         return false;
17916     },
17917
17918     /**
17919      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17920      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17921      * @param {Number} index The zero-based index of the list item to select
17922      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17923      * selected item if it is not currently in view (defaults to true)
17924      */
17925     select : function(index, scrollIntoView){
17926         this.selectedIndex = index;
17927         this.view.select(index);
17928         if(scrollIntoView !== false){
17929             var el = this.view.getNode(index);
17930             /*
17931              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17932              */
17933             if(el){
17934                 this.list.scrollChildIntoView(el, false);
17935             }
17936         }
17937     },
17938
17939     // private
17940     selectNext : function(){
17941         var ct = this.store.getCount();
17942         if(ct > 0){
17943             if(this.selectedIndex == -1){
17944                 this.select(0);
17945             }else if(this.selectedIndex < ct-1){
17946                 this.select(this.selectedIndex+1);
17947             }
17948         }
17949     },
17950
17951     // private
17952     selectPrev : function(){
17953         var ct = this.store.getCount();
17954         if(ct > 0){
17955             if(this.selectedIndex == -1){
17956                 this.select(0);
17957             }else if(this.selectedIndex != 0){
17958                 this.select(this.selectedIndex-1);
17959             }
17960         }
17961     },
17962
17963     // private
17964     onKeyUp : function(e){
17965         if(this.editable !== false && !e.isSpecialKey()){
17966             this.lastKey = e.getKey();
17967             this.dqTask.delay(this.queryDelay);
17968         }
17969     },
17970
17971     // private
17972     validateBlur : function(){
17973         return !this.list || !this.list.isVisible();   
17974     },
17975
17976     // private
17977     initQuery : function(){
17978         
17979         var v = this.getRawValue();
17980         
17981         if(this.tickable && this.editable){
17982             v = this.tickableInputEl().getValue();
17983         }
17984         
17985         this.doQuery(v);
17986     },
17987
17988     // private
17989     doForce : function(){
17990         if(this.inputEl().dom.value.length > 0){
17991             this.inputEl().dom.value =
17992                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17993              
17994         }
17995     },
17996
17997     /**
17998      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17999      * query allowing the query action to be canceled if needed.
18000      * @param {String} query The SQL query to execute
18001      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18002      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18003      * saved in the current store (defaults to false)
18004      */
18005     doQuery : function(q, forceAll){
18006         
18007         if(q === undefined || q === null){
18008             q = '';
18009         }
18010         var qe = {
18011             query: q,
18012             forceAll: forceAll,
18013             combo: this,
18014             cancel:false
18015         };
18016         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18017             return false;
18018         }
18019         q = qe.query;
18020         
18021         forceAll = qe.forceAll;
18022         if(forceAll === true || (q.length >= this.minChars)){
18023             
18024             this.hasQuery = true;
18025             
18026             if(this.lastQuery != q || this.alwaysQuery){
18027                 this.lastQuery = q;
18028                 if(this.mode == 'local'){
18029                     this.selectedIndex = -1;
18030                     if(forceAll){
18031                         this.store.clearFilter();
18032                     }else{
18033                         
18034                         if(this.specialFilter){
18035                             this.fireEvent('specialfilter', this);
18036                             this.onLoad();
18037                             return;
18038                         }
18039                         
18040                         this.store.filter(this.displayField, q);
18041                     }
18042                     
18043                     this.store.fireEvent("datachanged", this.store);
18044                     
18045                     this.onLoad();
18046                     
18047                     
18048                 }else{
18049                     
18050                     this.store.baseParams[this.queryParam] = q;
18051                     
18052                     var options = {params : this.getParams(q)};
18053                     
18054                     if(this.loadNext){
18055                         options.add = true;
18056                         options.params.start = this.page * this.pageSize;
18057                     }
18058                     
18059                     this.store.load(options);
18060                     
18061                     /*
18062                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18063                      *  we should expand the list on onLoad
18064                      *  so command out it
18065                      */
18066 //                    this.expand();
18067                 }
18068             }else{
18069                 this.selectedIndex = -1;
18070                 this.onLoad();   
18071             }
18072         }
18073         
18074         this.loadNext = false;
18075     },
18076     
18077     // private
18078     getParams : function(q){
18079         var p = {};
18080         //p[this.queryParam] = q;
18081         
18082         if(this.pageSize){
18083             p.start = 0;
18084             p.limit = this.pageSize;
18085         }
18086         return p;
18087     },
18088
18089     /**
18090      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18091      */
18092     collapse : function(){
18093         if(!this.isExpanded()){
18094             return;
18095         }
18096         
18097         this.list.hide();
18098         
18099         this.hasFocus = false;
18100         
18101         if(this.tickable){
18102             this.okBtn.hide();
18103             this.cancelBtn.hide();
18104             this.trigger.show();
18105             
18106             if(this.editable){
18107                 this.tickableInputEl().dom.value = '';
18108                 this.tickableInputEl().blur();
18109             }
18110             
18111         }
18112         
18113         Roo.get(document).un('mousedown', this.collapseIf, this);
18114         Roo.get(document).un('mousewheel', this.collapseIf, this);
18115         if (!this.editable) {
18116             Roo.get(document).un('keydown', this.listKeyPress, this);
18117         }
18118         this.fireEvent('collapse', this);
18119         
18120         this.validate();
18121     },
18122
18123     // private
18124     collapseIf : function(e){
18125         var in_combo  = e.within(this.el);
18126         var in_list =  e.within(this.list);
18127         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18128         
18129         if (in_combo || in_list || is_list) {
18130             //e.stopPropagation();
18131             return;
18132         }
18133         
18134         if(this.tickable){
18135             this.onTickableFooterButtonClick(e, false, false);
18136         }
18137
18138         this.collapse();
18139         
18140     },
18141
18142     /**
18143      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18144      */
18145     expand : function(){
18146        
18147         if(this.isExpanded() || !this.hasFocus){
18148             return;
18149         }
18150         
18151         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18152         this.list.setWidth(lw);
18153         
18154         Roo.log('expand');
18155         
18156         this.list.show();
18157         
18158         this.restrictHeight();
18159         
18160         if(this.tickable){
18161             
18162             this.tickItems = Roo.apply([], this.item);
18163             
18164             this.okBtn.show();
18165             this.cancelBtn.show();
18166             this.trigger.hide();
18167             
18168             if(this.editable){
18169                 this.tickableInputEl().focus();
18170             }
18171             
18172         }
18173         
18174         Roo.get(document).on('mousedown', this.collapseIf, this);
18175         Roo.get(document).on('mousewheel', this.collapseIf, this);
18176         if (!this.editable) {
18177             Roo.get(document).on('keydown', this.listKeyPress, this);
18178         }
18179         
18180         this.fireEvent('expand', this);
18181     },
18182
18183     // private
18184     // Implements the default empty TriggerField.onTriggerClick function
18185     onTriggerClick : function(e)
18186     {
18187         Roo.log('trigger click');
18188         
18189         if(this.disabled || !this.triggerList){
18190             return;
18191         }
18192         
18193         this.page = 0;
18194         this.loadNext = false;
18195         
18196         if(this.isExpanded()){
18197             this.collapse();
18198             if (!this.blockFocus) {
18199                 this.inputEl().focus();
18200             }
18201             
18202         }else {
18203             this.hasFocus = true;
18204             if(this.triggerAction == 'all') {
18205                 this.doQuery(this.allQuery, true);
18206             } else {
18207                 this.doQuery(this.getRawValue());
18208             }
18209             if (!this.blockFocus) {
18210                 this.inputEl().focus();
18211             }
18212         }
18213     },
18214     
18215     onTickableTriggerClick : function(e)
18216     {
18217         if(this.disabled){
18218             return;
18219         }
18220         
18221         this.page = 0;
18222         this.loadNext = false;
18223         this.hasFocus = true;
18224         
18225         if(this.triggerAction == 'all') {
18226             this.doQuery(this.allQuery, true);
18227         } else {
18228             this.doQuery(this.getRawValue());
18229         }
18230     },
18231     
18232     onSearchFieldClick : function(e)
18233     {
18234         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18235             this.onTickableFooterButtonClick(e, false, false);
18236             return;
18237         }
18238         
18239         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18240             return;
18241         }
18242         
18243         this.page = 0;
18244         this.loadNext = false;
18245         this.hasFocus = true;
18246         
18247         if(this.triggerAction == 'all') {
18248             this.doQuery(this.allQuery, true);
18249         } else {
18250             this.doQuery(this.getRawValue());
18251         }
18252     },
18253     
18254     listKeyPress : function(e)
18255     {
18256         //Roo.log('listkeypress');
18257         // scroll to first matching element based on key pres..
18258         if (e.isSpecialKey()) {
18259             return false;
18260         }
18261         var k = String.fromCharCode(e.getKey()).toUpperCase();
18262         //Roo.log(k);
18263         var match  = false;
18264         var csel = this.view.getSelectedNodes();
18265         var cselitem = false;
18266         if (csel.length) {
18267             var ix = this.view.indexOf(csel[0]);
18268             cselitem  = this.store.getAt(ix);
18269             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18270                 cselitem = false;
18271             }
18272             
18273         }
18274         
18275         this.store.each(function(v) { 
18276             if (cselitem) {
18277                 // start at existing selection.
18278                 if (cselitem.id == v.id) {
18279                     cselitem = false;
18280                 }
18281                 return true;
18282             }
18283                 
18284             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18285                 match = this.store.indexOf(v);
18286                 return false;
18287             }
18288             return true;
18289         }, this);
18290         
18291         if (match === false) {
18292             return true; // no more action?
18293         }
18294         // scroll to?
18295         this.view.select(match);
18296         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18297         sn.scrollIntoView(sn.dom.parentNode, false);
18298     },
18299     
18300     onViewScroll : function(e, t){
18301         
18302         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){
18303             return;
18304         }
18305         
18306         this.hasQuery = true;
18307         
18308         this.loading = this.list.select('.loading', true).first();
18309         
18310         if(this.loading === null){
18311             this.list.createChild({
18312                 tag: 'div',
18313                 cls: 'loading roo-select2-more-results roo-select2-active',
18314                 html: 'Loading more results...'
18315             });
18316             
18317             this.loading = this.list.select('.loading', true).first();
18318             
18319             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18320             
18321             this.loading.hide();
18322         }
18323         
18324         this.loading.show();
18325         
18326         var _combo = this;
18327         
18328         this.page++;
18329         this.loadNext = true;
18330         
18331         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18332         
18333         return;
18334     },
18335     
18336     addItem : function(o)
18337     {   
18338         var dv = ''; // display value
18339         
18340         if (this.displayField) {
18341             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18342         } else {
18343             // this is an error condition!!!
18344             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18345         }
18346         
18347         if(!dv.length){
18348             return;
18349         }
18350         
18351         var choice = this.choices.createChild({
18352             tag: 'li',
18353             cls: 'roo-select2-search-choice',
18354             cn: [
18355                 {
18356                     tag: 'div',
18357                     html: dv
18358                 },
18359                 {
18360                     tag: 'a',
18361                     href: '#',
18362                     cls: 'roo-select2-search-choice-close fa fa-times',
18363                     tabindex: '-1'
18364                 }
18365             ]
18366             
18367         }, this.searchField);
18368         
18369         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18370         
18371         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18372         
18373         this.item.push(o);
18374         
18375         this.lastData = o;
18376         
18377         this.syncValue();
18378         
18379         this.inputEl().dom.value = '';
18380         
18381         this.validate();
18382     },
18383     
18384     onRemoveItem : function(e, _self, o)
18385     {
18386         e.preventDefault();
18387         
18388         this.lastItem = Roo.apply([], this.item);
18389         
18390         var index = this.item.indexOf(o.data) * 1;
18391         
18392         if( index < 0){
18393             Roo.log('not this item?!');
18394             return;
18395         }
18396         
18397         this.item.splice(index, 1);
18398         o.item.remove();
18399         
18400         this.syncValue();
18401         
18402         this.fireEvent('remove', this, e);
18403         
18404         this.validate();
18405         
18406     },
18407     
18408     syncValue : function()
18409     {
18410         if(!this.item.length){
18411             this.clearValue();
18412             return;
18413         }
18414             
18415         var value = [];
18416         var _this = this;
18417         Roo.each(this.item, function(i){
18418             if(_this.valueField){
18419                 value.push(i[_this.valueField]);
18420                 return;
18421             }
18422
18423             value.push(i);
18424         });
18425
18426         this.value = value.join(',');
18427
18428         if(this.hiddenField){
18429             this.hiddenField.dom.value = this.value;
18430         }
18431         
18432         this.store.fireEvent("datachanged", this.store);
18433         
18434         this.validate();
18435     },
18436     
18437     clearItem : function()
18438     {
18439         if(!this.multiple){
18440             return;
18441         }
18442         
18443         this.item = [];
18444         
18445         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18446            c.remove();
18447         });
18448         
18449         this.syncValue();
18450         
18451         this.validate();
18452         
18453         if(this.tickable && !Roo.isTouch){
18454             this.view.refresh();
18455         }
18456     },
18457     
18458     inputEl: function ()
18459     {
18460         if(Roo.isIOS && this.useNativeIOS){
18461             return this.el.select('select.roo-ios-select', true).first();
18462         }
18463         
18464         if(Roo.isTouch && this.mobileTouchView){
18465             return this.el.select('input.form-control',true).first();
18466         }
18467         
18468         if(this.tickable){
18469             return this.searchField;
18470         }
18471         
18472         return this.el.select('input.form-control',true).first();
18473     },
18474     
18475     onTickableFooterButtonClick : function(e, btn, el)
18476     {
18477         e.preventDefault();
18478         
18479         this.lastItem = Roo.apply([], this.item);
18480         
18481         if(btn && btn.name == 'cancel'){
18482             this.tickItems = Roo.apply([], this.item);
18483             this.collapse();
18484             return;
18485         }
18486         
18487         this.clearItem();
18488         
18489         var _this = this;
18490         
18491         Roo.each(this.tickItems, function(o){
18492             _this.addItem(o);
18493         });
18494         
18495         this.collapse();
18496         
18497     },
18498     
18499     validate : function()
18500     {
18501         if(this.getVisibilityEl().hasClass('hidden')){
18502             return true;
18503         }
18504         
18505         var v = this.getRawValue();
18506         
18507         if(this.multiple){
18508             v = this.getValue();
18509         }
18510         
18511         if(this.disabled || this.allowBlank || v.length){
18512             this.markValid();
18513             return true;
18514         }
18515         
18516         this.markInvalid();
18517         return false;
18518     },
18519     
18520     tickableInputEl : function()
18521     {
18522         if(!this.tickable || !this.editable){
18523             return this.inputEl();
18524         }
18525         
18526         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18527     },
18528     
18529     
18530     getAutoCreateTouchView : function()
18531     {
18532         var id = Roo.id();
18533         
18534         var cfg = {
18535             cls: 'form-group' //input-group
18536         };
18537         
18538         var input =  {
18539             tag: 'input',
18540             id : id,
18541             type : this.inputType,
18542             cls : 'form-control x-combo-noedit',
18543             autocomplete: 'new-password',
18544             placeholder : this.placeholder || '',
18545             readonly : true
18546         };
18547         
18548         if (this.name) {
18549             input.name = this.name;
18550         }
18551         
18552         if (this.size) {
18553             input.cls += ' input-' + this.size;
18554         }
18555         
18556         if (this.disabled) {
18557             input.disabled = true;
18558         }
18559         
18560         var inputblock = {
18561             cls : 'roo-combobox-wrap',
18562             cn : [
18563                 input
18564             ]
18565         };
18566         
18567         if(this.before){
18568             inputblock.cls += ' input-group';
18569             
18570             inputblock.cn.unshift({
18571                 tag :'span',
18572                 cls : 'input-group-addon input-group-prepend input-group-text',
18573                 html : this.before
18574             });
18575         }
18576         
18577         if(this.removable && !this.multiple){
18578             inputblock.cls += ' roo-removable';
18579             
18580             inputblock.cn.push({
18581                 tag: 'button',
18582                 html : 'x',
18583                 cls : 'roo-combo-removable-btn close'
18584             });
18585         }
18586
18587         if(this.hasFeedback && !this.allowBlank){
18588             
18589             inputblock.cls += ' has-feedback';
18590             
18591             inputblock.cn.push({
18592                 tag: 'span',
18593                 cls: 'glyphicon form-control-feedback'
18594             });
18595             
18596         }
18597         
18598         if (this.after) {
18599             
18600             inputblock.cls += (this.before) ? '' : ' input-group';
18601             
18602             inputblock.cn.push({
18603                 tag :'span',
18604                 cls : 'input-group-addon input-group-append input-group-text',
18605                 html : this.after
18606             });
18607         }
18608
18609         
18610         var ibwrap = inputblock;
18611         
18612         if(this.multiple){
18613             ibwrap = {
18614                 tag: 'ul',
18615                 cls: 'roo-select2-choices',
18616                 cn:[
18617                     {
18618                         tag: 'li',
18619                         cls: 'roo-select2-search-field',
18620                         cn: [
18621
18622                             inputblock
18623                         ]
18624                     }
18625                 ]
18626             };
18627         
18628             
18629         }
18630         
18631         var combobox = {
18632             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18633             cn: [
18634                 {
18635                     tag: 'input',
18636                     type : 'hidden',
18637                     cls: 'form-hidden-field'
18638                 },
18639                 ibwrap
18640             ]
18641         };
18642         
18643         if(!this.multiple && this.showToggleBtn){
18644             
18645             var caret = {
18646                 cls: 'caret'
18647             };
18648             
18649             if (this.caret != false) {
18650                 caret = {
18651                      tag: 'i',
18652                      cls: 'fa fa-' + this.caret
18653                 };
18654                 
18655             }
18656             
18657             combobox.cn.push({
18658                 tag :'span',
18659                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18660                 cn : [
18661                     Roo.bootstrap.version == 3 ? caret : '',
18662                     {
18663                         tag: 'span',
18664                         cls: 'combobox-clear',
18665                         cn  : [
18666                             {
18667                                 tag : 'i',
18668                                 cls: 'icon-remove'
18669                             }
18670                         ]
18671                     }
18672                 ]
18673
18674             })
18675         }
18676         
18677         if(this.multiple){
18678             combobox.cls += ' roo-select2-container-multi';
18679         }
18680         
18681         var required =  this.allowBlank ?  {
18682                     tag : 'i',
18683                     style: 'display: none'
18684                 } : {
18685                    tag : 'i',
18686                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18687                    tooltip : 'This field is required'
18688                 };
18689         
18690         var align = this.labelAlign || this.parentLabelAlign();
18691         
18692         if (align ==='left' && this.fieldLabel.length) {
18693
18694             cfg.cn = [
18695                 required,
18696                 {
18697                     tag: 'label',
18698                     cls : 'control-label col-form-label',
18699                     html : this.fieldLabel
18700
18701                 },
18702                 {
18703                     cls : 'roo-combobox-wrap ', 
18704                     cn: [
18705                         combobox
18706                     ]
18707                 }
18708             ];
18709             
18710             var labelCfg = cfg.cn[1];
18711             var contentCfg = cfg.cn[2];
18712             
18713
18714             if(this.indicatorpos == 'right'){
18715                 cfg.cn = [
18716                     {
18717                         tag: 'label',
18718                         'for' :  id,
18719                         cls : 'control-label col-form-label',
18720                         cn : [
18721                             {
18722                                 tag : 'span',
18723                                 html : this.fieldLabel
18724                             },
18725                             required
18726                         ]
18727                     },
18728                     {
18729                         cls : "roo-combobox-wrap ",
18730                         cn: [
18731                             combobox
18732                         ]
18733                     }
18734
18735                 ];
18736                 
18737                 labelCfg = cfg.cn[0];
18738                 contentCfg = cfg.cn[1];
18739             }
18740             
18741            
18742             
18743             if(this.labelWidth > 12){
18744                 labelCfg.style = "width: " + this.labelWidth + 'px';
18745             }
18746            
18747             if(this.labelWidth < 13 && this.labelmd == 0){
18748                 this.labelmd = this.labelWidth;
18749             }
18750             
18751             if(this.labellg > 0){
18752                 labelCfg.cls += ' col-lg-' + this.labellg;
18753                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18754             }
18755             
18756             if(this.labelmd > 0){
18757                 labelCfg.cls += ' col-md-' + this.labelmd;
18758                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18759             }
18760             
18761             if(this.labelsm > 0){
18762                 labelCfg.cls += ' col-sm-' + this.labelsm;
18763                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18764             }
18765             
18766             if(this.labelxs > 0){
18767                 labelCfg.cls += ' col-xs-' + this.labelxs;
18768                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18769             }
18770                 
18771                 
18772         } else if ( this.fieldLabel.length) {
18773             cfg.cn = [
18774                required,
18775                 {
18776                     tag: 'label',
18777                     cls : 'control-label',
18778                     html : this.fieldLabel
18779
18780                 },
18781                 {
18782                     cls : '', 
18783                     cn: [
18784                         combobox
18785                     ]
18786                 }
18787             ];
18788             
18789             if(this.indicatorpos == 'right'){
18790                 cfg.cn = [
18791                     {
18792                         tag: 'label',
18793                         cls : 'control-label',
18794                         html : this.fieldLabel,
18795                         cn : [
18796                             required
18797                         ]
18798                     },
18799                     {
18800                         cls : '', 
18801                         cn: [
18802                             combobox
18803                         ]
18804                     }
18805                 ];
18806             }
18807         } else {
18808             cfg.cn = combobox;    
18809         }
18810         
18811         
18812         var settings = this;
18813         
18814         ['xs','sm','md','lg'].map(function(size){
18815             if (settings[size]) {
18816                 cfg.cls += ' col-' + size + '-' + settings[size];
18817             }
18818         });
18819         
18820         return cfg;
18821     },
18822     
18823     initTouchView : function()
18824     {
18825         this.renderTouchView();
18826         
18827         this.touchViewEl.on('scroll', function(){
18828             this.el.dom.scrollTop = 0;
18829         }, this);
18830         
18831         this.originalValue = this.getValue();
18832         
18833         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18834         
18835         this.inputEl().on("click", this.showTouchView, this);
18836         if (this.triggerEl) {
18837             this.triggerEl.on("click", this.showTouchView, this);
18838         }
18839         
18840         
18841         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18842         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18843         
18844         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18845         
18846         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18847         this.store.on('load', this.onTouchViewLoad, this);
18848         this.store.on('loadexception', this.onTouchViewLoadException, this);
18849         
18850         if(this.hiddenName){
18851             
18852             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18853             
18854             this.hiddenField.dom.value =
18855                 this.hiddenValue !== undefined ? this.hiddenValue :
18856                 this.value !== undefined ? this.value : '';
18857         
18858             this.el.dom.removeAttribute('name');
18859             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18860         }
18861         
18862         if(this.multiple){
18863             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18864             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18865         }
18866         
18867         if(this.removable && !this.multiple){
18868             var close = this.closeTriggerEl();
18869             if(close){
18870                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18871                 close.on('click', this.removeBtnClick, this, close);
18872             }
18873         }
18874         /*
18875          * fix the bug in Safari iOS8
18876          */
18877         this.inputEl().on("focus", function(e){
18878             document.activeElement.blur();
18879         }, this);
18880         
18881         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18882         
18883         return;
18884         
18885         
18886     },
18887     
18888     renderTouchView : function()
18889     {
18890         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18891         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18892         
18893         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18894         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18895         
18896         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18897         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898         this.touchViewBodyEl.setStyle('overflow', 'auto');
18899         
18900         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18901         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18902         
18903         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18904         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18905         
18906     },
18907     
18908     showTouchView : function()
18909     {
18910         if(this.disabled){
18911             return;
18912         }
18913         
18914         this.touchViewHeaderEl.hide();
18915
18916         if(this.modalTitle.length){
18917             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18918             this.touchViewHeaderEl.show();
18919         }
18920
18921         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18922         this.touchViewEl.show();
18923
18924         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18925         
18926         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18927         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18928
18929         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18930
18931         if(this.modalTitle.length){
18932             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18933         }
18934         
18935         this.touchViewBodyEl.setHeight(bodyHeight);
18936
18937         if(this.animate){
18938             var _this = this;
18939             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18940         }else{
18941             this.touchViewEl.addClass(['in','show']);
18942         }
18943         
18944         if(this._touchViewMask){
18945             Roo.get(document.body).addClass("x-body-masked");
18946             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18947             this._touchViewMask.setStyle('z-index', 10000);
18948             this._touchViewMask.addClass('show');
18949         }
18950         
18951         this.doTouchViewQuery();
18952         
18953     },
18954     
18955     hideTouchView : function()
18956     {
18957         this.touchViewEl.removeClass(['in','show']);
18958
18959         if(this.animate){
18960             var _this = this;
18961             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18962         }else{
18963             this.touchViewEl.setStyle('display', 'none');
18964         }
18965         
18966         if(this._touchViewMask){
18967             this._touchViewMask.removeClass('show');
18968             Roo.get(document.body).removeClass("x-body-masked");
18969         }
18970     },
18971     
18972     setTouchViewValue : function()
18973     {
18974         if(this.multiple){
18975             this.clearItem();
18976         
18977             var _this = this;
18978
18979             Roo.each(this.tickItems, function(o){
18980                 this.addItem(o);
18981             }, this);
18982         }
18983         
18984         this.hideTouchView();
18985     },
18986     
18987     doTouchViewQuery : function()
18988     {
18989         var qe = {
18990             query: '',
18991             forceAll: true,
18992             combo: this,
18993             cancel:false
18994         };
18995         
18996         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18997             return false;
18998         }
18999         
19000         if(!this.alwaysQuery || this.mode == 'local'){
19001             this.onTouchViewLoad();
19002             return;
19003         }
19004         
19005         this.store.load();
19006     },
19007     
19008     onTouchViewBeforeLoad : function(combo,opts)
19009     {
19010         return;
19011     },
19012
19013     // private
19014     onTouchViewLoad : function()
19015     {
19016         if(this.store.getCount() < 1){
19017             this.onTouchViewEmptyResults();
19018             return;
19019         }
19020         
19021         this.clearTouchView();
19022         
19023         var rawValue = this.getRawValue();
19024         
19025         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19026         
19027         this.tickItems = [];
19028         
19029         this.store.data.each(function(d, rowIndex){
19030             var row = this.touchViewListGroup.createChild(template);
19031             
19032             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19033                 row.addClass(d.data.cls);
19034             }
19035             
19036             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19037                 var cfg = {
19038                     data : d.data,
19039                     html : d.data[this.displayField]
19040                 };
19041                 
19042                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19043                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19044                 }
19045             }
19046             row.removeClass('selected');
19047             if(!this.multiple && this.valueField &&
19048                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19049             {
19050                 // radio buttons..
19051                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19052                 row.addClass('selected');
19053             }
19054             
19055             if(this.multiple && this.valueField &&
19056                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19057             {
19058                 
19059                 // checkboxes...
19060                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19061                 this.tickItems.push(d.data);
19062             }
19063             
19064             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19065             
19066         }, this);
19067         
19068         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19069         
19070         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19071
19072         if(this.modalTitle.length){
19073             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19074         }
19075
19076         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19077         
19078         if(this.mobile_restrict_height && listHeight < bodyHeight){
19079             this.touchViewBodyEl.setHeight(listHeight);
19080         }
19081         
19082         var _this = this;
19083         
19084         if(firstChecked && listHeight > bodyHeight){
19085             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19086         }
19087         
19088     },
19089     
19090     onTouchViewLoadException : function()
19091     {
19092         this.hideTouchView();
19093     },
19094     
19095     onTouchViewEmptyResults : function()
19096     {
19097         this.clearTouchView();
19098         
19099         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19100         
19101         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19102         
19103     },
19104     
19105     clearTouchView : function()
19106     {
19107         this.touchViewListGroup.dom.innerHTML = '';
19108     },
19109     
19110     onTouchViewClick : function(e, el, o)
19111     {
19112         e.preventDefault();
19113         
19114         var row = o.row;
19115         var rowIndex = o.rowIndex;
19116         
19117         var r = this.store.getAt(rowIndex);
19118         
19119         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19120             
19121             if(!this.multiple){
19122                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19123                     c.dom.removeAttribute('checked');
19124                 }, this);
19125
19126                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19127
19128                 this.setFromData(r.data);
19129
19130                 var close = this.closeTriggerEl();
19131
19132                 if(close){
19133                     close.show();
19134                 }
19135
19136                 this.hideTouchView();
19137
19138                 this.fireEvent('select', this, r, rowIndex);
19139
19140                 return;
19141             }
19142
19143             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19144                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19145                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19146                 return;
19147             }
19148
19149             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19150             this.addItem(r.data);
19151             this.tickItems.push(r.data);
19152         }
19153     },
19154     
19155     getAutoCreateNativeIOS : function()
19156     {
19157         var cfg = {
19158             cls: 'form-group' //input-group,
19159         };
19160         
19161         var combobox =  {
19162             tag: 'select',
19163             cls : 'roo-ios-select'
19164         };
19165         
19166         if (this.name) {
19167             combobox.name = this.name;
19168         }
19169         
19170         if (this.disabled) {
19171             combobox.disabled = true;
19172         }
19173         
19174         var settings = this;
19175         
19176         ['xs','sm','md','lg'].map(function(size){
19177             if (settings[size]) {
19178                 cfg.cls += ' col-' + size + '-' + settings[size];
19179             }
19180         });
19181         
19182         cfg.cn = combobox;
19183         
19184         return cfg;
19185         
19186     },
19187     
19188     initIOSView : function()
19189     {
19190         this.store.on('load', this.onIOSViewLoad, this);
19191         
19192         return;
19193     },
19194     
19195     onIOSViewLoad : function()
19196     {
19197         if(this.store.getCount() < 1){
19198             return;
19199         }
19200         
19201         this.clearIOSView();
19202         
19203         if(this.allowBlank) {
19204             
19205             var default_text = '-- SELECT --';
19206             
19207             if(this.placeholder.length){
19208                 default_text = this.placeholder;
19209             }
19210             
19211             if(this.emptyTitle.length){
19212                 default_text += ' - ' + this.emptyTitle + ' -';
19213             }
19214             
19215             var opt = this.inputEl().createChild({
19216                 tag: 'option',
19217                 value : 0,
19218                 html : default_text
19219             });
19220             
19221             var o = {};
19222             o[this.valueField] = 0;
19223             o[this.displayField] = default_text;
19224             
19225             this.ios_options.push({
19226                 data : o,
19227                 el : opt
19228             });
19229             
19230         }
19231         
19232         this.store.data.each(function(d, rowIndex){
19233             
19234             var html = '';
19235             
19236             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19237                 html = d.data[this.displayField];
19238             }
19239             
19240             var value = '';
19241             
19242             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19243                 value = d.data[this.valueField];
19244             }
19245             
19246             var option = {
19247                 tag: 'option',
19248                 value : value,
19249                 html : html
19250             };
19251             
19252             if(this.value == d.data[this.valueField]){
19253                 option['selected'] = true;
19254             }
19255             
19256             var opt = this.inputEl().createChild(option);
19257             
19258             this.ios_options.push({
19259                 data : d.data,
19260                 el : opt
19261             });
19262             
19263         }, this);
19264         
19265         this.inputEl().on('change', function(){
19266            this.fireEvent('select', this);
19267         }, this);
19268         
19269     },
19270     
19271     clearIOSView: function()
19272     {
19273         this.inputEl().dom.innerHTML = '';
19274         
19275         this.ios_options = [];
19276     },
19277     
19278     setIOSValue: function(v)
19279     {
19280         this.value = v;
19281         
19282         if(!this.ios_options){
19283             return;
19284         }
19285         
19286         Roo.each(this.ios_options, function(opts){
19287            
19288            opts.el.dom.removeAttribute('selected');
19289            
19290            if(opts.data[this.valueField] != v){
19291                return;
19292            }
19293            
19294            opts.el.dom.setAttribute('selected', true);
19295            
19296         }, this);
19297     }
19298
19299     /** 
19300     * @cfg {Boolean} grow 
19301     * @hide 
19302     */
19303     /** 
19304     * @cfg {Number} growMin 
19305     * @hide 
19306     */
19307     /** 
19308     * @cfg {Number} growMax 
19309     * @hide 
19310     */
19311     /**
19312      * @hide
19313      * @method autoSize
19314      */
19315 });
19316
19317 Roo.apply(Roo.bootstrap.ComboBox,  {
19318     
19319     header : {
19320         tag: 'div',
19321         cls: 'modal-header',
19322         cn: [
19323             {
19324                 tag: 'h4',
19325                 cls: 'modal-title'
19326             }
19327         ]
19328     },
19329     
19330     body : {
19331         tag: 'div',
19332         cls: 'modal-body',
19333         cn: [
19334             {
19335                 tag: 'ul',
19336                 cls: 'list-group'
19337             }
19338         ]
19339     },
19340     
19341     listItemRadio : {
19342         tag: 'li',
19343         cls: 'list-group-item',
19344         cn: [
19345             {
19346                 tag: 'span',
19347                 cls: 'roo-combobox-list-group-item-value'
19348             },
19349             {
19350                 tag: 'div',
19351                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19352                 cn: [
19353                     {
19354                         tag: 'input',
19355                         type: 'radio'
19356                     },
19357                     {
19358                         tag: 'label'
19359                     }
19360                 ]
19361             }
19362         ]
19363     },
19364     
19365     listItemCheckbox : {
19366         tag: 'li',
19367         cls: 'list-group-item',
19368         cn: [
19369             {
19370                 tag: 'span',
19371                 cls: 'roo-combobox-list-group-item-value'
19372             },
19373             {
19374                 tag: 'div',
19375                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19376                 cn: [
19377                     {
19378                         tag: 'input',
19379                         type: 'checkbox'
19380                     },
19381                     {
19382                         tag: 'label'
19383                     }
19384                 ]
19385             }
19386         ]
19387     },
19388     
19389     emptyResult : {
19390         tag: 'div',
19391         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19392     },
19393     
19394     footer : {
19395         tag: 'div',
19396         cls: 'modal-footer',
19397         cn: [
19398             {
19399                 tag: 'div',
19400                 cls: 'row',
19401                 cn: [
19402                     {
19403                         tag: 'div',
19404                         cls: 'col-xs-6 text-left',
19405                         cn: {
19406                             tag: 'button',
19407                             cls: 'btn btn-danger roo-touch-view-cancel',
19408                             html: 'Cancel'
19409                         }
19410                     },
19411                     {
19412                         tag: 'div',
19413                         cls: 'col-xs-6 text-right',
19414                         cn: {
19415                             tag: 'button',
19416                             cls: 'btn btn-success roo-touch-view-ok',
19417                             html: 'OK'
19418                         }
19419                     }
19420                 ]
19421             }
19422         ]
19423         
19424     }
19425 });
19426
19427 Roo.apply(Roo.bootstrap.ComboBox,  {
19428     
19429     touchViewTemplate : {
19430         tag: 'div',
19431         cls: 'modal fade roo-combobox-touch-view',
19432         cn: [
19433             {
19434                 tag: 'div',
19435                 cls: 'modal-dialog',
19436                 style : 'position:fixed', // we have to fix position....
19437                 cn: [
19438                     {
19439                         tag: 'div',
19440                         cls: 'modal-content',
19441                         cn: [
19442                             Roo.bootstrap.ComboBox.header,
19443                             Roo.bootstrap.ComboBox.body,
19444                             Roo.bootstrap.ComboBox.footer
19445                         ]
19446                     }
19447                 ]
19448             }
19449         ]
19450     }
19451 });/*
19452  * Based on:
19453  * Ext JS Library 1.1.1
19454  * Copyright(c) 2006-2007, Ext JS, LLC.
19455  *
19456  * Originally Released Under LGPL - original licence link has changed is not relivant.
19457  *
19458  * Fork - LGPL
19459  * <script type="text/javascript">
19460  */
19461
19462 /**
19463  * @class Roo.View
19464  * @extends Roo.util.Observable
19465  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19466  * This class also supports single and multi selection modes. <br>
19467  * Create a data model bound view:
19468  <pre><code>
19469  var store = new Roo.data.Store(...);
19470
19471  var view = new Roo.View({
19472     el : "my-element",
19473     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19474  
19475     singleSelect: true,
19476     selectedClass: "ydataview-selected",
19477     store: store
19478  });
19479
19480  // listen for node click?
19481  view.on("click", function(vw, index, node, e){
19482  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19483  });
19484
19485  // load XML data
19486  dataModel.load("foobar.xml");
19487  </code></pre>
19488  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19489  * <br><br>
19490  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19491  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19492  * 
19493  * Note: old style constructor is still suported (container, template, config)
19494  * 
19495  * @constructor
19496  * Create a new View
19497  * @param {Object} config The config object
19498  * 
19499  */
19500 Roo.View = function(config, depreciated_tpl, depreciated_config){
19501     
19502     this.parent = false;
19503     
19504     if (typeof(depreciated_tpl) == 'undefined') {
19505         // new way.. - universal constructor.
19506         Roo.apply(this, config);
19507         this.el  = Roo.get(this.el);
19508     } else {
19509         // old format..
19510         this.el  = Roo.get(config);
19511         this.tpl = depreciated_tpl;
19512         Roo.apply(this, depreciated_config);
19513     }
19514     this.wrapEl  = this.el.wrap().wrap();
19515     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19516     
19517     
19518     if(typeof(this.tpl) == "string"){
19519         this.tpl = new Roo.Template(this.tpl);
19520     } else {
19521         // support xtype ctors..
19522         this.tpl = new Roo.factory(this.tpl, Roo);
19523     }
19524     
19525     
19526     this.tpl.compile();
19527     
19528     /** @private */
19529     this.addEvents({
19530         /**
19531          * @event beforeclick
19532          * Fires before a click is processed. Returns false to cancel the default action.
19533          * @param {Roo.View} this
19534          * @param {Number} index The index of the target node
19535          * @param {HTMLElement} node The target node
19536          * @param {Roo.EventObject} e The raw event object
19537          */
19538             "beforeclick" : true,
19539         /**
19540          * @event click
19541          * Fires when a template node is clicked.
19542          * @param {Roo.View} this
19543          * @param {Number} index The index of the target node
19544          * @param {HTMLElement} node The target node
19545          * @param {Roo.EventObject} e The raw event object
19546          */
19547             "click" : true,
19548         /**
19549          * @event dblclick
19550          * Fires when a template node is double clicked.
19551          * @param {Roo.View} this
19552          * @param {Number} index The index of the target node
19553          * @param {HTMLElement} node The target node
19554          * @param {Roo.EventObject} e The raw event object
19555          */
19556             "dblclick" : true,
19557         /**
19558          * @event contextmenu
19559          * Fires when a template node is right clicked.
19560          * @param {Roo.View} this
19561          * @param {Number} index The index of the target node
19562          * @param {HTMLElement} node The target node
19563          * @param {Roo.EventObject} e The raw event object
19564          */
19565             "contextmenu" : true,
19566         /**
19567          * @event selectionchange
19568          * Fires when the selected nodes change.
19569          * @param {Roo.View} this
19570          * @param {Array} selections Array of the selected nodes
19571          */
19572             "selectionchange" : true,
19573     
19574         /**
19575          * @event beforeselect
19576          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19577          * @param {Roo.View} this
19578          * @param {HTMLElement} node The node to be selected
19579          * @param {Array} selections Array of currently selected nodes
19580          */
19581             "beforeselect" : true,
19582         /**
19583          * @event preparedata
19584          * Fires on every row to render, to allow you to change the data.
19585          * @param {Roo.View} this
19586          * @param {Object} data to be rendered (change this)
19587          */
19588           "preparedata" : true
19589           
19590           
19591         });
19592
19593
19594
19595     this.el.on({
19596         "click": this.onClick,
19597         "dblclick": this.onDblClick,
19598         "contextmenu": this.onContextMenu,
19599         scope:this
19600     });
19601
19602     this.selections = [];
19603     this.nodes = [];
19604     this.cmp = new Roo.CompositeElementLite([]);
19605     if(this.store){
19606         this.store = Roo.factory(this.store, Roo.data);
19607         this.setStore(this.store, true);
19608     }
19609     
19610     if ( this.footer && this.footer.xtype) {
19611            
19612          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19613         
19614         this.footer.dataSource = this.store;
19615         this.footer.container = fctr;
19616         this.footer = Roo.factory(this.footer, Roo);
19617         fctr.insertFirst(this.el);
19618         
19619         // this is a bit insane - as the paging toolbar seems to detach the el..
19620 //        dom.parentNode.parentNode.parentNode
19621          // they get detached?
19622     }
19623     
19624     
19625     Roo.View.superclass.constructor.call(this);
19626     
19627     
19628 };
19629
19630 Roo.extend(Roo.View, Roo.util.Observable, {
19631     
19632      /**
19633      * @cfg {Roo.data.Store} store Data store to load data from.
19634      */
19635     store : false,
19636     
19637     /**
19638      * @cfg {String|Roo.Element} el The container element.
19639      */
19640     el : '',
19641     
19642     /**
19643      * @cfg {String|Roo.Template} tpl The template used by this View 
19644      */
19645     tpl : false,
19646     /**
19647      * @cfg {String} dataName the named area of the template to use as the data area
19648      *                          Works with domtemplates roo-name="name"
19649      */
19650     dataName: false,
19651     /**
19652      * @cfg {String} selectedClass The css class to add to selected nodes
19653      */
19654     selectedClass : "x-view-selected",
19655      /**
19656      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19657      */
19658     emptyText : "",
19659     
19660     /**
19661      * @cfg {String} text to display on mask (default Loading)
19662      */
19663     mask : false,
19664     /**
19665      * @cfg {Boolean} multiSelect Allow multiple selection
19666      */
19667     multiSelect : false,
19668     /**
19669      * @cfg {Boolean} singleSelect Allow single selection
19670      */
19671     singleSelect:  false,
19672     
19673     /**
19674      * @cfg {Boolean} toggleSelect - selecting 
19675      */
19676     toggleSelect : false,
19677     
19678     /**
19679      * @cfg {Boolean} tickable - selecting 
19680      */
19681     tickable : false,
19682     
19683     /**
19684      * Returns the element this view is bound to.
19685      * @return {Roo.Element}
19686      */
19687     getEl : function(){
19688         return this.wrapEl;
19689     },
19690     
19691     
19692
19693     /**
19694      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19695      */
19696     refresh : function(){
19697         //Roo.log('refresh');
19698         var t = this.tpl;
19699         
19700         // if we are using something like 'domtemplate', then
19701         // the what gets used is:
19702         // t.applySubtemplate(NAME, data, wrapping data..)
19703         // the outer template then get' applied with
19704         //     the store 'extra data'
19705         // and the body get's added to the
19706         //      roo-name="data" node?
19707         //      <span class='roo-tpl-{name}'></span> ?????
19708         
19709         
19710         
19711         this.clearSelections();
19712         this.el.update("");
19713         var html = [];
19714         var records = this.store.getRange();
19715         if(records.length < 1) {
19716             
19717             // is this valid??  = should it render a template??
19718             
19719             this.el.update(this.emptyText);
19720             return;
19721         }
19722         var el = this.el;
19723         if (this.dataName) {
19724             this.el.update(t.apply(this.store.meta)); //????
19725             el = this.el.child('.roo-tpl-' + this.dataName);
19726         }
19727         
19728         for(var i = 0, len = records.length; i < len; i++){
19729             var data = this.prepareData(records[i].data, i, records[i]);
19730             this.fireEvent("preparedata", this, data, i, records[i]);
19731             
19732             var d = Roo.apply({}, data);
19733             
19734             if(this.tickable){
19735                 Roo.apply(d, {'roo-id' : Roo.id()});
19736                 
19737                 var _this = this;
19738             
19739                 Roo.each(this.parent.item, function(item){
19740                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19741                         return;
19742                     }
19743                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19744                 });
19745             }
19746             
19747             html[html.length] = Roo.util.Format.trim(
19748                 this.dataName ?
19749                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19750                     t.apply(d)
19751             );
19752         }
19753         
19754         
19755         
19756         el.update(html.join(""));
19757         this.nodes = el.dom.childNodes;
19758         this.updateIndexes(0);
19759     },
19760     
19761
19762     /**
19763      * Function to override to reformat the data that is sent to
19764      * the template for each node.
19765      * DEPRICATED - use the preparedata event handler.
19766      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19767      * a JSON object for an UpdateManager bound view).
19768      */
19769     prepareData : function(data, index, record)
19770     {
19771         this.fireEvent("preparedata", this, data, index, record);
19772         return data;
19773     },
19774
19775     onUpdate : function(ds, record){
19776         // Roo.log('on update');   
19777         this.clearSelections();
19778         var index = this.store.indexOf(record);
19779         var n = this.nodes[index];
19780         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19781         n.parentNode.removeChild(n);
19782         this.updateIndexes(index, index);
19783     },
19784
19785     
19786     
19787 // --------- FIXME     
19788     onAdd : function(ds, records, index)
19789     {
19790         //Roo.log(['on Add', ds, records, index] );        
19791         this.clearSelections();
19792         if(this.nodes.length == 0){
19793             this.refresh();
19794             return;
19795         }
19796         var n = this.nodes[index];
19797         for(var i = 0, len = records.length; i < len; i++){
19798             var d = this.prepareData(records[i].data, i, records[i]);
19799             if(n){
19800                 this.tpl.insertBefore(n, d);
19801             }else{
19802                 
19803                 this.tpl.append(this.el, d);
19804             }
19805         }
19806         this.updateIndexes(index);
19807     },
19808
19809     onRemove : function(ds, record, index){
19810        // Roo.log('onRemove');
19811         this.clearSelections();
19812         var el = this.dataName  ?
19813             this.el.child('.roo-tpl-' + this.dataName) :
19814             this.el; 
19815         
19816         el.dom.removeChild(this.nodes[index]);
19817         this.updateIndexes(index);
19818     },
19819
19820     /**
19821      * Refresh an individual node.
19822      * @param {Number} index
19823      */
19824     refreshNode : function(index){
19825         this.onUpdate(this.store, this.store.getAt(index));
19826     },
19827
19828     updateIndexes : function(startIndex, endIndex){
19829         var ns = this.nodes;
19830         startIndex = startIndex || 0;
19831         endIndex = endIndex || ns.length - 1;
19832         for(var i = startIndex; i <= endIndex; i++){
19833             ns[i].nodeIndex = i;
19834         }
19835     },
19836
19837     /**
19838      * Changes the data store this view uses and refresh the view.
19839      * @param {Store} store
19840      */
19841     setStore : function(store, initial){
19842         if(!initial && this.store){
19843             this.store.un("datachanged", this.refresh);
19844             this.store.un("add", this.onAdd);
19845             this.store.un("remove", this.onRemove);
19846             this.store.un("update", this.onUpdate);
19847             this.store.un("clear", this.refresh);
19848             this.store.un("beforeload", this.onBeforeLoad);
19849             this.store.un("load", this.onLoad);
19850             this.store.un("loadexception", this.onLoad);
19851         }
19852         if(store){
19853           
19854             store.on("datachanged", this.refresh, this);
19855             store.on("add", this.onAdd, this);
19856             store.on("remove", this.onRemove, this);
19857             store.on("update", this.onUpdate, this);
19858             store.on("clear", this.refresh, this);
19859             store.on("beforeload", this.onBeforeLoad, this);
19860             store.on("load", this.onLoad, this);
19861             store.on("loadexception", this.onLoad, this);
19862         }
19863         
19864         if(store){
19865             this.refresh();
19866         }
19867     },
19868     /**
19869      * onbeforeLoad - masks the loading area.
19870      *
19871      */
19872     onBeforeLoad : function(store,opts)
19873     {
19874          //Roo.log('onBeforeLoad');   
19875         if (!opts.add) {
19876             this.el.update("");
19877         }
19878         this.el.mask(this.mask ? this.mask : "Loading" ); 
19879     },
19880     onLoad : function ()
19881     {
19882         this.el.unmask();
19883     },
19884     
19885
19886     /**
19887      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19888      * @param {HTMLElement} node
19889      * @return {HTMLElement} The template node
19890      */
19891     findItemFromChild : function(node){
19892         var el = this.dataName  ?
19893             this.el.child('.roo-tpl-' + this.dataName,true) :
19894             this.el.dom; 
19895         
19896         if(!node || node.parentNode == el){
19897                     return node;
19898             }
19899             var p = node.parentNode;
19900             while(p && p != el){
19901             if(p.parentNode == el){
19902                 return p;
19903             }
19904             p = p.parentNode;
19905         }
19906             return null;
19907     },
19908
19909     /** @ignore */
19910     onClick : function(e){
19911         var item = this.findItemFromChild(e.getTarget());
19912         if(item){
19913             var index = this.indexOf(item);
19914             if(this.onItemClick(item, index, e) !== false){
19915                 this.fireEvent("click", this, index, item, e);
19916             }
19917         }else{
19918             this.clearSelections();
19919         }
19920     },
19921
19922     /** @ignore */
19923     onContextMenu : function(e){
19924         var item = this.findItemFromChild(e.getTarget());
19925         if(item){
19926             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19927         }
19928     },
19929
19930     /** @ignore */
19931     onDblClick : function(e){
19932         var item = this.findItemFromChild(e.getTarget());
19933         if(item){
19934             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19935         }
19936     },
19937
19938     onItemClick : function(item, index, e)
19939     {
19940         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19941             return false;
19942         }
19943         if (this.toggleSelect) {
19944             var m = this.isSelected(item) ? 'unselect' : 'select';
19945             //Roo.log(m);
19946             var _t = this;
19947             _t[m](item, true, false);
19948             return true;
19949         }
19950         if(this.multiSelect || this.singleSelect){
19951             if(this.multiSelect && e.shiftKey && this.lastSelection){
19952                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19953             }else{
19954                 this.select(item, this.multiSelect && e.ctrlKey);
19955                 this.lastSelection = item;
19956             }
19957             
19958             if(!this.tickable){
19959                 e.preventDefault();
19960             }
19961             
19962         }
19963         return true;
19964     },
19965
19966     /**
19967      * Get the number of selected nodes.
19968      * @return {Number}
19969      */
19970     getSelectionCount : function(){
19971         return this.selections.length;
19972     },
19973
19974     /**
19975      * Get the currently selected nodes.
19976      * @return {Array} An array of HTMLElements
19977      */
19978     getSelectedNodes : function(){
19979         return this.selections;
19980     },
19981
19982     /**
19983      * Get the indexes of the selected nodes.
19984      * @return {Array}
19985      */
19986     getSelectedIndexes : function(){
19987         var indexes = [], s = this.selections;
19988         for(var i = 0, len = s.length; i < len; i++){
19989             indexes.push(s[i].nodeIndex);
19990         }
19991         return indexes;
19992     },
19993
19994     /**
19995      * Clear all selections
19996      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19997      */
19998     clearSelections : function(suppressEvent){
19999         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20000             this.cmp.elements = this.selections;
20001             this.cmp.removeClass(this.selectedClass);
20002             this.selections = [];
20003             if(!suppressEvent){
20004                 this.fireEvent("selectionchange", this, this.selections);
20005             }
20006         }
20007     },
20008
20009     /**
20010      * Returns true if the passed node is selected
20011      * @param {HTMLElement/Number} node The node or node index
20012      * @return {Boolean}
20013      */
20014     isSelected : function(node){
20015         var s = this.selections;
20016         if(s.length < 1){
20017             return false;
20018         }
20019         node = this.getNode(node);
20020         return s.indexOf(node) !== -1;
20021     },
20022
20023     /**
20024      * Selects nodes.
20025      * @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
20026      * @param {Boolean} keepExisting (optional) true to keep existing selections
20027      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20028      */
20029     select : function(nodeInfo, keepExisting, suppressEvent){
20030         if(nodeInfo instanceof Array){
20031             if(!keepExisting){
20032                 this.clearSelections(true);
20033             }
20034             for(var i = 0, len = nodeInfo.length; i < len; i++){
20035                 this.select(nodeInfo[i], true, true);
20036             }
20037             return;
20038         } 
20039         var node = this.getNode(nodeInfo);
20040         if(!node || this.isSelected(node)){
20041             return; // already selected.
20042         }
20043         if(!keepExisting){
20044             this.clearSelections(true);
20045         }
20046         
20047         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20048             Roo.fly(node).addClass(this.selectedClass);
20049             this.selections.push(node);
20050             if(!suppressEvent){
20051                 this.fireEvent("selectionchange", this, this.selections);
20052             }
20053         }
20054         
20055         
20056     },
20057       /**
20058      * Unselects nodes.
20059      * @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
20060      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20061      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20062      */
20063     unselect : function(nodeInfo, keepExisting, suppressEvent)
20064     {
20065         if(nodeInfo instanceof Array){
20066             Roo.each(this.selections, function(s) {
20067                 this.unselect(s, nodeInfo);
20068             }, this);
20069             return;
20070         }
20071         var node = this.getNode(nodeInfo);
20072         if(!node || !this.isSelected(node)){
20073             //Roo.log("not selected");
20074             return; // not selected.
20075         }
20076         // fireevent???
20077         var ns = [];
20078         Roo.each(this.selections, function(s) {
20079             if (s == node ) {
20080                 Roo.fly(node).removeClass(this.selectedClass);
20081
20082                 return;
20083             }
20084             ns.push(s);
20085         },this);
20086         
20087         this.selections= ns;
20088         this.fireEvent("selectionchange", this, this.selections);
20089     },
20090
20091     /**
20092      * Gets a template node.
20093      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20094      * @return {HTMLElement} The node or null if it wasn't found
20095      */
20096     getNode : function(nodeInfo){
20097         if(typeof nodeInfo == "string"){
20098             return document.getElementById(nodeInfo);
20099         }else if(typeof nodeInfo == "number"){
20100             return this.nodes[nodeInfo];
20101         }
20102         return nodeInfo;
20103     },
20104
20105     /**
20106      * Gets a range template nodes.
20107      * @param {Number} startIndex
20108      * @param {Number} endIndex
20109      * @return {Array} An array of nodes
20110      */
20111     getNodes : function(start, end){
20112         var ns = this.nodes;
20113         start = start || 0;
20114         end = typeof end == "undefined" ? ns.length - 1 : end;
20115         var nodes = [];
20116         if(start <= end){
20117             for(var i = start; i <= end; i++){
20118                 nodes.push(ns[i]);
20119             }
20120         } else{
20121             for(var i = start; i >= end; i--){
20122                 nodes.push(ns[i]);
20123             }
20124         }
20125         return nodes;
20126     },
20127
20128     /**
20129      * Finds the index of the passed node
20130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20131      * @return {Number} The index of the node or -1
20132      */
20133     indexOf : function(node){
20134         node = this.getNode(node);
20135         if(typeof node.nodeIndex == "number"){
20136             return node.nodeIndex;
20137         }
20138         var ns = this.nodes;
20139         for(var i = 0, len = ns.length; i < len; i++){
20140             if(ns[i] == node){
20141                 return i;
20142             }
20143         }
20144         return -1;
20145     }
20146 });
20147 /*
20148  * - LGPL
20149  *
20150  * based on jquery fullcalendar
20151  * 
20152  */
20153
20154 Roo.bootstrap = Roo.bootstrap || {};
20155 /**
20156  * @class Roo.bootstrap.Calendar
20157  * @extends Roo.bootstrap.Component
20158  * Bootstrap Calendar class
20159  * @cfg {Boolean} loadMask (true|false) default false
20160  * @cfg {Object} header generate the user specific header of the calendar, default false
20161
20162  * @constructor
20163  * Create a new Container
20164  * @param {Object} config The config object
20165  */
20166
20167
20168
20169 Roo.bootstrap.Calendar = function(config){
20170     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20171      this.addEvents({
20172         /**
20173              * @event select
20174              * Fires when a date is selected
20175              * @param {DatePicker} this
20176              * @param {Date} date The selected date
20177              */
20178         'select': true,
20179         /**
20180              * @event monthchange
20181              * Fires when the displayed month changes 
20182              * @param {DatePicker} this
20183              * @param {Date} date The selected month
20184              */
20185         'monthchange': true,
20186         /**
20187              * @event evententer
20188              * Fires when mouse over an event
20189              * @param {Calendar} this
20190              * @param {event} Event
20191              */
20192         'evententer': true,
20193         /**
20194              * @event eventleave
20195              * Fires when the mouse leaves an
20196              * @param {Calendar} this
20197              * @param {event}
20198              */
20199         'eventleave': true,
20200         /**
20201              * @event eventclick
20202              * Fires when the mouse click an
20203              * @param {Calendar} this
20204              * @param {event}
20205              */
20206         'eventclick': true
20207         
20208     });
20209
20210 };
20211
20212 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20213     
20214           /**
20215      * @cfg {Roo.data.Store} store
20216      * The data source for the calendar
20217      */
20218         store : false,
20219      /**
20220      * @cfg {Number} startDay
20221      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20222      */
20223     startDay : 0,
20224     
20225     loadMask : false,
20226     
20227     header : false,
20228       
20229     getAutoCreate : function(){
20230         
20231         
20232         var fc_button = function(name, corner, style, content ) {
20233             return Roo.apply({},{
20234                 tag : 'span',
20235                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20236                          (corner.length ?
20237                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20238                             ''
20239                         ),
20240                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20241                 unselectable: 'on'
20242             });
20243         };
20244         
20245         var header = {};
20246         
20247         if(!this.header){
20248             header = {
20249                 tag : 'table',
20250                 cls : 'fc-header',
20251                 style : 'width:100%',
20252                 cn : [
20253                     {
20254                         tag: 'tr',
20255                         cn : [
20256                             {
20257                                 tag : 'td',
20258                                 cls : 'fc-header-left',
20259                                 cn : [
20260                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20261                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20262                                     { tag: 'span', cls: 'fc-header-space' },
20263                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20264
20265
20266                                 ]
20267                             },
20268
20269                             {
20270                                 tag : 'td',
20271                                 cls : 'fc-header-center',
20272                                 cn : [
20273                                     {
20274                                         tag: 'span',
20275                                         cls: 'fc-header-title',
20276                                         cn : {
20277                                             tag: 'H2',
20278                                             html : 'month / year'
20279                                         }
20280                                     }
20281
20282                                 ]
20283                             },
20284                             {
20285                                 tag : 'td',
20286                                 cls : 'fc-header-right',
20287                                 cn : [
20288                               /*      fc_button('month', 'left', '', 'month' ),
20289                                     fc_button('week', '', '', 'week' ),
20290                                     fc_button('day', 'right', '', 'day' )
20291                                 */    
20292
20293                                 ]
20294                             }
20295
20296                         ]
20297                     }
20298                 ]
20299             };
20300         }
20301         
20302         header = this.header;
20303         
20304        
20305         var cal_heads = function() {
20306             var ret = [];
20307             // fixme - handle this.
20308             
20309             for (var i =0; i < Date.dayNames.length; i++) {
20310                 var d = Date.dayNames[i];
20311                 ret.push({
20312                     tag: 'th',
20313                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20314                     html : d.substring(0,3)
20315                 });
20316                 
20317             }
20318             ret[0].cls += ' fc-first';
20319             ret[6].cls += ' fc-last';
20320             return ret;
20321         };
20322         var cal_cell = function(n) {
20323             return  {
20324                 tag: 'td',
20325                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20326                 cn : [
20327                     {
20328                         cn : [
20329                             {
20330                                 cls: 'fc-day-number',
20331                                 html: 'D'
20332                             },
20333                             {
20334                                 cls: 'fc-day-content',
20335                              
20336                                 cn : [
20337                                      {
20338                                         style: 'position: relative;' // height: 17px;
20339                                     }
20340                                 ]
20341                             }
20342                             
20343                             
20344                         ]
20345                     }
20346                 ]
20347                 
20348             }
20349         };
20350         var cal_rows = function() {
20351             
20352             var ret = [];
20353             for (var r = 0; r < 6; r++) {
20354                 var row= {
20355                     tag : 'tr',
20356                     cls : 'fc-week',
20357                     cn : []
20358                 };
20359                 
20360                 for (var i =0; i < Date.dayNames.length; i++) {
20361                     var d = Date.dayNames[i];
20362                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20363
20364                 }
20365                 row.cn[0].cls+=' fc-first';
20366                 row.cn[0].cn[0].style = 'min-height:90px';
20367                 row.cn[6].cls+=' fc-last';
20368                 ret.push(row);
20369                 
20370             }
20371             ret[0].cls += ' fc-first';
20372             ret[4].cls += ' fc-prev-last';
20373             ret[5].cls += ' fc-last';
20374             return ret;
20375             
20376         };
20377         
20378         var cal_table = {
20379             tag: 'table',
20380             cls: 'fc-border-separate',
20381             style : 'width:100%',
20382             cellspacing  : 0,
20383             cn : [
20384                 { 
20385                     tag: 'thead',
20386                     cn : [
20387                         { 
20388                             tag: 'tr',
20389                             cls : 'fc-first fc-last',
20390                             cn : cal_heads()
20391                         }
20392                     ]
20393                 },
20394                 { 
20395                     tag: 'tbody',
20396                     cn : cal_rows()
20397                 }
20398                   
20399             ]
20400         };
20401          
20402          var cfg = {
20403             cls : 'fc fc-ltr',
20404             cn : [
20405                 header,
20406                 {
20407                     cls : 'fc-content',
20408                     style : "position: relative;",
20409                     cn : [
20410                         {
20411                             cls : 'fc-view fc-view-month fc-grid',
20412                             style : 'position: relative',
20413                             unselectable : 'on',
20414                             cn : [
20415                                 {
20416                                     cls : 'fc-event-container',
20417                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20418                                 },
20419                                 cal_table
20420                             ]
20421                         }
20422                     ]
20423     
20424                 }
20425            ] 
20426             
20427         };
20428         
20429          
20430         
20431         return cfg;
20432     },
20433     
20434     
20435     initEvents : function()
20436     {
20437         if(!this.store){
20438             throw "can not find store for calendar";
20439         }
20440         
20441         var mark = {
20442             tag: "div",
20443             cls:"x-dlg-mask",
20444             style: "text-align:center",
20445             cn: [
20446                 {
20447                     tag: "div",
20448                     style: "background-color:white;width:50%;margin:250 auto",
20449                     cn: [
20450                         {
20451                             tag: "img",
20452                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20453                         },
20454                         {
20455                             tag: "span",
20456                             html: "Loading"
20457                         }
20458                         
20459                     ]
20460                 }
20461             ]
20462         };
20463         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20464         
20465         var size = this.el.select('.fc-content', true).first().getSize();
20466         this.maskEl.setSize(size.width, size.height);
20467         this.maskEl.enableDisplayMode("block");
20468         if(!this.loadMask){
20469             this.maskEl.hide();
20470         }
20471         
20472         this.store = Roo.factory(this.store, Roo.data);
20473         this.store.on('load', this.onLoad, this);
20474         this.store.on('beforeload', this.onBeforeLoad, this);
20475         
20476         this.resize();
20477         
20478         this.cells = this.el.select('.fc-day',true);
20479         //Roo.log(this.cells);
20480         this.textNodes = this.el.query('.fc-day-number');
20481         this.cells.addClassOnOver('fc-state-hover');
20482         
20483         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20484         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20485         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20486         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20487         
20488         this.on('monthchange', this.onMonthChange, this);
20489         
20490         this.update(new Date().clearTime());
20491     },
20492     
20493     resize : function() {
20494         var sz  = this.el.getSize();
20495         
20496         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20497         this.el.select('.fc-day-content div',true).setHeight(34);
20498     },
20499     
20500     
20501     // private
20502     showPrevMonth : function(e){
20503         this.update(this.activeDate.add("mo", -1));
20504     },
20505     showToday : function(e){
20506         this.update(new Date().clearTime());
20507     },
20508     // private
20509     showNextMonth : function(e){
20510         this.update(this.activeDate.add("mo", 1));
20511     },
20512
20513     // private
20514     showPrevYear : function(){
20515         this.update(this.activeDate.add("y", -1));
20516     },
20517
20518     // private
20519     showNextYear : function(){
20520         this.update(this.activeDate.add("y", 1));
20521     },
20522
20523     
20524    // private
20525     update : function(date)
20526     {
20527         var vd = this.activeDate;
20528         this.activeDate = date;
20529 //        if(vd && this.el){
20530 //            var t = date.getTime();
20531 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20532 //                Roo.log('using add remove');
20533 //                
20534 //                this.fireEvent('monthchange', this, date);
20535 //                
20536 //                this.cells.removeClass("fc-state-highlight");
20537 //                this.cells.each(function(c){
20538 //                   if(c.dateValue == t){
20539 //                       c.addClass("fc-state-highlight");
20540 //                       setTimeout(function(){
20541 //                            try{c.dom.firstChild.focus();}catch(e){}
20542 //                       }, 50);
20543 //                       return false;
20544 //                   }
20545 //                   return true;
20546 //                });
20547 //                return;
20548 //            }
20549 //        }
20550         
20551         var days = date.getDaysInMonth();
20552         
20553         var firstOfMonth = date.getFirstDateOfMonth();
20554         var startingPos = firstOfMonth.getDay()-this.startDay;
20555         
20556         if(startingPos < this.startDay){
20557             startingPos += 7;
20558         }
20559         
20560         var pm = date.add(Date.MONTH, -1);
20561         var prevStart = pm.getDaysInMonth()-startingPos;
20562 //        
20563         this.cells = this.el.select('.fc-day',true);
20564         this.textNodes = this.el.query('.fc-day-number');
20565         this.cells.addClassOnOver('fc-state-hover');
20566         
20567         var cells = this.cells.elements;
20568         var textEls = this.textNodes;
20569         
20570         Roo.each(cells, function(cell){
20571             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20572         });
20573         
20574         days += startingPos;
20575
20576         // convert everything to numbers so it's fast
20577         var day = 86400000;
20578         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20579         //Roo.log(d);
20580         //Roo.log(pm);
20581         //Roo.log(prevStart);
20582         
20583         var today = new Date().clearTime().getTime();
20584         var sel = date.clearTime().getTime();
20585         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20586         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20587         var ddMatch = this.disabledDatesRE;
20588         var ddText = this.disabledDatesText;
20589         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20590         var ddaysText = this.disabledDaysText;
20591         var format = this.format;
20592         
20593         var setCellClass = function(cal, cell){
20594             cell.row = 0;
20595             cell.events = [];
20596             cell.more = [];
20597             //Roo.log('set Cell Class');
20598             cell.title = "";
20599             var t = d.getTime();
20600             
20601             //Roo.log(d);
20602             
20603             cell.dateValue = t;
20604             if(t == today){
20605                 cell.className += " fc-today";
20606                 cell.className += " fc-state-highlight";
20607                 cell.title = cal.todayText;
20608             }
20609             if(t == sel){
20610                 // disable highlight in other month..
20611                 //cell.className += " fc-state-highlight";
20612                 
20613             }
20614             // disabling
20615             if(t < min) {
20616                 cell.className = " fc-state-disabled";
20617                 cell.title = cal.minText;
20618                 return;
20619             }
20620             if(t > max) {
20621                 cell.className = " fc-state-disabled";
20622                 cell.title = cal.maxText;
20623                 return;
20624             }
20625             if(ddays){
20626                 if(ddays.indexOf(d.getDay()) != -1){
20627                     cell.title = ddaysText;
20628                     cell.className = " fc-state-disabled";
20629                 }
20630             }
20631             if(ddMatch && format){
20632                 var fvalue = d.dateFormat(format);
20633                 if(ddMatch.test(fvalue)){
20634                     cell.title = ddText.replace("%0", fvalue);
20635                     cell.className = " fc-state-disabled";
20636                 }
20637             }
20638             
20639             if (!cell.initialClassName) {
20640                 cell.initialClassName = cell.dom.className;
20641             }
20642             
20643             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20644         };
20645
20646         var i = 0;
20647         
20648         for(; i < startingPos; i++) {
20649             textEls[i].innerHTML = (++prevStart);
20650             d.setDate(d.getDate()+1);
20651             
20652             cells[i].className = "fc-past fc-other-month";
20653             setCellClass(this, cells[i]);
20654         }
20655         
20656         var intDay = 0;
20657         
20658         for(; i < days; i++){
20659             intDay = i - startingPos + 1;
20660             textEls[i].innerHTML = (intDay);
20661             d.setDate(d.getDate()+1);
20662             
20663             cells[i].className = ''; // "x-date-active";
20664             setCellClass(this, cells[i]);
20665         }
20666         var extraDays = 0;
20667         
20668         for(; i < 42; i++) {
20669             textEls[i].innerHTML = (++extraDays);
20670             d.setDate(d.getDate()+1);
20671             
20672             cells[i].className = "fc-future fc-other-month";
20673             setCellClass(this, cells[i]);
20674         }
20675         
20676         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20677         
20678         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20679         
20680         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20681         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20682         
20683         if(totalRows != 6){
20684             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20685             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20686         }
20687         
20688         this.fireEvent('monthchange', this, date);
20689         
20690         
20691         /*
20692         if(!this.internalRender){
20693             var main = this.el.dom.firstChild;
20694             var w = main.offsetWidth;
20695             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20696             Roo.fly(main).setWidth(w);
20697             this.internalRender = true;
20698             // opera does not respect the auto grow header center column
20699             // then, after it gets a width opera refuses to recalculate
20700             // without a second pass
20701             if(Roo.isOpera && !this.secondPass){
20702                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20703                 this.secondPass = true;
20704                 this.update.defer(10, this, [date]);
20705             }
20706         }
20707         */
20708         
20709     },
20710     
20711     findCell : function(dt) {
20712         dt = dt.clearTime().getTime();
20713         var ret = false;
20714         this.cells.each(function(c){
20715             //Roo.log("check " +c.dateValue + '?=' + dt);
20716             if(c.dateValue == dt){
20717                 ret = c;
20718                 return false;
20719             }
20720             return true;
20721         });
20722         
20723         return ret;
20724     },
20725     
20726     findCells : function(ev) {
20727         var s = ev.start.clone().clearTime().getTime();
20728        // Roo.log(s);
20729         var e= ev.end.clone().clearTime().getTime();
20730        // Roo.log(e);
20731         var ret = [];
20732         this.cells.each(function(c){
20733              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20734             
20735             if(c.dateValue > e){
20736                 return ;
20737             }
20738             if(c.dateValue < s){
20739                 return ;
20740             }
20741             ret.push(c);
20742         });
20743         
20744         return ret;    
20745     },
20746     
20747 //    findBestRow: function(cells)
20748 //    {
20749 //        var ret = 0;
20750 //        
20751 //        for (var i =0 ; i < cells.length;i++) {
20752 //            ret  = Math.max(cells[i].rows || 0,ret);
20753 //        }
20754 //        return ret;
20755 //        
20756 //    },
20757     
20758     
20759     addItem : function(ev)
20760     {
20761         // look for vertical location slot in
20762         var cells = this.findCells(ev);
20763         
20764 //        ev.row = this.findBestRow(cells);
20765         
20766         // work out the location.
20767         
20768         var crow = false;
20769         var rows = [];
20770         for(var i =0; i < cells.length; i++) {
20771             
20772             cells[i].row = cells[0].row;
20773             
20774             if(i == 0){
20775                 cells[i].row = cells[i].row + 1;
20776             }
20777             
20778             if (!crow) {
20779                 crow = {
20780                     start : cells[i],
20781                     end :  cells[i]
20782                 };
20783                 continue;
20784             }
20785             if (crow.start.getY() == cells[i].getY()) {
20786                 // on same row.
20787                 crow.end = cells[i];
20788                 continue;
20789             }
20790             // different row.
20791             rows.push(crow);
20792             crow = {
20793                 start: cells[i],
20794                 end : cells[i]
20795             };
20796             
20797         }
20798         
20799         rows.push(crow);
20800         ev.els = [];
20801         ev.rows = rows;
20802         ev.cells = cells;
20803         
20804         cells[0].events.push(ev);
20805         
20806         this.calevents.push(ev);
20807     },
20808     
20809     clearEvents: function() {
20810         
20811         if(!this.calevents){
20812             return;
20813         }
20814         
20815         Roo.each(this.cells.elements, function(c){
20816             c.row = 0;
20817             c.events = [];
20818             c.more = [];
20819         });
20820         
20821         Roo.each(this.calevents, function(e) {
20822             Roo.each(e.els, function(el) {
20823                 el.un('mouseenter' ,this.onEventEnter, this);
20824                 el.un('mouseleave' ,this.onEventLeave, this);
20825                 el.remove();
20826             },this);
20827         },this);
20828         
20829         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20830             e.remove();
20831         });
20832         
20833     },
20834     
20835     renderEvents: function()
20836     {   
20837         var _this = this;
20838         
20839         this.cells.each(function(c) {
20840             
20841             if(c.row < 5){
20842                 return;
20843             }
20844             
20845             var ev = c.events;
20846             
20847             var r = 4;
20848             if(c.row != c.events.length){
20849                 r = 4 - (4 - (c.row - c.events.length));
20850             }
20851             
20852             c.events = ev.slice(0, r);
20853             c.more = ev.slice(r);
20854             
20855             if(c.more.length && c.more.length == 1){
20856                 c.events.push(c.more.pop());
20857             }
20858             
20859             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20860             
20861         });
20862             
20863         this.cells.each(function(c) {
20864             
20865             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20866             
20867             
20868             for (var e = 0; e < c.events.length; e++){
20869                 var ev = c.events[e];
20870                 var rows = ev.rows;
20871                 
20872                 for(var i = 0; i < rows.length; i++) {
20873                 
20874                     // how many rows should it span..
20875
20876                     var  cfg = {
20877                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20878                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20879
20880                         unselectable : "on",
20881                         cn : [
20882                             {
20883                                 cls: 'fc-event-inner',
20884                                 cn : [
20885     //                                {
20886     //                                  tag:'span',
20887     //                                  cls: 'fc-event-time',
20888     //                                  html : cells.length > 1 ? '' : ev.time
20889     //                                },
20890                                     {
20891                                       tag:'span',
20892                                       cls: 'fc-event-title',
20893                                       html : String.format('{0}', ev.title)
20894                                     }
20895
20896
20897                                 ]
20898                             },
20899                             {
20900                                 cls: 'ui-resizable-handle ui-resizable-e',
20901                                 html : '&nbsp;&nbsp;&nbsp'
20902                             }
20903
20904                         ]
20905                     };
20906
20907                     if (i == 0) {
20908                         cfg.cls += ' fc-event-start';
20909                     }
20910                     if ((i+1) == rows.length) {
20911                         cfg.cls += ' fc-event-end';
20912                     }
20913
20914                     var ctr = _this.el.select('.fc-event-container',true).first();
20915                     var cg = ctr.createChild(cfg);
20916
20917                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20918                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20919
20920                     var r = (c.more.length) ? 1 : 0;
20921                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20922                     cg.setWidth(ebox.right - sbox.x -2);
20923
20924                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20925                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20926                     cg.on('click', _this.onEventClick, _this, ev);
20927
20928                     ev.els.push(cg);
20929                     
20930                 }
20931                 
20932             }
20933             
20934             
20935             if(c.more.length){
20936                 var  cfg = {
20937                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20938                     style : 'position: absolute',
20939                     unselectable : "on",
20940                     cn : [
20941                         {
20942                             cls: 'fc-event-inner',
20943                             cn : [
20944                                 {
20945                                   tag:'span',
20946                                   cls: 'fc-event-title',
20947                                   html : 'More'
20948                                 }
20949
20950
20951                             ]
20952                         },
20953                         {
20954                             cls: 'ui-resizable-handle ui-resizable-e',
20955                             html : '&nbsp;&nbsp;&nbsp'
20956                         }
20957
20958                     ]
20959                 };
20960
20961                 var ctr = _this.el.select('.fc-event-container',true).first();
20962                 var cg = ctr.createChild(cfg);
20963
20964                 var sbox = c.select('.fc-day-content',true).first().getBox();
20965                 var ebox = c.select('.fc-day-content',true).first().getBox();
20966                 //Roo.log(cg);
20967                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20968                 cg.setWidth(ebox.right - sbox.x -2);
20969
20970                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20971                 
20972             }
20973             
20974         });
20975         
20976         
20977         
20978     },
20979     
20980     onEventEnter: function (e, el,event,d) {
20981         this.fireEvent('evententer', this, el, event);
20982     },
20983     
20984     onEventLeave: function (e, el,event,d) {
20985         this.fireEvent('eventleave', this, el, event);
20986     },
20987     
20988     onEventClick: function (e, el,event,d) {
20989         this.fireEvent('eventclick', this, el, event);
20990     },
20991     
20992     onMonthChange: function () {
20993         this.store.load();
20994     },
20995     
20996     onMoreEventClick: function(e, el, more)
20997     {
20998         var _this = this;
20999         
21000         this.calpopover.placement = 'right';
21001         this.calpopover.setTitle('More');
21002         
21003         this.calpopover.setContent('');
21004         
21005         var ctr = this.calpopover.el.select('.popover-content', true).first();
21006         
21007         Roo.each(more, function(m){
21008             var cfg = {
21009                 cls : 'fc-event-hori fc-event-draggable',
21010                 html : m.title
21011             };
21012             var cg = ctr.createChild(cfg);
21013             
21014             cg.on('click', _this.onEventClick, _this, m);
21015         });
21016         
21017         this.calpopover.show(el);
21018         
21019         
21020     },
21021     
21022     onLoad: function () 
21023     {   
21024         this.calevents = [];
21025         var cal = this;
21026         
21027         if(this.store.getCount() > 0){
21028             this.store.data.each(function(d){
21029                cal.addItem({
21030                     id : d.data.id,
21031                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21032                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21033                     time : d.data.start_time,
21034                     title : d.data.title,
21035                     description : d.data.description,
21036                     venue : d.data.venue
21037                 });
21038             });
21039         }
21040         
21041         this.renderEvents();
21042         
21043         if(this.calevents.length && this.loadMask){
21044             this.maskEl.hide();
21045         }
21046     },
21047     
21048     onBeforeLoad: function()
21049     {
21050         this.clearEvents();
21051         if(this.loadMask){
21052             this.maskEl.show();
21053         }
21054     }
21055 });
21056
21057  
21058  /*
21059  * - LGPL
21060  *
21061  * element
21062  * 
21063  */
21064
21065 /**
21066  * @class Roo.bootstrap.Popover
21067  * @extends Roo.bootstrap.Component
21068  * @builder-top
21069  * Bootstrap Popover class
21070  * @cfg {String} html contents of the popover   (or false to use children..)
21071  * @cfg {String} title of popover (or false to hide)
21072  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21073  * @cfg {String} trigger click || hover (or false to trigger manually)
21074  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21075  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21076  *      - if false and it has a 'parent' then it will be automatically added to that element
21077  *      - if string - Roo.get  will be called 
21078  * @cfg {Number} delay - delay before showing
21079  
21080  * @constructor
21081  * Create a new Popover
21082  * @param {Object} config The config object
21083  */
21084
21085 Roo.bootstrap.Popover = function(config){
21086     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21087     
21088     this.addEvents({
21089         // raw events
21090          /**
21091          * @event show
21092          * After the popover show
21093          * 
21094          * @param {Roo.bootstrap.Popover} this
21095          */
21096         "show" : true,
21097         /**
21098          * @event hide
21099          * After the popover hide
21100          * 
21101          * @param {Roo.bootstrap.Popover} this
21102          */
21103         "hide" : true
21104     });
21105 };
21106
21107 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21108     
21109     title: false,
21110     html: false,
21111     
21112     placement : 'right',
21113     trigger : 'hover', // hover
21114     modal : false,
21115     delay : 0,
21116     
21117     over: false,
21118     
21119     can_build_overlaid : false,
21120     
21121     maskEl : false, // the mask element
21122     headerEl : false,
21123     contentEl : false,
21124     alignEl : false, // when show is called with an element - this get's stored.
21125     
21126     getChildContainer : function()
21127     {
21128         return this.contentEl;
21129         
21130     },
21131     getPopoverHeader : function()
21132     {
21133         this.title = true; // flag not to hide it..
21134         this.headerEl.addClass('p-0');
21135         return this.headerEl
21136     },
21137     
21138     
21139     getAutoCreate : function(){
21140          
21141         var cfg = {
21142            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21143            style: 'display:block',
21144            cn : [
21145                 {
21146                     cls : 'arrow'
21147                 },
21148                 {
21149                     cls : 'popover-inner ',
21150                     cn : [
21151                         {
21152                             tag: 'h3',
21153                             cls: 'popover-title popover-header',
21154                             html : this.title === false ? '' : this.title
21155                         },
21156                         {
21157                             cls : 'popover-content popover-body '  + (this.cls || ''),
21158                             html : this.html || ''
21159                         }
21160                     ]
21161                     
21162                 }
21163            ]
21164         };
21165         
21166         return cfg;
21167     },
21168     /**
21169      * @param {string} the title
21170      */
21171     setTitle: function(str)
21172     {
21173         this.title = str;
21174         if (this.el) {
21175             this.headerEl.dom.innerHTML = str;
21176         }
21177         
21178     },
21179     /**
21180      * @param {string} the body content
21181      */
21182     setContent: function(str)
21183     {
21184         this.html = str;
21185         if (this.contentEl) {
21186             this.contentEl.dom.innerHTML = str;
21187         }
21188         
21189     },
21190     // as it get's added to the bottom of the page.
21191     onRender : function(ct, position)
21192     {
21193         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21194         
21195         
21196         
21197         if(!this.el){
21198             var cfg = Roo.apply({},  this.getAutoCreate());
21199             cfg.id = Roo.id();
21200             
21201             if (this.cls) {
21202                 cfg.cls += ' ' + this.cls;
21203             }
21204             if (this.style) {
21205                 cfg.style = this.style;
21206             }
21207             //Roo.log("adding to ");
21208             this.el = Roo.get(document.body).createChild(cfg, position);
21209 //            Roo.log(this.el);
21210         }
21211         
21212         this.contentEl = this.el.select('.popover-content',true).first();
21213         this.headerEl =  this.el.select('.popover-title',true).first();
21214         
21215         var nitems = [];
21216         if(typeof(this.items) != 'undefined'){
21217             var items = this.items;
21218             delete this.items;
21219
21220             for(var i =0;i < items.length;i++) {
21221                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21222             }
21223         }
21224
21225         this.items = nitems;
21226         
21227         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21228         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21229         
21230         
21231         
21232         this.initEvents();
21233     },
21234     
21235     resizeMask : function()
21236     {
21237         this.maskEl.setSize(
21238             Roo.lib.Dom.getViewWidth(true),
21239             Roo.lib.Dom.getViewHeight(true)
21240         );
21241     },
21242     
21243     initEvents : function()
21244     {
21245         
21246         if (!this.modal) { 
21247             Roo.bootstrap.Popover.register(this);
21248         }
21249          
21250         this.arrowEl = this.el.select('.arrow',true).first();
21251         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21252         this.el.enableDisplayMode('block');
21253         this.el.hide();
21254  
21255         
21256         if (this.over === false && !this.parent()) {
21257             return; 
21258         }
21259         if (this.triggers === false) {
21260             return;
21261         }
21262          
21263         // support parent
21264         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21265         var triggers = this.trigger ? this.trigger.split(' ') : [];
21266         Roo.each(triggers, function(trigger) {
21267         
21268             if (trigger == 'click') {
21269                 on_el.on('click', this.toggle, this);
21270             } else if (trigger != 'manual') {
21271                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21272                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21273       
21274                 on_el.on(eventIn  ,this.enter, this);
21275                 on_el.on(eventOut, this.leave, this);
21276             }
21277         }, this);
21278     },
21279     
21280     
21281     // private
21282     timeout : null,
21283     hoverState : null,
21284     
21285     toggle : function () {
21286         this.hoverState == 'in' ? this.leave() : this.enter();
21287     },
21288     
21289     enter : function () {
21290         
21291         clearTimeout(this.timeout);
21292     
21293         this.hoverState = 'in';
21294     
21295         if (!this.delay || !this.delay.show) {
21296             this.show();
21297             return;
21298         }
21299         var _t = this;
21300         this.timeout = setTimeout(function () {
21301             if (_t.hoverState == 'in') {
21302                 _t.show();
21303             }
21304         }, this.delay.show)
21305     },
21306     
21307     leave : function() {
21308         clearTimeout(this.timeout);
21309     
21310         this.hoverState = 'out';
21311     
21312         if (!this.delay || !this.delay.hide) {
21313             this.hide();
21314             return;
21315         }
21316         var _t = this;
21317         this.timeout = setTimeout(function () {
21318             if (_t.hoverState == 'out') {
21319                 _t.hide();
21320             }
21321         }, this.delay.hide)
21322     },
21323     /**
21324      * Show the popover
21325      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21326      * @param {string} (left|right|top|bottom) position
21327      */
21328     show : function (on_el, placement)
21329     {
21330         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21331         on_el = on_el || false; // default to false
21332          
21333         if (!on_el) {
21334             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21335                 on_el = this.parent().el;
21336             } else if (this.over) {
21337                 on_el = Roo.get(this.over);
21338             }
21339             
21340         }
21341         
21342         this.alignEl = Roo.get( on_el );
21343
21344         if (!this.el) {
21345             this.render(document.body);
21346         }
21347         
21348         
21349          
21350         
21351         if (this.title === false) {
21352             this.headerEl.hide();
21353         }
21354         
21355        
21356         this.el.show();
21357         this.el.dom.style.display = 'block';
21358          
21359  
21360         if (this.alignEl) {
21361             this.updatePosition(this.placement, true);
21362              
21363         } else {
21364             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21365             var es = this.el.getSize();
21366             var x = Roo.lib.Dom.getViewWidth()/2;
21367             var y = Roo.lib.Dom.getViewHeight()/2;
21368             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21369             
21370         }
21371
21372         
21373         //var arrow = this.el.select('.arrow',true).first();
21374         //arrow.set(align[2], 
21375         
21376         this.el.addClass('in');
21377         
21378          
21379         
21380         this.hoverState = 'in';
21381         
21382         if (this.modal) {
21383             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21384             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21385             this.maskEl.dom.style.display = 'block';
21386             this.maskEl.addClass('show');
21387         }
21388         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21389  
21390         this.fireEvent('show', this);
21391         
21392     },
21393     /**
21394      * fire this manually after loading a grid in the table for example
21395      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21396      * @param {Boolean} try and move it if we cant get right position.
21397      */
21398     updatePosition : function(placement, try_move)
21399     {
21400         // allow for calling with no parameters
21401         placement = placement   ? placement :  this.placement;
21402         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21403         
21404         this.el.removeClass([
21405             'fade','top','bottom', 'left', 'right','in',
21406             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21407         ]);
21408         this.el.addClass(placement + ' bs-popover-' + placement);
21409         
21410         if (!this.alignEl ) {
21411             return false;
21412         }
21413         
21414         switch (placement) {
21415             case 'right':
21416                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21417                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21418                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21419                     //normal display... or moved up/down.
21420                     this.el.setXY(offset);
21421                     var xy = this.alignEl.getAnchorXY('tr', false);
21422                     xy[0]+=2;xy[1]+=5;
21423                     this.arrowEl.setXY(xy);
21424                     return true;
21425                 }
21426                 // continue through...
21427                 return this.updatePosition('left', false);
21428                 
21429             
21430             case 'left':
21431                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21432                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21433                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21434                     //normal display... or moved up/down.
21435                     this.el.setXY(offset);
21436                     var xy = this.alignEl.getAnchorXY('tl', false);
21437                     xy[0]-=10;xy[1]+=5; // << fix me
21438                     this.arrowEl.setXY(xy);
21439                     return true;
21440                 }
21441                 // call self...
21442                 return this.updatePosition('right', false);
21443             
21444             case 'top':
21445                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21446                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21447                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21448                     //normal display... or moved up/down.
21449                     this.el.setXY(offset);
21450                     var xy = this.alignEl.getAnchorXY('t', false);
21451                     xy[1]-=10; // << fix me
21452                     this.arrowEl.setXY(xy);
21453                     return true;
21454                 }
21455                 // fall through
21456                return this.updatePosition('bottom', false);
21457             
21458             case 'bottom':
21459                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21460                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21461                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21462                     //normal display... or moved up/down.
21463                     this.el.setXY(offset);
21464                     var xy = this.alignEl.getAnchorXY('b', false);
21465                      xy[1]+=2; // << fix me
21466                     this.arrowEl.setXY(xy);
21467                     return true;
21468                 }
21469                 // fall through
21470                 return this.updatePosition('top', false);
21471                 
21472             
21473         }
21474         
21475         
21476         return false;
21477     },
21478     
21479     hide : function()
21480     {
21481         this.el.setXY([0,0]);
21482         this.el.removeClass('in');
21483         this.el.hide();
21484         this.hoverState = null;
21485         this.maskEl.hide(); // always..
21486         this.fireEvent('hide', this);
21487     }
21488     
21489 });
21490
21491
21492 Roo.apply(Roo.bootstrap.Popover, {
21493
21494     alignment : {
21495         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21496         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21497         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21498         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21499     },
21500     
21501     zIndex : 20001,
21502
21503     clickHander : false,
21504     
21505     
21506
21507     onMouseDown : function(e)
21508     {
21509         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21510             /// what is nothing is showing..
21511             this.hideAll();
21512         }
21513          
21514     },
21515     
21516     
21517     popups : [],
21518     
21519     register : function(popup)
21520     {
21521         if (!Roo.bootstrap.Popover.clickHandler) {
21522             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21523         }
21524         // hide other popups.
21525         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21526         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21527         this.hideAll(); //<< why?
21528         //this.popups.push(popup);
21529     },
21530     hideAll : function()
21531     {
21532         this.popups.forEach(function(p) {
21533             p.hide();
21534         });
21535     },
21536     onShow : function() {
21537         Roo.bootstrap.Popover.popups.push(this);
21538     },
21539     onHide : function() {
21540         Roo.bootstrap.Popover.popups.remove(this);
21541     } 
21542
21543 });/*
21544  * - LGPL
21545  *
21546  * Card header - holder for the card header elements.
21547  * 
21548  */
21549
21550 /**
21551  * @class Roo.bootstrap.PopoverNav
21552  * @extends Roo.bootstrap.NavGroup
21553  * Bootstrap Popover header navigation class
21554  * @constructor
21555  * Create a new Popover Header Navigation 
21556  * @param {Object} config The config object
21557  */
21558
21559 Roo.bootstrap.PopoverNav = function(config){
21560     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21561 };
21562
21563 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21564     
21565     
21566     container_method : 'getPopoverHeader' 
21567     
21568      
21569     
21570     
21571    
21572 });
21573
21574  
21575
21576  /*
21577  * - LGPL
21578  *
21579  * Progress
21580  * 
21581  */
21582
21583 /**
21584  * @class Roo.bootstrap.Progress
21585  * @extends Roo.bootstrap.Component
21586  * Bootstrap Progress class
21587  * @cfg {Boolean} striped striped of the progress bar
21588  * @cfg {Boolean} active animated of the progress bar
21589  * 
21590  * 
21591  * @constructor
21592  * Create a new Progress
21593  * @param {Object} config The config object
21594  */
21595
21596 Roo.bootstrap.Progress = function(config){
21597     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21598 };
21599
21600 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21601     
21602     striped : false,
21603     active: false,
21604     
21605     getAutoCreate : function(){
21606         var cfg = {
21607             tag: 'div',
21608             cls: 'progress'
21609         };
21610         
21611         
21612         if(this.striped){
21613             cfg.cls += ' progress-striped';
21614         }
21615       
21616         if(this.active){
21617             cfg.cls += ' active';
21618         }
21619         
21620         
21621         return cfg;
21622     }
21623    
21624 });
21625
21626  
21627
21628  /*
21629  * - LGPL
21630  *
21631  * ProgressBar
21632  * 
21633  */
21634
21635 /**
21636  * @class Roo.bootstrap.ProgressBar
21637  * @extends Roo.bootstrap.Component
21638  * Bootstrap ProgressBar class
21639  * @cfg {Number} aria_valuenow aria-value now
21640  * @cfg {Number} aria_valuemin aria-value min
21641  * @cfg {Number} aria_valuemax aria-value max
21642  * @cfg {String} label label for the progress bar
21643  * @cfg {String} panel (success | info | warning | danger )
21644  * @cfg {String} role role of the progress bar
21645  * @cfg {String} sr_only text
21646  * 
21647  * 
21648  * @constructor
21649  * Create a new ProgressBar
21650  * @param {Object} config The config object
21651  */
21652
21653 Roo.bootstrap.ProgressBar = function(config){
21654     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21655 };
21656
21657 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21658     
21659     aria_valuenow : 0,
21660     aria_valuemin : 0,
21661     aria_valuemax : 100,
21662     label : false,
21663     panel : false,
21664     role : false,
21665     sr_only: false,
21666     
21667     getAutoCreate : function()
21668     {
21669         
21670         var cfg = {
21671             tag: 'div',
21672             cls: 'progress-bar',
21673             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21674         };
21675         
21676         if(this.sr_only){
21677             cfg.cn = {
21678                 tag: 'span',
21679                 cls: 'sr-only',
21680                 html: this.sr_only
21681             }
21682         }
21683         
21684         if(this.role){
21685             cfg.role = this.role;
21686         }
21687         
21688         if(this.aria_valuenow){
21689             cfg['aria-valuenow'] = this.aria_valuenow;
21690         }
21691         
21692         if(this.aria_valuemin){
21693             cfg['aria-valuemin'] = this.aria_valuemin;
21694         }
21695         
21696         if(this.aria_valuemax){
21697             cfg['aria-valuemax'] = this.aria_valuemax;
21698         }
21699         
21700         if(this.label && !this.sr_only){
21701             cfg.html = this.label;
21702         }
21703         
21704         if(this.panel){
21705             cfg.cls += ' progress-bar-' + this.panel;
21706         }
21707         
21708         return cfg;
21709     },
21710     
21711     update : function(aria_valuenow)
21712     {
21713         this.aria_valuenow = aria_valuenow;
21714         
21715         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21716     }
21717    
21718 });
21719
21720  
21721
21722  /*
21723  * - LGPL
21724  *
21725  * column
21726  * 
21727  */
21728
21729 /**
21730  * @class Roo.bootstrap.TabGroup
21731  * @extends Roo.bootstrap.Column
21732  * Bootstrap Column class
21733  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21734  * @cfg {Boolean} carousel true to make the group behave like a carousel
21735  * @cfg {Boolean} bullets show bullets for the panels
21736  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21737  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21738  * @cfg {Boolean} showarrow (true|false) show arrow default true
21739  * 
21740  * @constructor
21741  * Create a new TabGroup
21742  * @param {Object} config The config object
21743  */
21744
21745 Roo.bootstrap.TabGroup = function(config){
21746     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21747     if (!this.navId) {
21748         this.navId = Roo.id();
21749     }
21750     this.tabs = [];
21751     Roo.bootstrap.TabGroup.register(this);
21752     
21753 };
21754
21755 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21756     
21757     carousel : false,
21758     transition : false,
21759     bullets : 0,
21760     timer : 0,
21761     autoslide : false,
21762     slideFn : false,
21763     slideOnTouch : false,
21764     showarrow : true,
21765     
21766     getAutoCreate : function()
21767     {
21768         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21769         
21770         cfg.cls += ' tab-content';
21771         
21772         if (this.carousel) {
21773             cfg.cls += ' carousel slide';
21774             
21775             cfg.cn = [{
21776                cls : 'carousel-inner',
21777                cn : []
21778             }];
21779         
21780             if(this.bullets  && !Roo.isTouch){
21781                 
21782                 var bullets = {
21783                     cls : 'carousel-bullets',
21784                     cn : []
21785                 };
21786                
21787                 if(this.bullets_cls){
21788                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21789                 }
21790                 
21791                 bullets.cn.push({
21792                     cls : 'clear'
21793                 });
21794                 
21795                 cfg.cn[0].cn.push(bullets);
21796             }
21797             
21798             if(this.showarrow){
21799                 cfg.cn[0].cn.push({
21800                     tag : 'div',
21801                     class : 'carousel-arrow',
21802                     cn : [
21803                         {
21804                             tag : 'div',
21805                             class : 'carousel-prev',
21806                             cn : [
21807                                 {
21808                                     tag : 'i',
21809                                     class : 'fa fa-chevron-left'
21810                                 }
21811                             ]
21812                         },
21813                         {
21814                             tag : 'div',
21815                             class : 'carousel-next',
21816                             cn : [
21817                                 {
21818                                     tag : 'i',
21819                                     class : 'fa fa-chevron-right'
21820                                 }
21821                             ]
21822                         }
21823                     ]
21824                 });
21825             }
21826             
21827         }
21828         
21829         return cfg;
21830     },
21831     
21832     initEvents:  function()
21833     {
21834 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21835 //            this.el.on("touchstart", this.onTouchStart, this);
21836 //        }
21837         
21838         if(this.autoslide){
21839             var _this = this;
21840             
21841             this.slideFn = window.setInterval(function() {
21842                 _this.showPanelNext();
21843             }, this.timer);
21844         }
21845         
21846         if(this.showarrow){
21847             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21848             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21849         }
21850         
21851         
21852     },
21853     
21854 //    onTouchStart : function(e, el, o)
21855 //    {
21856 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21857 //            return;
21858 //        }
21859 //        
21860 //        this.showPanelNext();
21861 //    },
21862     
21863     
21864     getChildContainer : function()
21865     {
21866         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21867     },
21868     
21869     /**
21870     * register a Navigation item
21871     * @param {Roo.bootstrap.NavItem} the navitem to add
21872     */
21873     register : function(item)
21874     {
21875         this.tabs.push( item);
21876         item.navId = this.navId; // not really needed..
21877         this.addBullet();
21878     
21879     },
21880     
21881     getActivePanel : function()
21882     {
21883         var r = false;
21884         Roo.each(this.tabs, function(t) {
21885             if (t.active) {
21886                 r = t;
21887                 return false;
21888             }
21889             return null;
21890         });
21891         return r;
21892         
21893     },
21894     getPanelByName : function(n)
21895     {
21896         var r = false;
21897         Roo.each(this.tabs, function(t) {
21898             if (t.tabId == n) {
21899                 r = t;
21900                 return false;
21901             }
21902             return null;
21903         });
21904         return r;
21905     },
21906     indexOfPanel : function(p)
21907     {
21908         var r = false;
21909         Roo.each(this.tabs, function(t,i) {
21910             if (t.tabId == p.tabId) {
21911                 r = i;
21912                 return false;
21913             }
21914             return null;
21915         });
21916         return r;
21917     },
21918     /**
21919      * show a specific panel
21920      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21921      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21922      */
21923     showPanel : function (pan)
21924     {
21925         if(this.transition || typeof(pan) == 'undefined'){
21926             Roo.log("waiting for the transitionend");
21927             return false;
21928         }
21929         
21930         if (typeof(pan) == 'number') {
21931             pan = this.tabs[pan];
21932         }
21933         
21934         if (typeof(pan) == 'string') {
21935             pan = this.getPanelByName(pan);
21936         }
21937         
21938         var cur = this.getActivePanel();
21939         
21940         if(!pan || !cur){
21941             Roo.log('pan or acitve pan is undefined');
21942             return false;
21943         }
21944         
21945         if (pan.tabId == this.getActivePanel().tabId) {
21946             return true;
21947         }
21948         
21949         if (false === cur.fireEvent('beforedeactivate')) {
21950             return false;
21951         }
21952         
21953         if(this.bullets > 0 && !Roo.isTouch){
21954             this.setActiveBullet(this.indexOfPanel(pan));
21955         }
21956         
21957         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21958             
21959             //class="carousel-item carousel-item-next carousel-item-left"
21960             
21961             this.transition = true;
21962             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21963             var lr = dir == 'next' ? 'left' : 'right';
21964             pan.el.addClass(dir); // or prev
21965             pan.el.addClass('carousel-item-' + dir); // or prev
21966             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21967             cur.el.addClass(lr); // or right
21968             pan.el.addClass(lr);
21969             cur.el.addClass('carousel-item-' +lr); // or right
21970             pan.el.addClass('carousel-item-' +lr);
21971             
21972             
21973             var _this = this;
21974             cur.el.on('transitionend', function() {
21975                 Roo.log("trans end?");
21976                 
21977                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21978                 pan.setActive(true);
21979                 
21980                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21981                 cur.setActive(false);
21982                 
21983                 _this.transition = false;
21984                 
21985             }, this, { single:  true } );
21986             
21987             return true;
21988         }
21989         
21990         cur.setActive(false);
21991         pan.setActive(true);
21992         
21993         return true;
21994         
21995     },
21996     showPanelNext : function()
21997     {
21998         var i = this.indexOfPanel(this.getActivePanel());
21999         
22000         if (i >= this.tabs.length - 1 && !this.autoslide) {
22001             return;
22002         }
22003         
22004         if (i >= this.tabs.length - 1 && this.autoslide) {
22005             i = -1;
22006         }
22007         
22008         this.showPanel(this.tabs[i+1]);
22009     },
22010     
22011     showPanelPrev : function()
22012     {
22013         var i = this.indexOfPanel(this.getActivePanel());
22014         
22015         if (i  < 1 && !this.autoslide) {
22016             return;
22017         }
22018         
22019         if (i < 1 && this.autoslide) {
22020             i = this.tabs.length;
22021         }
22022         
22023         this.showPanel(this.tabs[i-1]);
22024     },
22025     
22026     
22027     addBullet: function()
22028     {
22029         if(!this.bullets || Roo.isTouch){
22030             return;
22031         }
22032         var ctr = this.el.select('.carousel-bullets',true).first();
22033         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22034         var bullet = ctr.createChild({
22035             cls : 'bullet bullet-' + i
22036         },ctr.dom.lastChild);
22037         
22038         
22039         var _this = this;
22040         
22041         bullet.on('click', (function(e, el, o, ii, t){
22042
22043             e.preventDefault();
22044
22045             this.showPanel(ii);
22046
22047             if(this.autoslide && this.slideFn){
22048                 clearInterval(this.slideFn);
22049                 this.slideFn = window.setInterval(function() {
22050                     _this.showPanelNext();
22051                 }, this.timer);
22052             }
22053
22054         }).createDelegate(this, [i, bullet], true));
22055                 
22056         
22057     },
22058      
22059     setActiveBullet : function(i)
22060     {
22061         if(Roo.isTouch){
22062             return;
22063         }
22064         
22065         Roo.each(this.el.select('.bullet', true).elements, function(el){
22066             el.removeClass('selected');
22067         });
22068
22069         var bullet = this.el.select('.bullet-' + i, true).first();
22070         
22071         if(!bullet){
22072             return;
22073         }
22074         
22075         bullet.addClass('selected');
22076     }
22077     
22078     
22079   
22080 });
22081
22082  
22083
22084  
22085  
22086 Roo.apply(Roo.bootstrap.TabGroup, {
22087     
22088     groups: {},
22089      /**
22090     * register a Navigation Group
22091     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22092     */
22093     register : function(navgrp)
22094     {
22095         this.groups[navgrp.navId] = navgrp;
22096         
22097     },
22098     /**
22099     * fetch a Navigation Group based on the navigation ID
22100     * if one does not exist , it will get created.
22101     * @param {string} the navgroup to add
22102     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22103     */
22104     get: function(navId) {
22105         if (typeof(this.groups[navId]) == 'undefined') {
22106             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22107         }
22108         return this.groups[navId] ;
22109     }
22110     
22111     
22112     
22113 });
22114
22115  /*
22116  * - LGPL
22117  *
22118  * TabPanel
22119  * 
22120  */
22121
22122 /**
22123  * @class Roo.bootstrap.TabPanel
22124  * @extends Roo.bootstrap.Component
22125  * Bootstrap TabPanel class
22126  * @cfg {Boolean} active panel active
22127  * @cfg {String} html panel content
22128  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22129  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22130  * @cfg {String} href click to link..
22131  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22132  * 
22133  * 
22134  * @constructor
22135  * Create a new TabPanel
22136  * @param {Object} config The config object
22137  */
22138
22139 Roo.bootstrap.TabPanel = function(config){
22140     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22141     this.addEvents({
22142         /**
22143              * @event changed
22144              * Fires when the active status changes
22145              * @param {Roo.bootstrap.TabPanel} this
22146              * @param {Boolean} state the new state
22147             
22148          */
22149         'changed': true,
22150         /**
22151              * @event beforedeactivate
22152              * Fires before a tab is de-activated - can be used to do validation on a form.
22153              * @param {Roo.bootstrap.TabPanel} this
22154              * @return {Boolean} false if there is an error
22155             
22156          */
22157         'beforedeactivate': true
22158      });
22159     
22160     this.tabId = this.tabId || Roo.id();
22161   
22162 };
22163
22164 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22165     
22166     active: false,
22167     html: false,
22168     tabId: false,
22169     navId : false,
22170     href : '',
22171     touchSlide : false,
22172     getAutoCreate : function(){
22173         
22174         
22175         var cfg = {
22176             tag: 'div',
22177             // item is needed for carousel - not sure if it has any effect otherwise
22178             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22179             html: this.html || ''
22180         };
22181         
22182         if(this.active){
22183             cfg.cls += ' active';
22184         }
22185         
22186         if(this.tabId){
22187             cfg.tabId = this.tabId;
22188         }
22189         
22190         
22191         
22192         return cfg;
22193     },
22194     
22195     initEvents:  function()
22196     {
22197         var p = this.parent();
22198         
22199         this.navId = this.navId || p.navId;
22200         
22201         if (typeof(this.navId) != 'undefined') {
22202             // not really needed.. but just in case.. parent should be a NavGroup.
22203             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22204             
22205             tg.register(this);
22206             
22207             var i = tg.tabs.length - 1;
22208             
22209             if(this.active && tg.bullets > 0 && i < tg.bullets){
22210                 tg.setActiveBullet(i);
22211             }
22212         }
22213         
22214         this.el.on('click', this.onClick, this);
22215         
22216         if(Roo.isTouch && this.touchSlide){
22217             this.el.on("touchstart", this.onTouchStart, this);
22218             this.el.on("touchmove", this.onTouchMove, this);
22219             this.el.on("touchend", this.onTouchEnd, this);
22220         }
22221         
22222     },
22223     
22224     onRender : function(ct, position)
22225     {
22226         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22227     },
22228     
22229     setActive : function(state)
22230     {
22231         Roo.log("panel - set active " + this.tabId + "=" + state);
22232         
22233         this.active = state;
22234         if (!state) {
22235             this.el.removeClass('active');
22236             
22237         } else  if (!this.el.hasClass('active')) {
22238             this.el.addClass('active');
22239         }
22240         
22241         this.fireEvent('changed', this, state);
22242     },
22243     
22244     onClick : function(e)
22245     {
22246         e.preventDefault();
22247         
22248         if(!this.href.length){
22249             return;
22250         }
22251         
22252         window.location.href = this.href;
22253     },
22254     
22255     startX : 0,
22256     startY : 0,
22257     endX : 0,
22258     endY : 0,
22259     swiping : false,
22260     
22261     onTouchStart : function(e)
22262     {
22263         this.swiping = false;
22264         
22265         this.startX = e.browserEvent.touches[0].clientX;
22266         this.startY = e.browserEvent.touches[0].clientY;
22267     },
22268     
22269     onTouchMove : function(e)
22270     {
22271         this.swiping = true;
22272         
22273         this.endX = e.browserEvent.touches[0].clientX;
22274         this.endY = e.browserEvent.touches[0].clientY;
22275     },
22276     
22277     onTouchEnd : function(e)
22278     {
22279         if(!this.swiping){
22280             this.onClick(e);
22281             return;
22282         }
22283         
22284         var tabGroup = this.parent();
22285         
22286         if(this.endX > this.startX){ // swiping right
22287             tabGroup.showPanelPrev();
22288             return;
22289         }
22290         
22291         if(this.startX > this.endX){ // swiping left
22292             tabGroup.showPanelNext();
22293             return;
22294         }
22295     }
22296     
22297     
22298 });
22299  
22300
22301  
22302
22303  /*
22304  * - LGPL
22305  *
22306  * DateField
22307  * 
22308  */
22309
22310 /**
22311  * @class Roo.bootstrap.DateField
22312  * @extends Roo.bootstrap.Input
22313  * Bootstrap DateField class
22314  * @cfg {Number} weekStart default 0
22315  * @cfg {String} viewMode default empty, (months|years)
22316  * @cfg {String} minViewMode default empty, (months|years)
22317  * @cfg {Number} startDate default -Infinity
22318  * @cfg {Number} endDate default Infinity
22319  * @cfg {Boolean} todayHighlight default false
22320  * @cfg {Boolean} todayBtn default false
22321  * @cfg {Boolean} calendarWeeks default false
22322  * @cfg {Object} daysOfWeekDisabled default empty
22323  * @cfg {Boolean} singleMode default false (true | false)
22324  * 
22325  * @cfg {Boolean} keyboardNavigation default true
22326  * @cfg {String} language default en
22327  * 
22328  * @constructor
22329  * Create a new DateField
22330  * @param {Object} config The config object
22331  */
22332
22333 Roo.bootstrap.DateField = function(config){
22334     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22335      this.addEvents({
22336             /**
22337              * @event show
22338              * Fires when this field show.
22339              * @param {Roo.bootstrap.DateField} this
22340              * @param {Mixed} date The date value
22341              */
22342             show : true,
22343             /**
22344              * @event show
22345              * Fires when this field hide.
22346              * @param {Roo.bootstrap.DateField} this
22347              * @param {Mixed} date The date value
22348              */
22349             hide : true,
22350             /**
22351              * @event select
22352              * Fires when select a date.
22353              * @param {Roo.bootstrap.DateField} this
22354              * @param {Mixed} date The date value
22355              */
22356             select : true,
22357             /**
22358              * @event beforeselect
22359              * Fires when before select a date.
22360              * @param {Roo.bootstrap.DateField} this
22361              * @param {Mixed} date The date value
22362              */
22363             beforeselect : true
22364         });
22365 };
22366
22367 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22368     
22369     /**
22370      * @cfg {String} format
22371      * The default date format string which can be overriden for localization support.  The format must be
22372      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22373      */
22374     format : "m/d/y",
22375     /**
22376      * @cfg {String} altFormats
22377      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22378      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22379      */
22380     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22381     
22382     weekStart : 0,
22383     
22384     viewMode : '',
22385     
22386     minViewMode : '',
22387     
22388     todayHighlight : false,
22389     
22390     todayBtn: false,
22391     
22392     language: 'en',
22393     
22394     keyboardNavigation: true,
22395     
22396     calendarWeeks: false,
22397     
22398     startDate: -Infinity,
22399     
22400     endDate: Infinity,
22401     
22402     daysOfWeekDisabled: [],
22403     
22404     _events: [],
22405     
22406     singleMode : false,
22407     
22408     UTCDate: function()
22409     {
22410         return new Date(Date.UTC.apply(Date, arguments));
22411     },
22412     
22413     UTCToday: function()
22414     {
22415         var today = new Date();
22416         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22417     },
22418     
22419     getDate: function() {
22420             var d = this.getUTCDate();
22421             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22422     },
22423     
22424     getUTCDate: function() {
22425             return this.date;
22426     },
22427     
22428     setDate: function(d) {
22429             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22430     },
22431     
22432     setUTCDate: function(d) {
22433             this.date = d;
22434             this.setValue(this.formatDate(this.date));
22435     },
22436         
22437     onRender: function(ct, position)
22438     {
22439         
22440         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22441         
22442         this.language = this.language || 'en';
22443         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22444         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22445         
22446         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22447         this.format = this.format || 'm/d/y';
22448         this.isInline = false;
22449         this.isInput = true;
22450         this.component = this.el.select('.add-on', true).first() || false;
22451         this.component = (this.component && this.component.length === 0) ? false : this.component;
22452         this.hasInput = this.component && this.inputEl().length;
22453         
22454         if (typeof(this.minViewMode === 'string')) {
22455             switch (this.minViewMode) {
22456                 case 'months':
22457                     this.minViewMode = 1;
22458                     break;
22459                 case 'years':
22460                     this.minViewMode = 2;
22461                     break;
22462                 default:
22463                     this.minViewMode = 0;
22464                     break;
22465             }
22466         }
22467         
22468         if (typeof(this.viewMode === 'string')) {
22469             switch (this.viewMode) {
22470                 case 'months':
22471                     this.viewMode = 1;
22472                     break;
22473                 case 'years':
22474                     this.viewMode = 2;
22475                     break;
22476                 default:
22477                     this.viewMode = 0;
22478                     break;
22479             }
22480         }
22481                 
22482         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22483         
22484 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22485         
22486         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22487         
22488         this.picker().on('mousedown', this.onMousedown, this);
22489         this.picker().on('click', this.onClick, this);
22490         
22491         this.picker().addClass('datepicker-dropdown');
22492         
22493         this.startViewMode = this.viewMode;
22494         
22495         if(this.singleMode){
22496             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22497                 v.setVisibilityMode(Roo.Element.DISPLAY);
22498                 v.hide();
22499             });
22500             
22501             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22502                 v.setStyle('width', '189px');
22503             });
22504         }
22505         
22506         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22507             if(!this.calendarWeeks){
22508                 v.remove();
22509                 return;
22510             }
22511             
22512             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22513             v.attr('colspan', function(i, val){
22514                 return parseInt(val) + 1;
22515             });
22516         });
22517                         
22518         
22519         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22520         
22521         this.setStartDate(this.startDate);
22522         this.setEndDate(this.endDate);
22523         
22524         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22525         
22526         this.fillDow();
22527         this.fillMonths();
22528         this.update();
22529         this.showMode();
22530         
22531         if(this.isInline) {
22532             this.showPopup();
22533         }
22534     },
22535     
22536     picker : function()
22537     {
22538         return this.pickerEl;
22539 //        return this.el.select('.datepicker', true).first();
22540     },
22541     
22542     fillDow: function()
22543     {
22544         var dowCnt = this.weekStart;
22545         
22546         var dow = {
22547             tag: 'tr',
22548             cn: [
22549                 
22550             ]
22551         };
22552         
22553         if(this.calendarWeeks){
22554             dow.cn.push({
22555                 tag: 'th',
22556                 cls: 'cw',
22557                 html: '&nbsp;'
22558             })
22559         }
22560         
22561         while (dowCnt < this.weekStart + 7) {
22562             dow.cn.push({
22563                 tag: 'th',
22564                 cls: 'dow',
22565                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22566             });
22567         }
22568         
22569         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22570     },
22571     
22572     fillMonths: function()
22573     {    
22574         var i = 0;
22575         var months = this.picker().select('>.datepicker-months td', true).first();
22576         
22577         months.dom.innerHTML = '';
22578         
22579         while (i < 12) {
22580             var month = {
22581                 tag: 'span',
22582                 cls: 'month',
22583                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22584             };
22585             
22586             months.createChild(month);
22587         }
22588         
22589     },
22590     
22591     update: function()
22592     {
22593         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;
22594         
22595         if (this.date < this.startDate) {
22596             this.viewDate = new Date(this.startDate);
22597         } else if (this.date > this.endDate) {
22598             this.viewDate = new Date(this.endDate);
22599         } else {
22600             this.viewDate = new Date(this.date);
22601         }
22602         
22603         this.fill();
22604     },
22605     
22606     fill: function() 
22607     {
22608         var d = new Date(this.viewDate),
22609                 year = d.getUTCFullYear(),
22610                 month = d.getUTCMonth(),
22611                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22612                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22613                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22614                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22615                 currentDate = this.date && this.date.valueOf(),
22616                 today = this.UTCToday();
22617         
22618         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22619         
22620 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22621         
22622 //        this.picker.select('>tfoot th.today').
22623 //                                              .text(dates[this.language].today)
22624 //                                              .toggle(this.todayBtn !== false);
22625     
22626         this.updateNavArrows();
22627         this.fillMonths();
22628                                                 
22629         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22630         
22631         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22632          
22633         prevMonth.setUTCDate(day);
22634         
22635         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22636         
22637         var nextMonth = new Date(prevMonth);
22638         
22639         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22640         
22641         nextMonth = nextMonth.valueOf();
22642         
22643         var fillMonths = false;
22644         
22645         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22646         
22647         while(prevMonth.valueOf() <= nextMonth) {
22648             var clsName = '';
22649             
22650             if (prevMonth.getUTCDay() === this.weekStart) {
22651                 if(fillMonths){
22652                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22653                 }
22654                     
22655                 fillMonths = {
22656                     tag: 'tr',
22657                     cn: []
22658                 };
22659                 
22660                 if(this.calendarWeeks){
22661                     // ISO 8601: First week contains first thursday.
22662                     // ISO also states week starts on Monday, but we can be more abstract here.
22663                     var
22664                     // Start of current week: based on weekstart/current date
22665                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22666                     // Thursday of this week
22667                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22668                     // First Thursday of year, year from thursday
22669                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22670                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22671                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22672                     
22673                     fillMonths.cn.push({
22674                         tag: 'td',
22675                         cls: 'cw',
22676                         html: calWeek
22677                     });
22678                 }
22679             }
22680             
22681             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22682                 clsName += ' old';
22683             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22684                 clsName += ' new';
22685             }
22686             if (this.todayHighlight &&
22687                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22688                 prevMonth.getUTCMonth() == today.getMonth() &&
22689                 prevMonth.getUTCDate() == today.getDate()) {
22690                 clsName += ' today';
22691             }
22692             
22693             if (currentDate && prevMonth.valueOf() === currentDate) {
22694                 clsName += ' active';
22695             }
22696             
22697             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22698                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22699                     clsName += ' disabled';
22700             }
22701             
22702             fillMonths.cn.push({
22703                 tag: 'td',
22704                 cls: 'day ' + clsName,
22705                 html: prevMonth.getDate()
22706             });
22707             
22708             prevMonth.setDate(prevMonth.getDate()+1);
22709         }
22710           
22711         var currentYear = this.date && this.date.getUTCFullYear();
22712         var currentMonth = this.date && this.date.getUTCMonth();
22713         
22714         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22715         
22716         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22717             v.removeClass('active');
22718             
22719             if(currentYear === year && k === currentMonth){
22720                 v.addClass('active');
22721             }
22722             
22723             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22724                 v.addClass('disabled');
22725             }
22726             
22727         });
22728         
22729         
22730         year = parseInt(year/10, 10) * 10;
22731         
22732         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22733         
22734         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22735         
22736         year -= 1;
22737         for (var i = -1; i < 11; i++) {
22738             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22739                 tag: 'span',
22740                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22741                 html: year
22742             });
22743             
22744             year += 1;
22745         }
22746     },
22747     
22748     showMode: function(dir) 
22749     {
22750         if (dir) {
22751             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22752         }
22753         
22754         Roo.each(this.picker().select('>div',true).elements, function(v){
22755             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22756             v.hide();
22757         });
22758         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22759     },
22760     
22761     place: function()
22762     {
22763         if(this.isInline) {
22764             return;
22765         }
22766         
22767         this.picker().removeClass(['bottom', 'top']);
22768         
22769         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22770             /*
22771              * place to the top of element!
22772              *
22773              */
22774             
22775             this.picker().addClass('top');
22776             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22777             
22778             return;
22779         }
22780         
22781         this.picker().addClass('bottom');
22782         
22783         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22784     },
22785     
22786     parseDate : function(value)
22787     {
22788         if(!value || value instanceof Date){
22789             return value;
22790         }
22791         var v = Date.parseDate(value, this.format);
22792         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22793             v = Date.parseDate(value, 'Y-m-d');
22794         }
22795         if(!v && this.altFormats){
22796             if(!this.altFormatsArray){
22797                 this.altFormatsArray = this.altFormats.split("|");
22798             }
22799             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22800                 v = Date.parseDate(value, this.altFormatsArray[i]);
22801             }
22802         }
22803         return v;
22804     },
22805     
22806     formatDate : function(date, fmt)
22807     {   
22808         return (!date || !(date instanceof Date)) ?
22809         date : date.dateFormat(fmt || this.format);
22810     },
22811     
22812     onFocus : function()
22813     {
22814         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22815         this.showPopup();
22816     },
22817     
22818     onBlur : function()
22819     {
22820         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22821         
22822         var d = this.inputEl().getValue();
22823         
22824         this.setValue(d);
22825                 
22826         this.hidePopup();
22827     },
22828     
22829     showPopup : function()
22830     {
22831         this.picker().show();
22832         this.update();
22833         this.place();
22834         
22835         this.fireEvent('showpopup', this, this.date);
22836     },
22837     
22838     hidePopup : function()
22839     {
22840         if(this.isInline) {
22841             return;
22842         }
22843         this.picker().hide();
22844         this.viewMode = this.startViewMode;
22845         this.showMode();
22846         
22847         this.fireEvent('hidepopup', this, this.date);
22848         
22849     },
22850     
22851     onMousedown: function(e)
22852     {
22853         e.stopPropagation();
22854         e.preventDefault();
22855     },
22856     
22857     keyup: function(e)
22858     {
22859         Roo.bootstrap.DateField.superclass.keyup.call(this);
22860         this.update();
22861     },
22862
22863     setValue: function(v)
22864     {
22865         if(this.fireEvent('beforeselect', this, v) !== false){
22866             var d = new Date(this.parseDate(v) ).clearTime();
22867         
22868             if(isNaN(d.getTime())){
22869                 this.date = this.viewDate = '';
22870                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22871                 return;
22872             }
22873
22874             v = this.formatDate(d);
22875
22876             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22877
22878             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22879
22880             this.update();
22881
22882             this.fireEvent('select', this, this.date);
22883         }
22884     },
22885     
22886     getValue: function()
22887     {
22888         return this.formatDate(this.date);
22889     },
22890     
22891     fireKey: function(e)
22892     {
22893         if (!this.picker().isVisible()){
22894             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22895                 this.showPopup();
22896             }
22897             return;
22898         }
22899         
22900         var dateChanged = false,
22901         dir, day, month,
22902         newDate, newViewDate;
22903         
22904         switch(e.keyCode){
22905             case 27: // escape
22906                 this.hidePopup();
22907                 e.preventDefault();
22908                 break;
22909             case 37: // left
22910             case 39: // right
22911                 if (!this.keyboardNavigation) {
22912                     break;
22913                 }
22914                 dir = e.keyCode == 37 ? -1 : 1;
22915                 
22916                 if (e.ctrlKey){
22917                     newDate = this.moveYear(this.date, dir);
22918                     newViewDate = this.moveYear(this.viewDate, dir);
22919                 } else if (e.shiftKey){
22920                     newDate = this.moveMonth(this.date, dir);
22921                     newViewDate = this.moveMonth(this.viewDate, dir);
22922                 } else {
22923                     newDate = new Date(this.date);
22924                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22925                     newViewDate = new Date(this.viewDate);
22926                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22927                 }
22928                 if (this.dateWithinRange(newDate)){
22929                     this.date = newDate;
22930                     this.viewDate = newViewDate;
22931                     this.setValue(this.formatDate(this.date));
22932 //                    this.update();
22933                     e.preventDefault();
22934                     dateChanged = true;
22935                 }
22936                 break;
22937             case 38: // up
22938             case 40: // down
22939                 if (!this.keyboardNavigation) {
22940                     break;
22941                 }
22942                 dir = e.keyCode == 38 ? -1 : 1;
22943                 if (e.ctrlKey){
22944                     newDate = this.moveYear(this.date, dir);
22945                     newViewDate = this.moveYear(this.viewDate, dir);
22946                 } else if (e.shiftKey){
22947                     newDate = this.moveMonth(this.date, dir);
22948                     newViewDate = this.moveMonth(this.viewDate, dir);
22949                 } else {
22950                     newDate = new Date(this.date);
22951                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22952                     newViewDate = new Date(this.viewDate);
22953                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22954                 }
22955                 if (this.dateWithinRange(newDate)){
22956                     this.date = newDate;
22957                     this.viewDate = newViewDate;
22958                     this.setValue(this.formatDate(this.date));
22959 //                    this.update();
22960                     e.preventDefault();
22961                     dateChanged = true;
22962                 }
22963                 break;
22964             case 13: // enter
22965                 this.setValue(this.formatDate(this.date));
22966                 this.hidePopup();
22967                 e.preventDefault();
22968                 break;
22969             case 9: // tab
22970                 this.setValue(this.formatDate(this.date));
22971                 this.hidePopup();
22972                 break;
22973             case 16: // shift
22974             case 17: // ctrl
22975             case 18: // alt
22976                 break;
22977             default :
22978                 this.hidePopup();
22979                 
22980         }
22981     },
22982     
22983     
22984     onClick: function(e) 
22985     {
22986         e.stopPropagation();
22987         e.preventDefault();
22988         
22989         var target = e.getTarget();
22990         
22991         if(target.nodeName.toLowerCase() === 'i'){
22992             target = Roo.get(target).dom.parentNode;
22993         }
22994         
22995         var nodeName = target.nodeName;
22996         var className = target.className;
22997         var html = target.innerHTML;
22998         //Roo.log(nodeName);
22999         
23000         switch(nodeName.toLowerCase()) {
23001             case 'th':
23002                 switch(className) {
23003                     case 'switch':
23004                         this.showMode(1);
23005                         break;
23006                     case 'prev':
23007                     case 'next':
23008                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23009                         switch(this.viewMode){
23010                                 case 0:
23011                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23012                                         break;
23013                                 case 1:
23014                                 case 2:
23015                                         this.viewDate = this.moveYear(this.viewDate, dir);
23016                                         break;
23017                         }
23018                         this.fill();
23019                         break;
23020                     case 'today':
23021                         var date = new Date();
23022                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23023 //                        this.fill()
23024                         this.setValue(this.formatDate(this.date));
23025                         
23026                         this.hidePopup();
23027                         break;
23028                 }
23029                 break;
23030             case 'span':
23031                 if (className.indexOf('disabled') < 0) {
23032                 if (!this.viewDate) {
23033                     this.viewDate = new Date();
23034                 }
23035                 this.viewDate.setUTCDate(1);
23036                     if (className.indexOf('month') > -1) {
23037                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23038                     } else {
23039                         var year = parseInt(html, 10) || 0;
23040                         this.viewDate.setUTCFullYear(year);
23041                         
23042                     }
23043                     
23044                     if(this.singleMode){
23045                         this.setValue(this.formatDate(this.viewDate));
23046                         this.hidePopup();
23047                         return;
23048                     }
23049                     
23050                     this.showMode(-1);
23051                     this.fill();
23052                 }
23053                 break;
23054                 
23055             case 'td':
23056                 //Roo.log(className);
23057                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23058                     var day = parseInt(html, 10) || 1;
23059                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23060                         month = (this.viewDate || new Date()).getUTCMonth();
23061
23062                     if (className.indexOf('old') > -1) {
23063                         if(month === 0 ){
23064                             month = 11;
23065                             year -= 1;
23066                         }else{
23067                             month -= 1;
23068                         }
23069                     } else if (className.indexOf('new') > -1) {
23070                         if (month == 11) {
23071                             month = 0;
23072                             year += 1;
23073                         } else {
23074                             month += 1;
23075                         }
23076                     }
23077                     //Roo.log([year,month,day]);
23078                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23079                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23080 //                    this.fill();
23081                     //Roo.log(this.formatDate(this.date));
23082                     this.setValue(this.formatDate(this.date));
23083                     this.hidePopup();
23084                 }
23085                 break;
23086         }
23087     },
23088     
23089     setStartDate: function(startDate)
23090     {
23091         this.startDate = startDate || -Infinity;
23092         if (this.startDate !== -Infinity) {
23093             this.startDate = this.parseDate(this.startDate);
23094         }
23095         this.update();
23096         this.updateNavArrows();
23097     },
23098
23099     setEndDate: function(endDate)
23100     {
23101         this.endDate = endDate || Infinity;
23102         if (this.endDate !== Infinity) {
23103             this.endDate = this.parseDate(this.endDate);
23104         }
23105         this.update();
23106         this.updateNavArrows();
23107     },
23108     
23109     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23110     {
23111         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23112         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23113             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23114         }
23115         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23116             return parseInt(d, 10);
23117         });
23118         this.update();
23119         this.updateNavArrows();
23120     },
23121     
23122     updateNavArrows: function() 
23123     {
23124         if(this.singleMode){
23125             return;
23126         }
23127         
23128         var d = new Date(this.viewDate),
23129         year = d.getUTCFullYear(),
23130         month = d.getUTCMonth();
23131         
23132         Roo.each(this.picker().select('.prev', true).elements, function(v){
23133             v.show();
23134             switch (this.viewMode) {
23135                 case 0:
23136
23137                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23138                         v.hide();
23139                     }
23140                     break;
23141                 case 1:
23142                 case 2:
23143                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23144                         v.hide();
23145                     }
23146                     break;
23147             }
23148         });
23149         
23150         Roo.each(this.picker().select('.next', true).elements, function(v){
23151             v.show();
23152             switch (this.viewMode) {
23153                 case 0:
23154
23155                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23156                         v.hide();
23157                     }
23158                     break;
23159                 case 1:
23160                 case 2:
23161                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23162                         v.hide();
23163                     }
23164                     break;
23165             }
23166         })
23167     },
23168     
23169     moveMonth: function(date, dir)
23170     {
23171         if (!dir) {
23172             return date;
23173         }
23174         var new_date = new Date(date.valueOf()),
23175         day = new_date.getUTCDate(),
23176         month = new_date.getUTCMonth(),
23177         mag = Math.abs(dir),
23178         new_month, test;
23179         dir = dir > 0 ? 1 : -1;
23180         if (mag == 1){
23181             test = dir == -1
23182             // If going back one month, make sure month is not current month
23183             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23184             ? function(){
23185                 return new_date.getUTCMonth() == month;
23186             }
23187             // If going forward one month, make sure month is as expected
23188             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23189             : function(){
23190                 return new_date.getUTCMonth() != new_month;
23191             };
23192             new_month = month + dir;
23193             new_date.setUTCMonth(new_month);
23194             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23195             if (new_month < 0 || new_month > 11) {
23196                 new_month = (new_month + 12) % 12;
23197             }
23198         } else {
23199             // For magnitudes >1, move one month at a time...
23200             for (var i=0; i<mag; i++) {
23201                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23202                 new_date = this.moveMonth(new_date, dir);
23203             }
23204             // ...then reset the day, keeping it in the new month
23205             new_month = new_date.getUTCMonth();
23206             new_date.setUTCDate(day);
23207             test = function(){
23208                 return new_month != new_date.getUTCMonth();
23209             };
23210         }
23211         // Common date-resetting loop -- if date is beyond end of month, make it
23212         // end of month
23213         while (test()){
23214             new_date.setUTCDate(--day);
23215             new_date.setUTCMonth(new_month);
23216         }
23217         return new_date;
23218     },
23219
23220     moveYear: function(date, dir)
23221     {
23222         return this.moveMonth(date, dir*12);
23223     },
23224
23225     dateWithinRange: function(date)
23226     {
23227         return date >= this.startDate && date <= this.endDate;
23228     },
23229
23230     
23231     remove: function() 
23232     {
23233         this.picker().remove();
23234     },
23235     
23236     validateValue : function(value)
23237     {
23238         if(this.getVisibilityEl().hasClass('hidden')){
23239             return true;
23240         }
23241         
23242         if(value.length < 1)  {
23243             if(this.allowBlank){
23244                 return true;
23245             }
23246             return false;
23247         }
23248         
23249         if(value.length < this.minLength){
23250             return false;
23251         }
23252         if(value.length > this.maxLength){
23253             return false;
23254         }
23255         if(this.vtype){
23256             var vt = Roo.form.VTypes;
23257             if(!vt[this.vtype](value, this)){
23258                 return false;
23259             }
23260         }
23261         if(typeof this.validator == "function"){
23262             var msg = this.validator(value);
23263             if(msg !== true){
23264                 return false;
23265             }
23266         }
23267         
23268         if(this.regex && !this.regex.test(value)){
23269             return false;
23270         }
23271         
23272         if(typeof(this.parseDate(value)) == 'undefined'){
23273             return false;
23274         }
23275         
23276         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23277             return false;
23278         }      
23279         
23280         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23281             return false;
23282         } 
23283         
23284         
23285         return true;
23286     },
23287     
23288     reset : function()
23289     {
23290         this.date = this.viewDate = '';
23291         
23292         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23293     }
23294    
23295 });
23296
23297 Roo.apply(Roo.bootstrap.DateField,  {
23298     
23299     head : {
23300         tag: 'thead',
23301         cn: [
23302         {
23303             tag: 'tr',
23304             cn: [
23305             {
23306                 tag: 'th',
23307                 cls: 'prev',
23308                 html: '<i class="fa fa-arrow-left"/>'
23309             },
23310             {
23311                 tag: 'th',
23312                 cls: 'switch',
23313                 colspan: '5'
23314             },
23315             {
23316                 tag: 'th',
23317                 cls: 'next',
23318                 html: '<i class="fa fa-arrow-right"/>'
23319             }
23320
23321             ]
23322         }
23323         ]
23324     },
23325     
23326     content : {
23327         tag: 'tbody',
23328         cn: [
23329         {
23330             tag: 'tr',
23331             cn: [
23332             {
23333                 tag: 'td',
23334                 colspan: '7'
23335             }
23336             ]
23337         }
23338         ]
23339     },
23340     
23341     footer : {
23342         tag: 'tfoot',
23343         cn: [
23344         {
23345             tag: 'tr',
23346             cn: [
23347             {
23348                 tag: 'th',
23349                 colspan: '7',
23350                 cls: 'today'
23351             }
23352                     
23353             ]
23354         }
23355         ]
23356     },
23357     
23358     dates:{
23359         en: {
23360             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23361             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23362             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23363             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23364             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23365             today: "Today"
23366         }
23367     },
23368     
23369     modes: [
23370     {
23371         clsName: 'days',
23372         navFnc: 'Month',
23373         navStep: 1
23374     },
23375     {
23376         clsName: 'months',
23377         navFnc: 'FullYear',
23378         navStep: 1
23379     },
23380     {
23381         clsName: 'years',
23382         navFnc: 'FullYear',
23383         navStep: 10
23384     }]
23385 });
23386
23387 Roo.apply(Roo.bootstrap.DateField,  {
23388   
23389     template : {
23390         tag: 'div',
23391         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23392         cn: [
23393         {
23394             tag: 'div',
23395             cls: 'datepicker-days',
23396             cn: [
23397             {
23398                 tag: 'table',
23399                 cls: 'table-condensed',
23400                 cn:[
23401                 Roo.bootstrap.DateField.head,
23402                 {
23403                     tag: 'tbody'
23404                 },
23405                 Roo.bootstrap.DateField.footer
23406                 ]
23407             }
23408             ]
23409         },
23410         {
23411             tag: 'div',
23412             cls: 'datepicker-months',
23413             cn: [
23414             {
23415                 tag: 'table',
23416                 cls: 'table-condensed',
23417                 cn:[
23418                 Roo.bootstrap.DateField.head,
23419                 Roo.bootstrap.DateField.content,
23420                 Roo.bootstrap.DateField.footer
23421                 ]
23422             }
23423             ]
23424         },
23425         {
23426             tag: 'div',
23427             cls: 'datepicker-years',
23428             cn: [
23429             {
23430                 tag: 'table',
23431                 cls: 'table-condensed',
23432                 cn:[
23433                 Roo.bootstrap.DateField.head,
23434                 Roo.bootstrap.DateField.content,
23435                 Roo.bootstrap.DateField.footer
23436                 ]
23437             }
23438             ]
23439         }
23440         ]
23441     }
23442 });
23443
23444  
23445
23446  /*
23447  * - LGPL
23448  *
23449  * TimeField
23450  * 
23451  */
23452
23453 /**
23454  * @class Roo.bootstrap.TimeField
23455  * @extends Roo.bootstrap.Input
23456  * Bootstrap DateField class
23457  * 
23458  * 
23459  * @constructor
23460  * Create a new TimeField
23461  * @param {Object} config The config object
23462  */
23463
23464 Roo.bootstrap.TimeField = function(config){
23465     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23466     this.addEvents({
23467             /**
23468              * @event show
23469              * Fires when this field show.
23470              * @param {Roo.bootstrap.DateField} thisthis
23471              * @param {Mixed} date The date value
23472              */
23473             show : true,
23474             /**
23475              * @event show
23476              * Fires when this field hide.
23477              * @param {Roo.bootstrap.DateField} this
23478              * @param {Mixed} date The date value
23479              */
23480             hide : true,
23481             /**
23482              * @event select
23483              * Fires when select a date.
23484              * @param {Roo.bootstrap.DateField} this
23485              * @param {Mixed} date The date value
23486              */
23487             select : true
23488         });
23489 };
23490
23491 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23492     
23493     /**
23494      * @cfg {String} format
23495      * The default time format string which can be overriden for localization support.  The format must be
23496      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23497      */
23498     format : "H:i",
23499
23500     getAutoCreate : function()
23501     {
23502         this.after = '<i class="fa far fa-clock"></i>';
23503         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23504         
23505          
23506     },
23507     onRender: function(ct, position)
23508     {
23509         
23510         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23511                 
23512         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23513         
23514         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23515         
23516         this.pop = this.picker().select('>.datepicker-time',true).first();
23517         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23518         
23519         this.picker().on('mousedown', this.onMousedown, this);
23520         this.picker().on('click', this.onClick, this);
23521         
23522         this.picker().addClass('datepicker-dropdown');
23523     
23524         this.fillTime();
23525         this.update();
23526             
23527         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23528         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23529         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23530         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23531         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23532         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23533
23534     },
23535     
23536     fireKey: function(e){
23537         if (!this.picker().isVisible()){
23538             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23539                 this.show();
23540             }
23541             return;
23542         }
23543
23544         e.preventDefault();
23545         
23546         switch(e.keyCode){
23547             case 27: // escape
23548                 this.hide();
23549                 break;
23550             case 37: // left
23551             case 39: // right
23552                 this.onTogglePeriod();
23553                 break;
23554             case 38: // up
23555                 this.onIncrementMinutes();
23556                 break;
23557             case 40: // down
23558                 this.onDecrementMinutes();
23559                 break;
23560             case 13: // enter
23561             case 9: // tab
23562                 this.setTime();
23563                 break;
23564         }
23565     },
23566     
23567     onClick: function(e) {
23568         e.stopPropagation();
23569         e.preventDefault();
23570     },
23571     
23572     picker : function()
23573     {
23574         return this.pickerEl;
23575     },
23576     
23577     fillTime: function()
23578     {    
23579         var time = this.pop.select('tbody', true).first();
23580         
23581         time.dom.innerHTML = '';
23582         
23583         time.createChild({
23584             tag: 'tr',
23585             cn: [
23586                 {
23587                     tag: 'td',
23588                     cn: [
23589                         {
23590                             tag: 'a',
23591                             href: '#',
23592                             cls: 'btn',
23593                             cn: [
23594                                 {
23595                                     tag: 'i',
23596                                     cls: 'hours-up fa fas fa-chevron-up'
23597                                 }
23598                             ]
23599                         } 
23600                     ]
23601                 },
23602                 {
23603                     tag: 'td',
23604                     cls: 'separator'
23605                 },
23606                 {
23607                     tag: 'td',
23608                     cn: [
23609                         {
23610                             tag: 'a',
23611                             href: '#',
23612                             cls: 'btn',
23613                             cn: [
23614                                 {
23615                                     tag: 'i',
23616                                     cls: 'minutes-up fa fas fa-chevron-up'
23617                                 }
23618                             ]
23619                         }
23620                     ]
23621                 },
23622                 {
23623                     tag: 'td',
23624                     cls: 'separator'
23625                 }
23626             ]
23627         });
23628         
23629         time.createChild({
23630             tag: 'tr',
23631             cn: [
23632                 {
23633                     tag: 'td',
23634                     cn: [
23635                         {
23636                             tag: 'span',
23637                             cls: 'timepicker-hour',
23638                             html: '00'
23639                         }  
23640                     ]
23641                 },
23642                 {
23643                     tag: 'td',
23644                     cls: 'separator',
23645                     html: ':'
23646                 },
23647                 {
23648                     tag: 'td',
23649                     cn: [
23650                         {
23651                             tag: 'span',
23652                             cls: 'timepicker-minute',
23653                             html: '00'
23654                         }  
23655                     ]
23656                 },
23657                 {
23658                     tag: 'td',
23659                     cls: 'separator'
23660                 },
23661                 {
23662                     tag: 'td',
23663                     cn: [
23664                         {
23665                             tag: 'button',
23666                             type: 'button',
23667                             cls: 'btn btn-primary period',
23668                             html: 'AM'
23669                             
23670                         }
23671                     ]
23672                 }
23673             ]
23674         });
23675         
23676         time.createChild({
23677             tag: 'tr',
23678             cn: [
23679                 {
23680                     tag: 'td',
23681                     cn: [
23682                         {
23683                             tag: 'a',
23684                             href: '#',
23685                             cls: 'btn',
23686                             cn: [
23687                                 {
23688                                     tag: 'span',
23689                                     cls: 'hours-down fa fas fa-chevron-down'
23690                                 }
23691                             ]
23692                         }
23693                     ]
23694                 },
23695                 {
23696                     tag: 'td',
23697                     cls: 'separator'
23698                 },
23699                 {
23700                     tag: 'td',
23701                     cn: [
23702                         {
23703                             tag: 'a',
23704                             href: '#',
23705                             cls: 'btn',
23706                             cn: [
23707                                 {
23708                                     tag: 'span',
23709                                     cls: 'minutes-down fa fas fa-chevron-down'
23710                                 }
23711                             ]
23712                         }
23713                     ]
23714                 },
23715                 {
23716                     tag: 'td',
23717                     cls: 'separator'
23718                 }
23719             ]
23720         });
23721         
23722     },
23723     
23724     update: function()
23725     {
23726         
23727         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23728         
23729         this.fill();
23730     },
23731     
23732     fill: function() 
23733     {
23734         var hours = this.time.getHours();
23735         var minutes = this.time.getMinutes();
23736         var period = 'AM';
23737         
23738         if(hours > 11){
23739             period = 'PM';
23740         }
23741         
23742         if(hours == 0){
23743             hours = 12;
23744         }
23745         
23746         
23747         if(hours > 12){
23748             hours = hours - 12;
23749         }
23750         
23751         if(hours < 10){
23752             hours = '0' + hours;
23753         }
23754         
23755         if(minutes < 10){
23756             minutes = '0' + minutes;
23757         }
23758         
23759         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23760         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23761         this.pop.select('button', true).first().dom.innerHTML = period;
23762         
23763     },
23764     
23765     place: function()
23766     {   
23767         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23768         
23769         var cls = ['bottom'];
23770         
23771         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23772             cls.pop();
23773             cls.push('top');
23774         }
23775         
23776         cls.push('right');
23777         
23778         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23779             cls.pop();
23780             cls.push('left');
23781         }
23782         //this.picker().setXY(20000,20000);
23783         this.picker().addClass(cls.join('-'));
23784         
23785         var _this = this;
23786         
23787         Roo.each(cls, function(c){
23788             if(c == 'bottom'){
23789                 (function() {
23790                  //  
23791                 }).defer(200);
23792                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23793                 //_this.picker().setTop(_this.inputEl().getHeight());
23794                 return;
23795             }
23796             if(c == 'top'){
23797                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23798                 
23799                 //_this.picker().setTop(0 - _this.picker().getHeight());
23800                 return;
23801             }
23802             /*
23803             if(c == 'left'){
23804                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23805                 return;
23806             }
23807             if(c == 'right'){
23808                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23809                 return;
23810             }
23811             */
23812         });
23813         
23814     },
23815   
23816     onFocus : function()
23817     {
23818         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23819         this.show();
23820     },
23821     
23822     onBlur : function()
23823     {
23824         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23825         this.hide();
23826     },
23827     
23828     show : function()
23829     {
23830         this.picker().show();
23831         this.pop.show();
23832         this.update();
23833         this.place();
23834         
23835         this.fireEvent('show', this, this.date);
23836     },
23837     
23838     hide : function()
23839     {
23840         this.picker().hide();
23841         this.pop.hide();
23842         
23843         this.fireEvent('hide', this, this.date);
23844     },
23845     
23846     setTime : function()
23847     {
23848         this.hide();
23849         this.setValue(this.time.format(this.format));
23850         
23851         this.fireEvent('select', this, this.date);
23852         
23853         
23854     },
23855     
23856     onMousedown: function(e){
23857         e.stopPropagation();
23858         e.preventDefault();
23859     },
23860     
23861     onIncrementHours: function()
23862     {
23863         Roo.log('onIncrementHours');
23864         this.time = this.time.add(Date.HOUR, 1);
23865         this.update();
23866         
23867     },
23868     
23869     onDecrementHours: function()
23870     {
23871         Roo.log('onDecrementHours');
23872         this.time = this.time.add(Date.HOUR, -1);
23873         this.update();
23874     },
23875     
23876     onIncrementMinutes: function()
23877     {
23878         Roo.log('onIncrementMinutes');
23879         this.time = this.time.add(Date.MINUTE, 1);
23880         this.update();
23881     },
23882     
23883     onDecrementMinutes: function()
23884     {
23885         Roo.log('onDecrementMinutes');
23886         this.time = this.time.add(Date.MINUTE, -1);
23887         this.update();
23888     },
23889     
23890     onTogglePeriod: function()
23891     {
23892         Roo.log('onTogglePeriod');
23893         this.time = this.time.add(Date.HOUR, 12);
23894         this.update();
23895     }
23896     
23897    
23898 });
23899  
23900
23901 Roo.apply(Roo.bootstrap.TimeField,  {
23902   
23903     template : {
23904         tag: 'div',
23905         cls: 'datepicker dropdown-menu',
23906         cn: [
23907             {
23908                 tag: 'div',
23909                 cls: 'datepicker-time',
23910                 cn: [
23911                 {
23912                     tag: 'table',
23913                     cls: 'table-condensed',
23914                     cn:[
23915                         {
23916                             tag: 'tbody',
23917                             cn: [
23918                                 {
23919                                     tag: 'tr',
23920                                     cn: [
23921                                     {
23922                                         tag: 'td',
23923                                         colspan: '7'
23924                                     }
23925                                     ]
23926                                 }
23927                             ]
23928                         },
23929                         {
23930                             tag: 'tfoot',
23931                             cn: [
23932                                 {
23933                                     tag: 'tr',
23934                                     cn: [
23935                                     {
23936                                         tag: 'th',
23937                                         colspan: '7',
23938                                         cls: '',
23939                                         cn: [
23940                                             {
23941                                                 tag: 'button',
23942                                                 cls: 'btn btn-info ok',
23943                                                 html: 'OK'
23944                                             }
23945                                         ]
23946                                     }
23947                     
23948                                     ]
23949                                 }
23950                             ]
23951                         }
23952                     ]
23953                 }
23954                 ]
23955             }
23956         ]
23957     }
23958 });
23959
23960  
23961
23962  /*
23963  * - LGPL
23964  *
23965  * MonthField
23966  * 
23967  */
23968
23969 /**
23970  * @class Roo.bootstrap.MonthField
23971  * @extends Roo.bootstrap.Input
23972  * Bootstrap MonthField class
23973  * 
23974  * @cfg {String} language default en
23975  * 
23976  * @constructor
23977  * Create a new MonthField
23978  * @param {Object} config The config object
23979  */
23980
23981 Roo.bootstrap.MonthField = function(config){
23982     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23983     
23984     this.addEvents({
23985         /**
23986          * @event show
23987          * Fires when this field show.
23988          * @param {Roo.bootstrap.MonthField} this
23989          * @param {Mixed} date The date value
23990          */
23991         show : true,
23992         /**
23993          * @event show
23994          * Fires when this field hide.
23995          * @param {Roo.bootstrap.MonthField} this
23996          * @param {Mixed} date The date value
23997          */
23998         hide : true,
23999         /**
24000          * @event select
24001          * Fires when select a date.
24002          * @param {Roo.bootstrap.MonthField} this
24003          * @param {String} oldvalue The old value
24004          * @param {String} newvalue The new value
24005          */
24006         select : true
24007     });
24008 };
24009
24010 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24011     
24012     onRender: function(ct, position)
24013     {
24014         
24015         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24016         
24017         this.language = this.language || 'en';
24018         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24019         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24020         
24021         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24022         this.isInline = false;
24023         this.isInput = true;
24024         this.component = this.el.select('.add-on', true).first() || false;
24025         this.component = (this.component && this.component.length === 0) ? false : this.component;
24026         this.hasInput = this.component && this.inputEL().length;
24027         
24028         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24029         
24030         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24031         
24032         this.picker().on('mousedown', this.onMousedown, this);
24033         this.picker().on('click', this.onClick, this);
24034         
24035         this.picker().addClass('datepicker-dropdown');
24036         
24037         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24038             v.setStyle('width', '189px');
24039         });
24040         
24041         this.fillMonths();
24042         
24043         this.update();
24044         
24045         if(this.isInline) {
24046             this.show();
24047         }
24048         
24049     },
24050     
24051     setValue: function(v, suppressEvent)
24052     {   
24053         var o = this.getValue();
24054         
24055         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24056         
24057         this.update();
24058
24059         if(suppressEvent !== true){
24060             this.fireEvent('select', this, o, v);
24061         }
24062         
24063     },
24064     
24065     getValue: function()
24066     {
24067         return this.value;
24068     },
24069     
24070     onClick: function(e) 
24071     {
24072         e.stopPropagation();
24073         e.preventDefault();
24074         
24075         var target = e.getTarget();
24076         
24077         if(target.nodeName.toLowerCase() === 'i'){
24078             target = Roo.get(target).dom.parentNode;
24079         }
24080         
24081         var nodeName = target.nodeName;
24082         var className = target.className;
24083         var html = target.innerHTML;
24084         
24085         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24086             return;
24087         }
24088         
24089         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24090         
24091         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24092         
24093         this.hide();
24094                         
24095     },
24096     
24097     picker : function()
24098     {
24099         return this.pickerEl;
24100     },
24101     
24102     fillMonths: function()
24103     {    
24104         var i = 0;
24105         var months = this.picker().select('>.datepicker-months td', true).first();
24106         
24107         months.dom.innerHTML = '';
24108         
24109         while (i < 12) {
24110             var month = {
24111                 tag: 'span',
24112                 cls: 'month',
24113                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24114             };
24115             
24116             months.createChild(month);
24117         }
24118         
24119     },
24120     
24121     update: function()
24122     {
24123         var _this = this;
24124         
24125         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24126             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24127         }
24128         
24129         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24130             e.removeClass('active');
24131             
24132             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24133                 e.addClass('active');
24134             }
24135         })
24136     },
24137     
24138     place: function()
24139     {
24140         if(this.isInline) {
24141             return;
24142         }
24143         
24144         this.picker().removeClass(['bottom', 'top']);
24145         
24146         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24147             /*
24148              * place to the top of element!
24149              *
24150              */
24151             
24152             this.picker().addClass('top');
24153             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24154             
24155             return;
24156         }
24157         
24158         this.picker().addClass('bottom');
24159         
24160         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24161     },
24162     
24163     onFocus : function()
24164     {
24165         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24166         this.show();
24167     },
24168     
24169     onBlur : function()
24170     {
24171         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24172         
24173         var d = this.inputEl().getValue();
24174         
24175         this.setValue(d);
24176                 
24177         this.hide();
24178     },
24179     
24180     show : function()
24181     {
24182         this.picker().show();
24183         this.picker().select('>.datepicker-months', true).first().show();
24184         this.update();
24185         this.place();
24186         
24187         this.fireEvent('show', this, this.date);
24188     },
24189     
24190     hide : function()
24191     {
24192         if(this.isInline) {
24193             return;
24194         }
24195         this.picker().hide();
24196         this.fireEvent('hide', this, this.date);
24197         
24198     },
24199     
24200     onMousedown: function(e)
24201     {
24202         e.stopPropagation();
24203         e.preventDefault();
24204     },
24205     
24206     keyup: function(e)
24207     {
24208         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24209         this.update();
24210     },
24211
24212     fireKey: function(e)
24213     {
24214         if (!this.picker().isVisible()){
24215             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24216                 this.show();
24217             }
24218             return;
24219         }
24220         
24221         var dir;
24222         
24223         switch(e.keyCode){
24224             case 27: // escape
24225                 this.hide();
24226                 e.preventDefault();
24227                 break;
24228             case 37: // left
24229             case 39: // right
24230                 dir = e.keyCode == 37 ? -1 : 1;
24231                 
24232                 this.vIndex = this.vIndex + dir;
24233                 
24234                 if(this.vIndex < 0){
24235                     this.vIndex = 0;
24236                 }
24237                 
24238                 if(this.vIndex > 11){
24239                     this.vIndex = 11;
24240                 }
24241                 
24242                 if(isNaN(this.vIndex)){
24243                     this.vIndex = 0;
24244                 }
24245                 
24246                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24247                 
24248                 break;
24249             case 38: // up
24250             case 40: // down
24251                 
24252                 dir = e.keyCode == 38 ? -1 : 1;
24253                 
24254                 this.vIndex = this.vIndex + dir * 4;
24255                 
24256                 if(this.vIndex < 0){
24257                     this.vIndex = 0;
24258                 }
24259                 
24260                 if(this.vIndex > 11){
24261                     this.vIndex = 11;
24262                 }
24263                 
24264                 if(isNaN(this.vIndex)){
24265                     this.vIndex = 0;
24266                 }
24267                 
24268                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24269                 break;
24270                 
24271             case 13: // enter
24272                 
24273                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24274                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24275                 }
24276                 
24277                 this.hide();
24278                 e.preventDefault();
24279                 break;
24280             case 9: // tab
24281                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24282                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24283                 }
24284                 this.hide();
24285                 break;
24286             case 16: // shift
24287             case 17: // ctrl
24288             case 18: // alt
24289                 break;
24290             default :
24291                 this.hide();
24292                 
24293         }
24294     },
24295     
24296     remove: function() 
24297     {
24298         this.picker().remove();
24299     }
24300    
24301 });
24302
24303 Roo.apply(Roo.bootstrap.MonthField,  {
24304     
24305     content : {
24306         tag: 'tbody',
24307         cn: [
24308         {
24309             tag: 'tr',
24310             cn: [
24311             {
24312                 tag: 'td',
24313                 colspan: '7'
24314             }
24315             ]
24316         }
24317         ]
24318     },
24319     
24320     dates:{
24321         en: {
24322             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24323             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24324         }
24325     }
24326 });
24327
24328 Roo.apply(Roo.bootstrap.MonthField,  {
24329   
24330     template : {
24331         tag: 'div',
24332         cls: 'datepicker dropdown-menu roo-dynamic',
24333         cn: [
24334             {
24335                 tag: 'div',
24336                 cls: 'datepicker-months',
24337                 cn: [
24338                 {
24339                     tag: 'table',
24340                     cls: 'table-condensed',
24341                     cn:[
24342                         Roo.bootstrap.DateField.content
24343                     ]
24344                 }
24345                 ]
24346             }
24347         ]
24348     }
24349 });
24350
24351  
24352
24353  
24354  /*
24355  * - LGPL
24356  *
24357  * CheckBox
24358  * 
24359  */
24360
24361 /**
24362  * @class Roo.bootstrap.CheckBox
24363  * @extends Roo.bootstrap.Input
24364  * Bootstrap CheckBox class
24365  * 
24366  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24367  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24368  * @cfg {String} boxLabel The text that appears beside the checkbox
24369  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24370  * @cfg {Boolean} checked initnal the element
24371  * @cfg {Boolean} inline inline the element (default false)
24372  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24373  * @cfg {String} tooltip label tooltip
24374  * 
24375  * @constructor
24376  * Create a new CheckBox
24377  * @param {Object} config The config object
24378  */
24379
24380 Roo.bootstrap.CheckBox = function(config){
24381     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24382    
24383     this.addEvents({
24384         /**
24385         * @event check
24386         * Fires when the element is checked or unchecked.
24387         * @param {Roo.bootstrap.CheckBox} this This input
24388         * @param {Boolean} checked The new checked value
24389         */
24390        check : true,
24391        /**
24392         * @event click
24393         * Fires when the element is click.
24394         * @param {Roo.bootstrap.CheckBox} this This input
24395         */
24396        click : true
24397     });
24398     
24399 };
24400
24401 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24402   
24403     inputType: 'checkbox',
24404     inputValue: 1,
24405     valueOff: 0,
24406     boxLabel: false,
24407     checked: false,
24408     weight : false,
24409     inline: false,
24410     tooltip : '',
24411     
24412     // checkbox success does not make any sense really.. 
24413     invalidClass : "",
24414     validClass : "",
24415     
24416     
24417     getAutoCreate : function()
24418     {
24419         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24420         
24421         var id = Roo.id();
24422         
24423         var cfg = {};
24424         
24425         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24426         
24427         if(this.inline){
24428             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24429         }
24430         
24431         var input =  {
24432             tag: 'input',
24433             id : id,
24434             type : this.inputType,
24435             value : this.inputValue,
24436             cls : 'roo-' + this.inputType, //'form-box',
24437             placeholder : this.placeholder || ''
24438             
24439         };
24440         
24441         if(this.inputType != 'radio'){
24442             var hidden =  {
24443                 tag: 'input',
24444                 type : 'hidden',
24445                 cls : 'roo-hidden-value',
24446                 value : this.checked ? this.inputValue : this.valueOff
24447             };
24448         }
24449         
24450             
24451         if (this.weight) { // Validity check?
24452             cfg.cls += " " + this.inputType + "-" + this.weight;
24453         }
24454         
24455         if (this.disabled) {
24456             input.disabled=true;
24457         }
24458         
24459         if(this.checked){
24460             input.checked = this.checked;
24461         }
24462         
24463         if (this.name) {
24464             
24465             input.name = this.name;
24466             
24467             if(this.inputType != 'radio'){
24468                 hidden.name = this.name;
24469                 input.name = '_hidden_' + this.name;
24470             }
24471         }
24472         
24473         if (this.size) {
24474             input.cls += ' input-' + this.size;
24475         }
24476         
24477         var settings=this;
24478         
24479         ['xs','sm','md','lg'].map(function(size){
24480             if (settings[size]) {
24481                 cfg.cls += ' col-' + size + '-' + settings[size];
24482             }
24483         });
24484         
24485         var inputblock = input;
24486          
24487         if (this.before || this.after) {
24488             
24489             inputblock = {
24490                 cls : 'input-group',
24491                 cn :  [] 
24492             };
24493             
24494             if (this.before) {
24495                 inputblock.cn.push({
24496                     tag :'span',
24497                     cls : 'input-group-addon',
24498                     html : this.before
24499                 });
24500             }
24501             
24502             inputblock.cn.push(input);
24503             
24504             if(this.inputType != 'radio'){
24505                 inputblock.cn.push(hidden);
24506             }
24507             
24508             if (this.after) {
24509                 inputblock.cn.push({
24510                     tag :'span',
24511                     cls : 'input-group-addon',
24512                     html : this.after
24513                 });
24514             }
24515             
24516         }
24517         var boxLabelCfg = false;
24518         
24519         if(this.boxLabel){
24520            
24521             boxLabelCfg = {
24522                 tag: 'label',
24523                 //'for': id, // box label is handled by onclick - so no for...
24524                 cls: 'box-label',
24525                 html: this.boxLabel
24526             };
24527             if(this.tooltip){
24528                 boxLabelCfg.tooltip = this.tooltip;
24529             }
24530              
24531         }
24532         
24533         
24534         if (align ==='left' && this.fieldLabel.length) {
24535 //                Roo.log("left and has label");
24536             cfg.cn = [
24537                 {
24538                     tag: 'label',
24539                     'for' :  id,
24540                     cls : 'control-label',
24541                     html : this.fieldLabel
24542                 },
24543                 {
24544                     cls : "", 
24545                     cn: [
24546                         inputblock
24547                     ]
24548                 }
24549             ];
24550             
24551             if (boxLabelCfg) {
24552                 cfg.cn[1].cn.push(boxLabelCfg);
24553             }
24554             
24555             if(this.labelWidth > 12){
24556                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24557             }
24558             
24559             if(this.labelWidth < 13 && this.labelmd == 0){
24560                 this.labelmd = this.labelWidth;
24561             }
24562             
24563             if(this.labellg > 0){
24564                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24565                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24566             }
24567             
24568             if(this.labelmd > 0){
24569                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24570                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24571             }
24572             
24573             if(this.labelsm > 0){
24574                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24575                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24576             }
24577             
24578             if(this.labelxs > 0){
24579                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24580                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24581             }
24582             
24583         } else if ( this.fieldLabel.length) {
24584 //                Roo.log(" label");
24585                 cfg.cn = [
24586                    
24587                     {
24588                         tag: this.boxLabel ? 'span' : 'label',
24589                         'for': id,
24590                         cls: 'control-label box-input-label',
24591                         //cls : 'input-group-addon',
24592                         html : this.fieldLabel
24593                     },
24594                     
24595                     inputblock
24596                     
24597                 ];
24598                 if (boxLabelCfg) {
24599                     cfg.cn.push(boxLabelCfg);
24600                 }
24601
24602         } else {
24603             
24604 //                Roo.log(" no label && no align");
24605                 cfg.cn = [  inputblock ] ;
24606                 if (boxLabelCfg) {
24607                     cfg.cn.push(boxLabelCfg);
24608                 }
24609
24610                 
24611         }
24612         
24613        
24614         
24615         if(this.inputType != 'radio'){
24616             cfg.cn.push(hidden);
24617         }
24618         
24619         return cfg;
24620         
24621     },
24622     
24623     /**
24624      * return the real input element.
24625      */
24626     inputEl: function ()
24627     {
24628         return this.el.select('input.roo-' + this.inputType,true).first();
24629     },
24630     hiddenEl: function ()
24631     {
24632         return this.el.select('input.roo-hidden-value',true).first();
24633     },
24634     
24635     labelEl: function()
24636     {
24637         return this.el.select('label.control-label',true).first();
24638     },
24639     /* depricated... */
24640     
24641     label: function()
24642     {
24643         return this.labelEl();
24644     },
24645     
24646     boxLabelEl: function()
24647     {
24648         return this.el.select('label.box-label',true).first();
24649     },
24650     
24651     initEvents : function()
24652     {
24653 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24654         
24655         this.inputEl().on('click', this.onClick,  this);
24656         
24657         if (this.boxLabel) { 
24658             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24659         }
24660         
24661         this.startValue = this.getValue();
24662         
24663         if(this.groupId){
24664             Roo.bootstrap.CheckBox.register(this);
24665         }
24666     },
24667     
24668     onClick : function(e)
24669     {   
24670         if(this.fireEvent('click', this, e) !== false){
24671             this.setChecked(!this.checked);
24672         }
24673         
24674     },
24675     
24676     setChecked : function(state,suppressEvent)
24677     {
24678         this.startValue = this.getValue();
24679
24680         if(this.inputType == 'radio'){
24681             
24682             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24683                 e.dom.checked = false;
24684             });
24685             
24686             this.inputEl().dom.checked = true;
24687             
24688             this.inputEl().dom.value = this.inputValue;
24689             
24690             if(suppressEvent !== true){
24691                 this.fireEvent('check', this, true);
24692             }
24693             
24694             this.validate();
24695             
24696             return;
24697         }
24698         
24699         this.checked = state;
24700         
24701         this.inputEl().dom.checked = state;
24702         
24703         
24704         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24705         
24706         if(suppressEvent !== true){
24707             this.fireEvent('check', this, state);
24708         }
24709         
24710         this.validate();
24711     },
24712     
24713     getValue : function()
24714     {
24715         if(this.inputType == 'radio'){
24716             return this.getGroupValue();
24717         }
24718         
24719         return this.hiddenEl().dom.value;
24720         
24721     },
24722     
24723     getGroupValue : function()
24724     {
24725         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24726             return '';
24727         }
24728         
24729         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24730     },
24731     
24732     setValue : function(v,suppressEvent)
24733     {
24734         if(this.inputType == 'radio'){
24735             this.setGroupValue(v, suppressEvent);
24736             return;
24737         }
24738         
24739         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24740         
24741         this.validate();
24742     },
24743     
24744     setGroupValue : function(v, suppressEvent)
24745     {
24746         this.startValue = this.getValue();
24747         
24748         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24749             e.dom.checked = false;
24750             
24751             if(e.dom.value == v){
24752                 e.dom.checked = true;
24753             }
24754         });
24755         
24756         if(suppressEvent !== true){
24757             this.fireEvent('check', this, true);
24758         }
24759
24760         this.validate();
24761         
24762         return;
24763     },
24764     
24765     validate : function()
24766     {
24767         if(this.getVisibilityEl().hasClass('hidden')){
24768             return true;
24769         }
24770         
24771         if(
24772                 this.disabled || 
24773                 (this.inputType == 'radio' && this.validateRadio()) ||
24774                 (this.inputType == 'checkbox' && this.validateCheckbox())
24775         ){
24776             this.markValid();
24777             return true;
24778         }
24779         
24780         this.markInvalid();
24781         return false;
24782     },
24783     
24784     validateRadio : function()
24785     {
24786         if(this.getVisibilityEl().hasClass('hidden')){
24787             return true;
24788         }
24789         
24790         if(this.allowBlank){
24791             return true;
24792         }
24793         
24794         var valid = false;
24795         
24796         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24797             if(!e.dom.checked){
24798                 return;
24799             }
24800             
24801             valid = true;
24802             
24803             return false;
24804         });
24805         
24806         return valid;
24807     },
24808     
24809     validateCheckbox : function()
24810     {
24811         if(!this.groupId){
24812             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24813             //return (this.getValue() == this.inputValue) ? true : false;
24814         }
24815         
24816         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24817         
24818         if(!group){
24819             return false;
24820         }
24821         
24822         var r = false;
24823         
24824         for(var i in group){
24825             if(group[i].el.isVisible(true)){
24826                 r = false;
24827                 break;
24828             }
24829             
24830             r = true;
24831         }
24832         
24833         for(var i in group){
24834             if(r){
24835                 break;
24836             }
24837             
24838             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24839         }
24840         
24841         return r;
24842     },
24843     
24844     /**
24845      * Mark this field as valid
24846      */
24847     markValid : function()
24848     {
24849         var _this = this;
24850         
24851         this.fireEvent('valid', this);
24852         
24853         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24854         
24855         if(this.groupId){
24856             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24857         }
24858         
24859         if(label){
24860             label.markValid();
24861         }
24862
24863         if(this.inputType == 'radio'){
24864             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24865                 var fg = e.findParent('.form-group', false, true);
24866                 if (Roo.bootstrap.version == 3) {
24867                     fg.removeClass([_this.invalidClass, _this.validClass]);
24868                     fg.addClass(_this.validClass);
24869                 } else {
24870                     fg.removeClass(['is-valid', 'is-invalid']);
24871                     fg.addClass('is-valid');
24872                 }
24873             });
24874             
24875             return;
24876         }
24877
24878         if(!this.groupId){
24879             var fg = this.el.findParent('.form-group', false, true);
24880             if (Roo.bootstrap.version == 3) {
24881                 fg.removeClass([this.invalidClass, this.validClass]);
24882                 fg.addClass(this.validClass);
24883             } else {
24884                 fg.removeClass(['is-valid', 'is-invalid']);
24885                 fg.addClass('is-valid');
24886             }
24887             return;
24888         }
24889         
24890         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24891         
24892         if(!group){
24893             return;
24894         }
24895         
24896         for(var i in group){
24897             var fg = group[i].el.findParent('.form-group', false, true);
24898             if (Roo.bootstrap.version == 3) {
24899                 fg.removeClass([this.invalidClass, this.validClass]);
24900                 fg.addClass(this.validClass);
24901             } else {
24902                 fg.removeClass(['is-valid', 'is-invalid']);
24903                 fg.addClass('is-valid');
24904             }
24905         }
24906     },
24907     
24908      /**
24909      * Mark this field as invalid
24910      * @param {String} msg The validation message
24911      */
24912     markInvalid : function(msg)
24913     {
24914         if(this.allowBlank){
24915             return;
24916         }
24917         
24918         var _this = this;
24919         
24920         this.fireEvent('invalid', this, msg);
24921         
24922         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24923         
24924         if(this.groupId){
24925             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24926         }
24927         
24928         if(label){
24929             label.markInvalid();
24930         }
24931             
24932         if(this.inputType == 'radio'){
24933             
24934             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24935                 var fg = e.findParent('.form-group', false, true);
24936                 if (Roo.bootstrap.version == 3) {
24937                     fg.removeClass([_this.invalidClass, _this.validClass]);
24938                     fg.addClass(_this.invalidClass);
24939                 } else {
24940                     fg.removeClass(['is-invalid', 'is-valid']);
24941                     fg.addClass('is-invalid');
24942                 }
24943             });
24944             
24945             return;
24946         }
24947         
24948         if(!this.groupId){
24949             var fg = this.el.findParent('.form-group', false, true);
24950             if (Roo.bootstrap.version == 3) {
24951                 fg.removeClass([_this.invalidClass, _this.validClass]);
24952                 fg.addClass(_this.invalidClass);
24953             } else {
24954                 fg.removeClass(['is-invalid', 'is-valid']);
24955                 fg.addClass('is-invalid');
24956             }
24957             return;
24958         }
24959         
24960         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24961         
24962         if(!group){
24963             return;
24964         }
24965         
24966         for(var i in group){
24967             var fg = group[i].el.findParent('.form-group', false, true);
24968             if (Roo.bootstrap.version == 3) {
24969                 fg.removeClass([_this.invalidClass, _this.validClass]);
24970                 fg.addClass(_this.invalidClass);
24971             } else {
24972                 fg.removeClass(['is-invalid', 'is-valid']);
24973                 fg.addClass('is-invalid');
24974             }
24975         }
24976         
24977     },
24978     
24979     clearInvalid : function()
24980     {
24981         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24982         
24983         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24984         
24985         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24986         
24987         if (label && label.iconEl) {
24988             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24989             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24990         }
24991     },
24992     
24993     disable : function()
24994     {
24995         if(this.inputType != 'radio'){
24996             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24997             return;
24998         }
24999         
25000         var _this = this;
25001         
25002         if(this.rendered){
25003             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25004                 _this.getActionEl().addClass(this.disabledClass);
25005                 e.dom.disabled = true;
25006             });
25007         }
25008         
25009         this.disabled = true;
25010         this.fireEvent("disable", this);
25011         return this;
25012     },
25013
25014     enable : function()
25015     {
25016         if(this.inputType != 'radio'){
25017             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25018             return;
25019         }
25020         
25021         var _this = this;
25022         
25023         if(this.rendered){
25024             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25025                 _this.getActionEl().removeClass(this.disabledClass);
25026                 e.dom.disabled = false;
25027             });
25028         }
25029         
25030         this.disabled = false;
25031         this.fireEvent("enable", this);
25032         return this;
25033     },
25034     
25035     setBoxLabel : function(v)
25036     {
25037         this.boxLabel = v;
25038         
25039         if(this.rendered){
25040             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25041         }
25042     }
25043
25044 });
25045
25046 Roo.apply(Roo.bootstrap.CheckBox, {
25047     
25048     groups: {},
25049     
25050      /**
25051     * register a CheckBox Group
25052     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25053     */
25054     register : function(checkbox)
25055     {
25056         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25057             this.groups[checkbox.groupId] = {};
25058         }
25059         
25060         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25061             return;
25062         }
25063         
25064         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25065         
25066     },
25067     /**
25068     * fetch a CheckBox Group based on the group ID
25069     * @param {string} the group ID
25070     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25071     */
25072     get: function(groupId) {
25073         if (typeof(this.groups[groupId]) == 'undefined') {
25074             return false;
25075         }
25076         
25077         return this.groups[groupId] ;
25078     }
25079     
25080     
25081 });
25082 /*
25083  * - LGPL
25084  *
25085  * RadioItem
25086  * 
25087  */
25088
25089 /**
25090  * @class Roo.bootstrap.Radio
25091  * @extends Roo.bootstrap.Component
25092  * Bootstrap Radio class
25093  * @cfg {String} boxLabel - the label associated
25094  * @cfg {String} value - the value of radio
25095  * 
25096  * @constructor
25097  * Create a new Radio
25098  * @param {Object} config The config object
25099  */
25100 Roo.bootstrap.Radio = function(config){
25101     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25102     
25103 };
25104
25105 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25106     
25107     boxLabel : '',
25108     
25109     value : '',
25110     
25111     getAutoCreate : function()
25112     {
25113         var cfg = {
25114             tag : 'div',
25115             cls : 'form-group radio',
25116             cn : [
25117                 {
25118                     tag : 'label',
25119                     cls : 'box-label',
25120                     html : this.boxLabel
25121                 }
25122             ]
25123         };
25124         
25125         return cfg;
25126     },
25127     
25128     initEvents : function() 
25129     {
25130         this.parent().register(this);
25131         
25132         this.el.on('click', this.onClick, this);
25133         
25134     },
25135     
25136     onClick : function(e)
25137     {
25138         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25139             this.setChecked(true);
25140         }
25141     },
25142     
25143     setChecked : function(state, suppressEvent)
25144     {
25145         this.parent().setValue(this.value, suppressEvent);
25146         
25147     },
25148     
25149     setBoxLabel : function(v)
25150     {
25151         this.boxLabel = v;
25152         
25153         if(this.rendered){
25154             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25155         }
25156     }
25157     
25158 });
25159  
25160
25161  /*
25162  * - LGPL
25163  *
25164  * Input
25165  * 
25166  */
25167
25168 /**
25169  * @class Roo.bootstrap.SecurePass
25170  * @extends Roo.bootstrap.Input
25171  * Bootstrap SecurePass class
25172  *
25173  * 
25174  * @constructor
25175  * Create a new SecurePass
25176  * @param {Object} config The config object
25177  */
25178  
25179 Roo.bootstrap.SecurePass = function (config) {
25180     // these go here, so the translation tool can replace them..
25181     this.errors = {
25182         PwdEmpty: "Please type a password, and then retype it to confirm.",
25183         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25184         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25185         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25186         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25187         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25188         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25189         TooWeak: "Your password is Too Weak."
25190     },
25191     this.meterLabel = "Password strength:";
25192     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25193     this.meterClass = [
25194         "roo-password-meter-tooweak", 
25195         "roo-password-meter-weak", 
25196         "roo-password-meter-medium", 
25197         "roo-password-meter-strong", 
25198         "roo-password-meter-grey"
25199     ];
25200     
25201     this.errors = {};
25202     
25203     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25204 }
25205
25206 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25207     /**
25208      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25209      * {
25210      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25211      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25212      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25213      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25214      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25215      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25216      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25217      * })
25218      */
25219     // private
25220     
25221     meterWidth: 300,
25222     errorMsg :'',    
25223     errors: false,
25224     imageRoot: '/',
25225     /**
25226      * @cfg {String/Object} Label for the strength meter (defaults to
25227      * 'Password strength:')
25228      */
25229     // private
25230     meterLabel: '',
25231     /**
25232      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25233      * ['Weak', 'Medium', 'Strong'])
25234      */
25235     // private    
25236     pwdStrengths: false,    
25237     // private
25238     strength: 0,
25239     // private
25240     _lastPwd: null,
25241     // private
25242     kCapitalLetter: 0,
25243     kSmallLetter: 1,
25244     kDigit: 2,
25245     kPunctuation: 3,
25246     
25247     insecure: false,
25248     // private
25249     initEvents: function ()
25250     {
25251         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25252
25253         if (this.el.is('input[type=password]') && Roo.isSafari) {
25254             this.el.on('keydown', this.SafariOnKeyDown, this);
25255         }
25256
25257         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25258     },
25259     // private
25260     onRender: function (ct, position)
25261     {
25262         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25263         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25264         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25265
25266         this.trigger.createChild({
25267                    cn: [
25268                     {
25269                     //id: 'PwdMeter',
25270                     tag: 'div',
25271                     cls: 'roo-password-meter-grey col-xs-12',
25272                     style: {
25273                         //width: 0,
25274                         //width: this.meterWidth + 'px'                                                
25275                         }
25276                     },
25277                     {                            
25278                          cls: 'roo-password-meter-text'                          
25279                     }
25280                 ]            
25281         });
25282
25283          
25284         if (this.hideTrigger) {
25285             this.trigger.setDisplayed(false);
25286         }
25287         this.setSize(this.width || '', this.height || '');
25288     },
25289     // private
25290     onDestroy: function ()
25291     {
25292         if (this.trigger) {
25293             this.trigger.removeAllListeners();
25294             this.trigger.remove();
25295         }
25296         if (this.wrap) {
25297             this.wrap.remove();
25298         }
25299         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25300     },
25301     // private
25302     checkStrength: function ()
25303     {
25304         var pwd = this.inputEl().getValue();
25305         if (pwd == this._lastPwd) {
25306             return;
25307         }
25308
25309         var strength;
25310         if (this.ClientSideStrongPassword(pwd)) {
25311             strength = 3;
25312         } else if (this.ClientSideMediumPassword(pwd)) {
25313             strength = 2;
25314         } else if (this.ClientSideWeakPassword(pwd)) {
25315             strength = 1;
25316         } else {
25317             strength = 0;
25318         }
25319         
25320         Roo.log('strength1: ' + strength);
25321         
25322         //var pm = this.trigger.child('div/div/div').dom;
25323         var pm = this.trigger.child('div/div');
25324         pm.removeClass(this.meterClass);
25325         pm.addClass(this.meterClass[strength]);
25326                 
25327         
25328         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25329                 
25330         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25331         
25332         this._lastPwd = pwd;
25333     },
25334     reset: function ()
25335     {
25336         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25337         
25338         this._lastPwd = '';
25339         
25340         var pm = this.trigger.child('div/div');
25341         pm.removeClass(this.meterClass);
25342         pm.addClass('roo-password-meter-grey');        
25343         
25344         
25345         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25346         
25347         pt.innerHTML = '';
25348         this.inputEl().dom.type='password';
25349     },
25350     // private
25351     validateValue: function (value)
25352     {
25353         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25354             return false;
25355         }
25356         if (value.length == 0) {
25357             if (this.allowBlank) {
25358                 this.clearInvalid();
25359                 return true;
25360             }
25361
25362             this.markInvalid(this.errors.PwdEmpty);
25363             this.errorMsg = this.errors.PwdEmpty;
25364             return false;
25365         }
25366         
25367         if(this.insecure){
25368             return true;
25369         }
25370         
25371         if (!value.match(/[\x21-\x7e]+/)) {
25372             this.markInvalid(this.errors.PwdBadChar);
25373             this.errorMsg = this.errors.PwdBadChar;
25374             return false;
25375         }
25376         if (value.length < 6) {
25377             this.markInvalid(this.errors.PwdShort);
25378             this.errorMsg = this.errors.PwdShort;
25379             return false;
25380         }
25381         if (value.length > 16) {
25382             this.markInvalid(this.errors.PwdLong);
25383             this.errorMsg = this.errors.PwdLong;
25384             return false;
25385         }
25386         var strength;
25387         if (this.ClientSideStrongPassword(value)) {
25388             strength = 3;
25389         } else if (this.ClientSideMediumPassword(value)) {
25390             strength = 2;
25391         } else if (this.ClientSideWeakPassword(value)) {
25392             strength = 1;
25393         } else {
25394             strength = 0;
25395         }
25396
25397         
25398         if (strength < 2) {
25399             //this.markInvalid(this.errors.TooWeak);
25400             this.errorMsg = this.errors.TooWeak;
25401             //return false;
25402         }
25403         
25404         
25405         console.log('strength2: ' + strength);
25406         
25407         //var pm = this.trigger.child('div/div/div').dom;
25408         
25409         var pm = this.trigger.child('div/div');
25410         pm.removeClass(this.meterClass);
25411         pm.addClass(this.meterClass[strength]);
25412                 
25413         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25414                 
25415         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25416         
25417         this.errorMsg = ''; 
25418         return true;
25419     },
25420     // private
25421     CharacterSetChecks: function (type)
25422     {
25423         this.type = type;
25424         this.fResult = false;
25425     },
25426     // private
25427     isctype: function (character, type)
25428     {
25429         switch (type) {  
25430             case this.kCapitalLetter:
25431                 if (character >= 'A' && character <= 'Z') {
25432                     return true;
25433                 }
25434                 break;
25435             
25436             case this.kSmallLetter:
25437                 if (character >= 'a' && character <= 'z') {
25438                     return true;
25439                 }
25440                 break;
25441             
25442             case this.kDigit:
25443                 if (character >= '0' && character <= '9') {
25444                     return true;
25445                 }
25446                 break;
25447             
25448             case this.kPunctuation:
25449                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25450                     return true;
25451                 }
25452                 break;
25453             
25454             default:
25455                 return false;
25456         }
25457
25458     },
25459     // private
25460     IsLongEnough: function (pwd, size)
25461     {
25462         return !(pwd == null || isNaN(size) || pwd.length < size);
25463     },
25464     // private
25465     SpansEnoughCharacterSets: function (word, nb)
25466     {
25467         if (!this.IsLongEnough(word, nb))
25468         {
25469             return false;
25470         }
25471
25472         var characterSetChecks = new Array(
25473             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25474             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25475         );
25476         
25477         for (var index = 0; index < word.length; ++index) {
25478             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25479                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25480                     characterSetChecks[nCharSet].fResult = true;
25481                     break;
25482                 }
25483             }
25484         }
25485
25486         var nCharSets = 0;
25487         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25488             if (characterSetChecks[nCharSet].fResult) {
25489                 ++nCharSets;
25490             }
25491         }
25492
25493         if (nCharSets < nb) {
25494             return false;
25495         }
25496         return true;
25497     },
25498     // private
25499     ClientSideStrongPassword: function (pwd)
25500     {
25501         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25502     },
25503     // private
25504     ClientSideMediumPassword: function (pwd)
25505     {
25506         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25507     },
25508     // private
25509     ClientSideWeakPassword: function (pwd)
25510     {
25511         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25512     }
25513           
25514 })//<script type="text/javascript">
25515
25516 /*
25517  * Based  Ext JS Library 1.1.1
25518  * Copyright(c) 2006-2007, Ext JS, LLC.
25519  * LGPL
25520  *
25521  */
25522  
25523 /**
25524  * @class Roo.HtmlEditorCore
25525  * @extends Roo.Component
25526  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25527  *
25528  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25529  */
25530
25531 Roo.HtmlEditorCore = function(config){
25532     
25533     
25534     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25535     
25536     
25537     this.addEvents({
25538         /**
25539          * @event initialize
25540          * Fires when the editor is fully initialized (including the iframe)
25541          * @param {Roo.HtmlEditorCore} this
25542          */
25543         initialize: true,
25544         /**
25545          * @event activate
25546          * Fires when the editor is first receives the focus. Any insertion must wait
25547          * until after this event.
25548          * @param {Roo.HtmlEditorCore} this
25549          */
25550         activate: true,
25551          /**
25552          * @event beforesync
25553          * Fires before the textarea is updated with content from the editor iframe. Return false
25554          * to cancel the sync.
25555          * @param {Roo.HtmlEditorCore} this
25556          * @param {String} html
25557          */
25558         beforesync: true,
25559          /**
25560          * @event beforepush
25561          * Fires before the iframe editor is updated with content from the textarea. Return false
25562          * to cancel the push.
25563          * @param {Roo.HtmlEditorCore} this
25564          * @param {String} html
25565          */
25566         beforepush: true,
25567          /**
25568          * @event sync
25569          * Fires when the textarea is updated with content from the editor iframe.
25570          * @param {Roo.HtmlEditorCore} this
25571          * @param {String} html
25572          */
25573         sync: true,
25574          /**
25575          * @event push
25576          * Fires when the iframe editor is updated with content from the textarea.
25577          * @param {Roo.HtmlEditorCore} this
25578          * @param {String} html
25579          */
25580         push: true,
25581         
25582         /**
25583          * @event editorevent
25584          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25585          * @param {Roo.HtmlEditorCore} this
25586          */
25587         editorevent: true
25588         
25589     });
25590     
25591     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25592     
25593     // defaults : white / black...
25594     this.applyBlacklists();
25595     
25596     
25597     
25598 };
25599
25600
25601 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25602
25603
25604      /**
25605      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25606      */
25607     
25608     owner : false,
25609     
25610      /**
25611      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25612      *                        Roo.resizable.
25613      */
25614     resizable : false,
25615      /**
25616      * @cfg {Number} height (in pixels)
25617      */   
25618     height: 300,
25619    /**
25620      * @cfg {Number} width (in pixels)
25621      */   
25622     width: 500,
25623     
25624     /**
25625      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25626      * 
25627      */
25628     stylesheets: false,
25629     
25630     /**
25631      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25632      */
25633     allowComments: false,
25634     // id of frame..
25635     frameId: false,
25636     
25637     // private properties
25638     validationEvent : false,
25639     deferHeight: true,
25640     initialized : false,
25641     activated : false,
25642     sourceEditMode : false,
25643     onFocus : Roo.emptyFn,
25644     iframePad:3,
25645     hideMode:'offsets',
25646     
25647     clearUp: true,
25648     
25649     // blacklist + whitelisted elements..
25650     black: false,
25651     white: false,
25652      
25653     bodyCls : '',
25654
25655     /**
25656      * Protected method that will not generally be called directly. It
25657      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25658      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25659      */
25660     getDocMarkup : function(){
25661         // body styles..
25662         var st = '';
25663         
25664         // inherit styels from page...?? 
25665         if (this.stylesheets === false) {
25666             
25667             Roo.get(document.head).select('style').each(function(node) {
25668                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25669             });
25670             
25671             Roo.get(document.head).select('link').each(function(node) { 
25672                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25673             });
25674             
25675         } else if (!this.stylesheets.length) {
25676                 // simple..
25677                 st = '<style type="text/css">' +
25678                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25679                    '</style>';
25680         } else {
25681             for (var i in this.stylesheets) { 
25682                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25683             }
25684             
25685         }
25686         
25687         st +=  '<style type="text/css">' +
25688             'IMG { cursor: pointer } ' +
25689         '</style>';
25690
25691         var cls = 'roo-htmleditor-body';
25692         
25693         if(this.bodyCls.length){
25694             cls += ' ' + this.bodyCls;
25695         }
25696         
25697         return '<html><head>' + st  +
25698             //<style type="text/css">' +
25699             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25700             //'</style>' +
25701             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25702     },
25703
25704     // private
25705     onRender : function(ct, position)
25706     {
25707         var _t = this;
25708         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25709         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25710         
25711         
25712         this.el.dom.style.border = '0 none';
25713         this.el.dom.setAttribute('tabIndex', -1);
25714         this.el.addClass('x-hidden hide');
25715         
25716         
25717         
25718         if(Roo.isIE){ // fix IE 1px bogus margin
25719             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25720         }
25721        
25722         
25723         this.frameId = Roo.id();
25724         
25725          
25726         
25727         var iframe = this.owner.wrap.createChild({
25728             tag: 'iframe',
25729             cls: 'form-control', // bootstrap..
25730             id: this.frameId,
25731             name: this.frameId,
25732             frameBorder : 'no',
25733             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25734         }, this.el
25735         );
25736         
25737         
25738         this.iframe = iframe.dom;
25739
25740          this.assignDocWin();
25741         
25742         this.doc.designMode = 'on';
25743        
25744         this.doc.open();
25745         this.doc.write(this.getDocMarkup());
25746         this.doc.close();
25747
25748         
25749         var task = { // must defer to wait for browser to be ready
25750             run : function(){
25751                 //console.log("run task?" + this.doc.readyState);
25752                 this.assignDocWin();
25753                 if(this.doc.body || this.doc.readyState == 'complete'){
25754                     try {
25755                         this.doc.designMode="on";
25756                     } catch (e) {
25757                         return;
25758                     }
25759                     Roo.TaskMgr.stop(task);
25760                     this.initEditor.defer(10, this);
25761                 }
25762             },
25763             interval : 10,
25764             duration: 10000,
25765             scope: this
25766         };
25767         Roo.TaskMgr.start(task);
25768
25769     },
25770
25771     // private
25772     onResize : function(w, h)
25773     {
25774          Roo.log('resize: ' +w + ',' + h );
25775         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25776         if(!this.iframe){
25777             return;
25778         }
25779         if(typeof w == 'number'){
25780             
25781             this.iframe.style.width = w + 'px';
25782         }
25783         if(typeof h == 'number'){
25784             
25785             this.iframe.style.height = h + 'px';
25786             if(this.doc){
25787                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25788             }
25789         }
25790         
25791     },
25792
25793     /**
25794      * Toggles the editor between standard and source edit mode.
25795      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25796      */
25797     toggleSourceEdit : function(sourceEditMode){
25798         
25799         this.sourceEditMode = sourceEditMode === true;
25800         
25801         if(this.sourceEditMode){
25802  
25803             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25804             
25805         }else{
25806             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25807             //this.iframe.className = '';
25808             this.deferFocus();
25809         }
25810         //this.setSize(this.owner.wrap.getSize());
25811         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25812     },
25813
25814     
25815   
25816
25817     /**
25818      * Protected method that will not generally be called directly. If you need/want
25819      * custom HTML cleanup, this is the method you should override.
25820      * @param {String} html The HTML to be cleaned
25821      * return {String} The cleaned HTML
25822      */
25823     cleanHtml : function(html){
25824         html = String(html);
25825         if(html.length > 5){
25826             if(Roo.isSafari){ // strip safari nonsense
25827                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25828             }
25829         }
25830         if(html == '&nbsp;'){
25831             html = '';
25832         }
25833         return html;
25834     },
25835
25836     /**
25837      * HTML Editor -> Textarea
25838      * Protected method that will not generally be called directly. Syncs the contents
25839      * of the editor iframe with the textarea.
25840      */
25841     syncValue : function(){
25842         if(this.initialized){
25843             var bd = (this.doc.body || this.doc.documentElement);
25844             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25845             var html = bd.innerHTML;
25846             if(Roo.isSafari){
25847                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25848                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25849                 if(m && m[1]){
25850                     html = '<div style="'+m[0]+'">' + html + '</div>';
25851                 }
25852             }
25853             html = this.cleanHtml(html);
25854             // fix up the special chars.. normaly like back quotes in word...
25855             // however we do not want to do this with chinese..
25856             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25857                 
25858                 var cc = match.charCodeAt();
25859
25860                 // Get the character value, handling surrogate pairs
25861                 if (match.length == 2) {
25862                     // It's a surrogate pair, calculate the Unicode code point
25863                     var high = match.charCodeAt(0) - 0xD800;
25864                     var low  = match.charCodeAt(1) - 0xDC00;
25865                     cc = (high * 0x400) + low + 0x10000;
25866                 }  else if (
25867                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25868                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25869                     (cc >= 0xf900 && cc < 0xfb00 )
25870                 ) {
25871                         return match;
25872                 }  
25873          
25874                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25875                 return "&#" + cc + ";";
25876                 
25877                 
25878             });
25879             
25880             
25881              
25882             if(this.owner.fireEvent('beforesync', this, html) !== false){
25883                 this.el.dom.value = html;
25884                 this.owner.fireEvent('sync', this, html);
25885             }
25886         }
25887     },
25888
25889     /**
25890      * Protected method that will not generally be called directly. Pushes the value of the textarea
25891      * into the iframe editor.
25892      */
25893     pushValue : function(){
25894         if(this.initialized){
25895             var v = this.el.dom.value.trim();
25896             
25897 //            if(v.length < 1){
25898 //                v = '&#160;';
25899 //            }
25900             
25901             if(this.owner.fireEvent('beforepush', this, v) !== false){
25902                 var d = (this.doc.body || this.doc.documentElement);
25903                 d.innerHTML = v;
25904                 this.cleanUpPaste();
25905                 this.el.dom.value = d.innerHTML;
25906                 this.owner.fireEvent('push', this, v);
25907             }
25908         }
25909     },
25910
25911     // private
25912     deferFocus : function(){
25913         this.focus.defer(10, this);
25914     },
25915
25916     // doc'ed in Field
25917     focus : function(){
25918         if(this.win && !this.sourceEditMode){
25919             this.win.focus();
25920         }else{
25921             this.el.focus();
25922         }
25923     },
25924     
25925     assignDocWin: function()
25926     {
25927         var iframe = this.iframe;
25928         
25929          if(Roo.isIE){
25930             this.doc = iframe.contentWindow.document;
25931             this.win = iframe.contentWindow;
25932         } else {
25933 //            if (!Roo.get(this.frameId)) {
25934 //                return;
25935 //            }
25936 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25937 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25938             
25939             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25940                 return;
25941             }
25942             
25943             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25944             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25945         }
25946     },
25947     
25948     // private
25949     initEditor : function(){
25950         //console.log("INIT EDITOR");
25951         this.assignDocWin();
25952         
25953         
25954         
25955         this.doc.designMode="on";
25956         this.doc.open();
25957         this.doc.write(this.getDocMarkup());
25958         this.doc.close();
25959         
25960         var dbody = (this.doc.body || this.doc.documentElement);
25961         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25962         // this copies styles from the containing element into thsi one..
25963         // not sure why we need all of this..
25964         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25965         
25966         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25967         //ss['background-attachment'] = 'fixed'; // w3c
25968         dbody.bgProperties = 'fixed'; // ie
25969         //Roo.DomHelper.applyStyles(dbody, ss);
25970         Roo.EventManager.on(this.doc, {
25971             //'mousedown': this.onEditorEvent,
25972             'mouseup': this.onEditorEvent,
25973             'dblclick': this.onEditorEvent,
25974             'click': this.onEditorEvent,
25975             'keyup': this.onEditorEvent,
25976             buffer:100,
25977             scope: this
25978         });
25979         if(Roo.isGecko){
25980             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25981         }
25982         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25983             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25984         }
25985         this.initialized = true;
25986
25987         this.owner.fireEvent('initialize', this);
25988         this.pushValue();
25989     },
25990
25991     // private
25992     onDestroy : function(){
25993         
25994         
25995         
25996         if(this.rendered){
25997             
25998             //for (var i =0; i < this.toolbars.length;i++) {
25999             //    // fixme - ask toolbars for heights?
26000             //    this.toolbars[i].onDestroy();
26001            // }
26002             
26003             //this.wrap.dom.innerHTML = '';
26004             //this.wrap.remove();
26005         }
26006     },
26007
26008     // private
26009     onFirstFocus : function(){
26010         
26011         this.assignDocWin();
26012         
26013         
26014         this.activated = true;
26015          
26016     
26017         if(Roo.isGecko){ // prevent silly gecko errors
26018             this.win.focus();
26019             var s = this.win.getSelection();
26020             if(!s.focusNode || s.focusNode.nodeType != 3){
26021                 var r = s.getRangeAt(0);
26022                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26023                 r.collapse(true);
26024                 this.deferFocus();
26025             }
26026             try{
26027                 this.execCmd('useCSS', true);
26028                 this.execCmd('styleWithCSS', false);
26029             }catch(e){}
26030         }
26031         this.owner.fireEvent('activate', this);
26032     },
26033
26034     // private
26035     adjustFont: function(btn){
26036         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26037         //if(Roo.isSafari){ // safari
26038         //    adjust *= 2;
26039        // }
26040         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26041         if(Roo.isSafari){ // safari
26042             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26043             v =  (v < 10) ? 10 : v;
26044             v =  (v > 48) ? 48 : v;
26045             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26046             
26047         }
26048         
26049         
26050         v = Math.max(1, v+adjust);
26051         
26052         this.execCmd('FontSize', v  );
26053     },
26054
26055     onEditorEvent : function(e)
26056     {
26057         this.owner.fireEvent('editorevent', this, e);
26058       //  this.updateToolbar();
26059         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26060     },
26061
26062     insertTag : function(tg)
26063     {
26064         // could be a bit smarter... -> wrap the current selected tRoo..
26065         if (tg.toLowerCase() == 'span' ||
26066             tg.toLowerCase() == 'code' ||
26067             tg.toLowerCase() == 'sup' ||
26068             tg.toLowerCase() == 'sub' 
26069             ) {
26070             
26071             range = this.createRange(this.getSelection());
26072             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26073             wrappingNode.appendChild(range.extractContents());
26074             range.insertNode(wrappingNode);
26075
26076             return;
26077             
26078             
26079             
26080         }
26081         this.execCmd("formatblock",   tg);
26082         
26083     },
26084     
26085     insertText : function(txt)
26086     {
26087         
26088         
26089         var range = this.createRange();
26090         range.deleteContents();
26091                //alert(Sender.getAttribute('label'));
26092                
26093         range.insertNode(this.doc.createTextNode(txt));
26094     } ,
26095     
26096      
26097
26098     /**
26099      * Executes a Midas editor command on the editor document and performs necessary focus and
26100      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26101      * @param {String} cmd The Midas command
26102      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26103      */
26104     relayCmd : function(cmd, value){
26105         this.win.focus();
26106         this.execCmd(cmd, value);
26107         this.owner.fireEvent('editorevent', this);
26108         //this.updateToolbar();
26109         this.owner.deferFocus();
26110     },
26111
26112     /**
26113      * Executes a Midas editor command directly on the editor document.
26114      * For visual commands, you should use {@link #relayCmd} instead.
26115      * <b>This should only be called after the editor is initialized.</b>
26116      * @param {String} cmd The Midas command
26117      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26118      */
26119     execCmd : function(cmd, value){
26120         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26121         this.syncValue();
26122     },
26123  
26124  
26125    
26126     /**
26127      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26128      * to insert tRoo.
26129      * @param {String} text | dom node.. 
26130      */
26131     insertAtCursor : function(text)
26132     {
26133         
26134         if(!this.activated){
26135             return;
26136         }
26137         /*
26138         if(Roo.isIE){
26139             this.win.focus();
26140             var r = this.doc.selection.createRange();
26141             if(r){
26142                 r.collapse(true);
26143                 r.pasteHTML(text);
26144                 this.syncValue();
26145                 this.deferFocus();
26146             
26147             }
26148             return;
26149         }
26150         */
26151         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26152             this.win.focus();
26153             
26154             
26155             // from jquery ui (MIT licenced)
26156             var range, node;
26157             var win = this.win;
26158             
26159             if (win.getSelection && win.getSelection().getRangeAt) {
26160                 range = win.getSelection().getRangeAt(0);
26161                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26162                 range.insertNode(node);
26163             } else if (win.document.selection && win.document.selection.createRange) {
26164                 // no firefox support
26165                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26166                 win.document.selection.createRange().pasteHTML(txt);
26167             } else {
26168                 // no firefox support
26169                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26170                 this.execCmd('InsertHTML', txt);
26171             } 
26172             
26173             this.syncValue();
26174             
26175             this.deferFocus();
26176         }
26177     },
26178  // private
26179     mozKeyPress : function(e){
26180         if(e.ctrlKey){
26181             var c = e.getCharCode(), cmd;
26182           
26183             if(c > 0){
26184                 c = String.fromCharCode(c).toLowerCase();
26185                 switch(c){
26186                     case 'b':
26187                         cmd = 'bold';
26188                         break;
26189                     case 'i':
26190                         cmd = 'italic';
26191                         break;
26192                     
26193                     case 'u':
26194                         cmd = 'underline';
26195                         break;
26196                     
26197                     case 'v':
26198                         this.cleanUpPaste.defer(100, this);
26199                         return;
26200                         
26201                 }
26202                 if(cmd){
26203                     this.win.focus();
26204                     this.execCmd(cmd);
26205                     this.deferFocus();
26206                     e.preventDefault();
26207                 }
26208                 
26209             }
26210         }
26211     },
26212
26213     // private
26214     fixKeys : function(){ // load time branching for fastest keydown performance
26215         if(Roo.isIE){
26216             return function(e){
26217                 var k = e.getKey(), r;
26218                 if(k == e.TAB){
26219                     e.stopEvent();
26220                     r = this.doc.selection.createRange();
26221                     if(r){
26222                         r.collapse(true);
26223                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26224                         this.deferFocus();
26225                     }
26226                     return;
26227                 }
26228                 
26229                 if(k == e.ENTER){
26230                     r = this.doc.selection.createRange();
26231                     if(r){
26232                         var target = r.parentElement();
26233                         if(!target || target.tagName.toLowerCase() != 'li'){
26234                             e.stopEvent();
26235                             r.pasteHTML('<br />');
26236                             r.collapse(false);
26237                             r.select();
26238                         }
26239                     }
26240                 }
26241                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26242                     this.cleanUpPaste.defer(100, this);
26243                     return;
26244                 }
26245                 
26246                 
26247             };
26248         }else if(Roo.isOpera){
26249             return function(e){
26250                 var k = e.getKey();
26251                 if(k == e.TAB){
26252                     e.stopEvent();
26253                     this.win.focus();
26254                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26255                     this.deferFocus();
26256                 }
26257                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26258                     this.cleanUpPaste.defer(100, this);
26259                     return;
26260                 }
26261                 
26262             };
26263         }else if(Roo.isSafari){
26264             return function(e){
26265                 var k = e.getKey();
26266                 
26267                 if(k == e.TAB){
26268                     e.stopEvent();
26269                     this.execCmd('InsertText','\t');
26270                     this.deferFocus();
26271                     return;
26272                 }
26273                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26274                     this.cleanUpPaste.defer(100, this);
26275                     return;
26276                 }
26277                 
26278              };
26279         }
26280     }(),
26281     
26282     getAllAncestors: function()
26283     {
26284         var p = this.getSelectedNode();
26285         var a = [];
26286         if (!p) {
26287             a.push(p); // push blank onto stack..
26288             p = this.getParentElement();
26289         }
26290         
26291         
26292         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26293             a.push(p);
26294             p = p.parentNode;
26295         }
26296         a.push(this.doc.body);
26297         return a;
26298     },
26299     lastSel : false,
26300     lastSelNode : false,
26301     
26302     
26303     getSelection : function() 
26304     {
26305         this.assignDocWin();
26306         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26307     },
26308     
26309     getSelectedNode: function() 
26310     {
26311         // this may only work on Gecko!!!
26312         
26313         // should we cache this!!!!
26314         
26315         
26316         
26317          
26318         var range = this.createRange(this.getSelection()).cloneRange();
26319         
26320         if (Roo.isIE) {
26321             var parent = range.parentElement();
26322             while (true) {
26323                 var testRange = range.duplicate();
26324                 testRange.moveToElementText(parent);
26325                 if (testRange.inRange(range)) {
26326                     break;
26327                 }
26328                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26329                     break;
26330                 }
26331                 parent = parent.parentElement;
26332             }
26333             return parent;
26334         }
26335         
26336         // is ancestor a text element.
26337         var ac =  range.commonAncestorContainer;
26338         if (ac.nodeType == 3) {
26339             ac = ac.parentNode;
26340         }
26341         
26342         var ar = ac.childNodes;
26343          
26344         var nodes = [];
26345         var other_nodes = [];
26346         var has_other_nodes = false;
26347         for (var i=0;i<ar.length;i++) {
26348             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26349                 continue;
26350             }
26351             // fullly contained node.
26352             
26353             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26354                 nodes.push(ar[i]);
26355                 continue;
26356             }
26357             
26358             // probably selected..
26359             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26360                 other_nodes.push(ar[i]);
26361                 continue;
26362             }
26363             // outer..
26364             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26365                 continue;
26366             }
26367             
26368             
26369             has_other_nodes = true;
26370         }
26371         if (!nodes.length && other_nodes.length) {
26372             nodes= other_nodes;
26373         }
26374         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26375             return false;
26376         }
26377         
26378         return nodes[0];
26379     },
26380     createRange: function(sel)
26381     {
26382         // this has strange effects when using with 
26383         // top toolbar - not sure if it's a great idea.
26384         //this.editor.contentWindow.focus();
26385         if (typeof sel != "undefined") {
26386             try {
26387                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26388             } catch(e) {
26389                 return this.doc.createRange();
26390             }
26391         } else {
26392             return this.doc.createRange();
26393         }
26394     },
26395     getParentElement: function()
26396     {
26397         
26398         this.assignDocWin();
26399         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26400         
26401         var range = this.createRange(sel);
26402          
26403         try {
26404             var p = range.commonAncestorContainer;
26405             while (p.nodeType == 3) { // text node
26406                 p = p.parentNode;
26407             }
26408             return p;
26409         } catch (e) {
26410             return null;
26411         }
26412     
26413     },
26414     /***
26415      *
26416      * Range intersection.. the hard stuff...
26417      *  '-1' = before
26418      *  '0' = hits..
26419      *  '1' = after.
26420      *         [ -- selected range --- ]
26421      *   [fail]                        [fail]
26422      *
26423      *    basically..
26424      *      if end is before start or  hits it. fail.
26425      *      if start is after end or hits it fail.
26426      *
26427      *   if either hits (but other is outside. - then it's not 
26428      *   
26429      *    
26430      **/
26431     
26432     
26433     // @see http://www.thismuchiknow.co.uk/?p=64.
26434     rangeIntersectsNode : function(range, node)
26435     {
26436         var nodeRange = node.ownerDocument.createRange();
26437         try {
26438             nodeRange.selectNode(node);
26439         } catch (e) {
26440             nodeRange.selectNodeContents(node);
26441         }
26442     
26443         var rangeStartRange = range.cloneRange();
26444         rangeStartRange.collapse(true);
26445     
26446         var rangeEndRange = range.cloneRange();
26447         rangeEndRange.collapse(false);
26448     
26449         var nodeStartRange = nodeRange.cloneRange();
26450         nodeStartRange.collapse(true);
26451     
26452         var nodeEndRange = nodeRange.cloneRange();
26453         nodeEndRange.collapse(false);
26454     
26455         return rangeStartRange.compareBoundaryPoints(
26456                  Range.START_TO_START, nodeEndRange) == -1 &&
26457                rangeEndRange.compareBoundaryPoints(
26458                  Range.START_TO_START, nodeStartRange) == 1;
26459         
26460          
26461     },
26462     rangeCompareNode : function(range, node)
26463     {
26464         var nodeRange = node.ownerDocument.createRange();
26465         try {
26466             nodeRange.selectNode(node);
26467         } catch (e) {
26468             nodeRange.selectNodeContents(node);
26469         }
26470         
26471         
26472         range.collapse(true);
26473     
26474         nodeRange.collapse(true);
26475      
26476         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26477         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26478          
26479         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26480         
26481         var nodeIsBefore   =  ss == 1;
26482         var nodeIsAfter    = ee == -1;
26483         
26484         if (nodeIsBefore && nodeIsAfter) {
26485             return 0; // outer
26486         }
26487         if (!nodeIsBefore && nodeIsAfter) {
26488             return 1; //right trailed.
26489         }
26490         
26491         if (nodeIsBefore && !nodeIsAfter) {
26492             return 2;  // left trailed.
26493         }
26494         // fully contined.
26495         return 3;
26496     },
26497
26498     // private? - in a new class?
26499     cleanUpPaste :  function()
26500     {
26501         // cleans up the whole document..
26502         Roo.log('cleanuppaste');
26503         
26504         this.cleanUpChildren(this.doc.body);
26505         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26506         if (clean != this.doc.body.innerHTML) {
26507             this.doc.body.innerHTML = clean;
26508         }
26509         
26510     },
26511     
26512     cleanWordChars : function(input) {// change the chars to hex code
26513         var he = Roo.HtmlEditorCore;
26514         
26515         var output = input;
26516         Roo.each(he.swapCodes, function(sw) { 
26517             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26518             
26519             output = output.replace(swapper, sw[1]);
26520         });
26521         
26522         return output;
26523     },
26524     
26525     
26526     cleanUpChildren : function (n)
26527     {
26528         if (!n.childNodes.length) {
26529             return;
26530         }
26531         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26532            this.cleanUpChild(n.childNodes[i]);
26533         }
26534     },
26535     
26536     
26537         
26538     
26539     cleanUpChild : function (node)
26540     {
26541         var ed = this;
26542         //console.log(node);
26543         if (node.nodeName == "#text") {
26544             // clean up silly Windows -- stuff?
26545             return; 
26546         }
26547         if (node.nodeName == "#comment") {
26548             if (!this.allowComments) {
26549                 node.parentNode.removeChild(node);
26550             }
26551             // clean up silly Windows -- stuff?
26552             return; 
26553         }
26554         var lcname = node.tagName.toLowerCase();
26555         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26556         // whitelist of tags..
26557         
26558         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26559             // remove node.
26560             node.parentNode.removeChild(node);
26561             return;
26562             
26563         }
26564         
26565         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26566         
26567         // spans with no attributes - just remove them..
26568         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26569             remove_keep_children = true;
26570         }
26571         
26572         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26573         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26574         
26575         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26576         //    remove_keep_children = true;
26577         //}
26578         
26579         if (remove_keep_children) {
26580             this.cleanUpChildren(node);
26581             // inserts everything just before this node...
26582             while (node.childNodes.length) {
26583                 var cn = node.childNodes[0];
26584                 node.removeChild(cn);
26585                 node.parentNode.insertBefore(cn, node);
26586             }
26587             node.parentNode.removeChild(node);
26588             return;
26589         }
26590         
26591         if (!node.attributes || !node.attributes.length) {
26592             
26593           
26594             
26595             
26596             this.cleanUpChildren(node);
26597             return;
26598         }
26599         
26600         function cleanAttr(n,v)
26601         {
26602             
26603             if (v.match(/^\./) || v.match(/^\//)) {
26604                 return;
26605             }
26606             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26607                 return;
26608             }
26609             if (v.match(/^#/)) {
26610                 return;
26611             }
26612             if (v.match(/^\{/)) { // allow template editing.
26613                 return;
26614             }
26615 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26616             node.removeAttribute(n);
26617             
26618         }
26619         
26620         var cwhite = this.cwhite;
26621         var cblack = this.cblack;
26622             
26623         function cleanStyle(n,v)
26624         {
26625             if (v.match(/expression/)) { //XSS?? should we even bother..
26626                 node.removeAttribute(n);
26627                 return;
26628             }
26629             
26630             var parts = v.split(/;/);
26631             var clean = [];
26632             
26633             Roo.each(parts, function(p) {
26634                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26635                 if (!p.length) {
26636                     return true;
26637                 }
26638                 var l = p.split(':').shift().replace(/\s+/g,'');
26639                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26640                 
26641                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26642 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26643                     //node.removeAttribute(n);
26644                     return true;
26645                 }
26646                 //Roo.log()
26647                 // only allow 'c whitelisted system attributes'
26648                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26649 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26650                     //node.removeAttribute(n);
26651                     return true;
26652                 }
26653                 
26654                 
26655                  
26656                 
26657                 clean.push(p);
26658                 return true;
26659             });
26660             if (clean.length) { 
26661                 node.setAttribute(n, clean.join(';'));
26662             } else {
26663                 node.removeAttribute(n);
26664             }
26665             
26666         }
26667         
26668         
26669         for (var i = node.attributes.length-1; i > -1 ; i--) {
26670             var a = node.attributes[i];
26671             //console.log(a);
26672             
26673             if (a.name.toLowerCase().substr(0,2)=='on')  {
26674                 node.removeAttribute(a.name);
26675                 continue;
26676             }
26677             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26678                 node.removeAttribute(a.name);
26679                 continue;
26680             }
26681             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26682                 cleanAttr(a.name,a.value); // fixme..
26683                 continue;
26684             }
26685             if (a.name == 'style') {
26686                 cleanStyle(a.name,a.value);
26687                 continue;
26688             }
26689             /// clean up MS crap..
26690             // tecnically this should be a list of valid class'es..
26691             
26692             
26693             if (a.name == 'class') {
26694                 if (a.value.match(/^Mso/)) {
26695                     node.removeAttribute('class');
26696                 }
26697                 
26698                 if (a.value.match(/^body$/)) {
26699                     node.removeAttribute('class');
26700                 }
26701                 continue;
26702             }
26703             
26704             // style cleanup!?
26705             // class cleanup?
26706             
26707         }
26708         
26709         
26710         this.cleanUpChildren(node);
26711         
26712         
26713     },
26714     
26715     /**
26716      * Clean up MS wordisms...
26717      */
26718     cleanWord : function(node)
26719     {
26720         if (!node) {
26721             this.cleanWord(this.doc.body);
26722             return;
26723         }
26724         
26725         if(
26726                 node.nodeName == 'SPAN' &&
26727                 !node.hasAttributes() &&
26728                 node.childNodes.length == 1 &&
26729                 node.firstChild.nodeName == "#text"  
26730         ) {
26731             var textNode = node.firstChild;
26732             node.removeChild(textNode);
26733             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26734                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26735             }
26736             node.parentNode.insertBefore(textNode, node);
26737             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26738                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26739             }
26740             node.parentNode.removeChild(node);
26741         }
26742         
26743         if (node.nodeName == "#text") {
26744             // clean up silly Windows -- stuff?
26745             return; 
26746         }
26747         if (node.nodeName == "#comment") {
26748             node.parentNode.removeChild(node);
26749             // clean up silly Windows -- stuff?
26750             return; 
26751         }
26752         
26753         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26754             node.parentNode.removeChild(node);
26755             return;
26756         }
26757         //Roo.log(node.tagName);
26758         // remove - but keep children..
26759         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26760             //Roo.log('-- removed');
26761             while (node.childNodes.length) {
26762                 var cn = node.childNodes[0];
26763                 node.removeChild(cn);
26764                 node.parentNode.insertBefore(cn, node);
26765                 // move node to parent - and clean it..
26766                 this.cleanWord(cn);
26767             }
26768             node.parentNode.removeChild(node);
26769             /// no need to iterate chidlren = it's got none..
26770             //this.iterateChildren(node, this.cleanWord);
26771             return;
26772         }
26773         // clean styles
26774         if (node.className.length) {
26775             
26776             var cn = node.className.split(/\W+/);
26777             var cna = [];
26778             Roo.each(cn, function(cls) {
26779                 if (cls.match(/Mso[a-zA-Z]+/)) {
26780                     return;
26781                 }
26782                 cna.push(cls);
26783             });
26784             node.className = cna.length ? cna.join(' ') : '';
26785             if (!cna.length) {
26786                 node.removeAttribute("class");
26787             }
26788         }
26789         
26790         if (node.hasAttribute("lang")) {
26791             node.removeAttribute("lang");
26792         }
26793         
26794         if (node.hasAttribute("style")) {
26795             
26796             var styles = node.getAttribute("style").split(";");
26797             var nstyle = [];
26798             Roo.each(styles, function(s) {
26799                 if (!s.match(/:/)) {
26800                     return;
26801                 }
26802                 var kv = s.split(":");
26803                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26804                     return;
26805                 }
26806                 // what ever is left... we allow.
26807                 nstyle.push(s);
26808             });
26809             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26810             if (!nstyle.length) {
26811                 node.removeAttribute('style');
26812             }
26813         }
26814         this.iterateChildren(node, this.cleanWord);
26815         
26816         
26817         
26818     },
26819     /**
26820      * iterateChildren of a Node, calling fn each time, using this as the scole..
26821      * @param {DomNode} node node to iterate children of.
26822      * @param {Function} fn method of this class to call on each item.
26823      */
26824     iterateChildren : function(node, fn)
26825     {
26826         if (!node.childNodes.length) {
26827                 return;
26828         }
26829         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26830            fn.call(this, node.childNodes[i])
26831         }
26832     },
26833     
26834     
26835     /**
26836      * cleanTableWidths.
26837      *
26838      * Quite often pasting from word etc.. results in tables with column and widths.
26839      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26840      *
26841      */
26842     cleanTableWidths : function(node)
26843     {
26844          
26845          
26846         if (!node) {
26847             this.cleanTableWidths(this.doc.body);
26848             return;
26849         }
26850         
26851         // ignore list...
26852         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26853             return; 
26854         }
26855         Roo.log(node.tagName);
26856         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26857             this.iterateChildren(node, this.cleanTableWidths);
26858             return;
26859         }
26860         if (node.hasAttribute('width')) {
26861             node.removeAttribute('width');
26862         }
26863         
26864          
26865         if (node.hasAttribute("style")) {
26866             // pretty basic...
26867             
26868             var styles = node.getAttribute("style").split(";");
26869             var nstyle = [];
26870             Roo.each(styles, function(s) {
26871                 if (!s.match(/:/)) {
26872                     return;
26873                 }
26874                 var kv = s.split(":");
26875                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26876                     return;
26877                 }
26878                 // what ever is left... we allow.
26879                 nstyle.push(s);
26880             });
26881             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26882             if (!nstyle.length) {
26883                 node.removeAttribute('style');
26884             }
26885         }
26886         
26887         this.iterateChildren(node, this.cleanTableWidths);
26888         
26889         
26890     },
26891     
26892     
26893     
26894     
26895     domToHTML : function(currentElement, depth, nopadtext) {
26896         
26897         depth = depth || 0;
26898         nopadtext = nopadtext || false;
26899     
26900         if (!currentElement) {
26901             return this.domToHTML(this.doc.body);
26902         }
26903         
26904         //Roo.log(currentElement);
26905         var j;
26906         var allText = false;
26907         var nodeName = currentElement.nodeName;
26908         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26909         
26910         if  (nodeName == '#text') {
26911             
26912             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26913         }
26914         
26915         
26916         var ret = '';
26917         if (nodeName != 'BODY') {
26918              
26919             var i = 0;
26920             // Prints the node tagName, such as <A>, <IMG>, etc
26921             if (tagName) {
26922                 var attr = [];
26923                 for(i = 0; i < currentElement.attributes.length;i++) {
26924                     // quoting?
26925                     var aname = currentElement.attributes.item(i).name;
26926                     if (!currentElement.attributes.item(i).value.length) {
26927                         continue;
26928                     }
26929                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26930                 }
26931                 
26932                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26933             } 
26934             else {
26935                 
26936                 // eack
26937             }
26938         } else {
26939             tagName = false;
26940         }
26941         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26942             return ret;
26943         }
26944         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26945             nopadtext = true;
26946         }
26947         
26948         
26949         // Traverse the tree
26950         i = 0;
26951         var currentElementChild = currentElement.childNodes.item(i);
26952         var allText = true;
26953         var innerHTML  = '';
26954         lastnode = '';
26955         while (currentElementChild) {
26956             // Formatting code (indent the tree so it looks nice on the screen)
26957             var nopad = nopadtext;
26958             if (lastnode == 'SPAN') {
26959                 nopad  = true;
26960             }
26961             // text
26962             if  (currentElementChild.nodeName == '#text') {
26963                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26964                 toadd = nopadtext ? toadd : toadd.trim();
26965                 if (!nopad && toadd.length > 80) {
26966                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26967                 }
26968                 innerHTML  += toadd;
26969                 
26970                 i++;
26971                 currentElementChild = currentElement.childNodes.item(i);
26972                 lastNode = '';
26973                 continue;
26974             }
26975             allText = false;
26976             
26977             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26978                 
26979             // Recursively traverse the tree structure of the child node
26980             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26981             lastnode = currentElementChild.nodeName;
26982             i++;
26983             currentElementChild=currentElement.childNodes.item(i);
26984         }
26985         
26986         ret += innerHTML;
26987         
26988         if (!allText) {
26989                 // The remaining code is mostly for formatting the tree
26990             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26991         }
26992         
26993         
26994         if (tagName) {
26995             ret+= "</"+tagName+">";
26996         }
26997         return ret;
26998         
26999     },
27000         
27001     applyBlacklists : function()
27002     {
27003         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27004         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27005         
27006         this.white = [];
27007         this.black = [];
27008         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27009             if (b.indexOf(tag) > -1) {
27010                 return;
27011             }
27012             this.white.push(tag);
27013             
27014         }, this);
27015         
27016         Roo.each(w, function(tag) {
27017             if (b.indexOf(tag) > -1) {
27018                 return;
27019             }
27020             if (this.white.indexOf(tag) > -1) {
27021                 return;
27022             }
27023             this.white.push(tag);
27024             
27025         }, this);
27026         
27027         
27028         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27029             if (w.indexOf(tag) > -1) {
27030                 return;
27031             }
27032             this.black.push(tag);
27033             
27034         }, this);
27035         
27036         Roo.each(b, function(tag) {
27037             if (w.indexOf(tag) > -1) {
27038                 return;
27039             }
27040             if (this.black.indexOf(tag) > -1) {
27041                 return;
27042             }
27043             this.black.push(tag);
27044             
27045         }, this);
27046         
27047         
27048         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27049         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27050         
27051         this.cwhite = [];
27052         this.cblack = [];
27053         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27054             if (b.indexOf(tag) > -1) {
27055                 return;
27056             }
27057             this.cwhite.push(tag);
27058             
27059         }, this);
27060         
27061         Roo.each(w, function(tag) {
27062             if (b.indexOf(tag) > -1) {
27063                 return;
27064             }
27065             if (this.cwhite.indexOf(tag) > -1) {
27066                 return;
27067             }
27068             this.cwhite.push(tag);
27069             
27070         }, this);
27071         
27072         
27073         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27074             if (w.indexOf(tag) > -1) {
27075                 return;
27076             }
27077             this.cblack.push(tag);
27078             
27079         }, this);
27080         
27081         Roo.each(b, function(tag) {
27082             if (w.indexOf(tag) > -1) {
27083                 return;
27084             }
27085             if (this.cblack.indexOf(tag) > -1) {
27086                 return;
27087             }
27088             this.cblack.push(tag);
27089             
27090         }, this);
27091     },
27092     
27093     setStylesheets : function(stylesheets)
27094     {
27095         if(typeof(stylesheets) == 'string'){
27096             Roo.get(this.iframe.contentDocument.head).createChild({
27097                 tag : 'link',
27098                 rel : 'stylesheet',
27099                 type : 'text/css',
27100                 href : stylesheets
27101             });
27102             
27103             return;
27104         }
27105         var _this = this;
27106      
27107         Roo.each(stylesheets, function(s) {
27108             if(!s.length){
27109                 return;
27110             }
27111             
27112             Roo.get(_this.iframe.contentDocument.head).createChild({
27113                 tag : 'link',
27114                 rel : 'stylesheet',
27115                 type : 'text/css',
27116                 href : s
27117             });
27118         });
27119
27120         
27121     },
27122     
27123     removeStylesheets : function()
27124     {
27125         var _this = this;
27126         
27127         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27128             s.remove();
27129         });
27130     },
27131     
27132     setStyle : function(style)
27133     {
27134         Roo.get(this.iframe.contentDocument.head).createChild({
27135             tag : 'style',
27136             type : 'text/css',
27137             html : style
27138         });
27139
27140         return;
27141     }
27142     
27143     // hide stuff that is not compatible
27144     /**
27145      * @event blur
27146      * @hide
27147      */
27148     /**
27149      * @event change
27150      * @hide
27151      */
27152     /**
27153      * @event focus
27154      * @hide
27155      */
27156     /**
27157      * @event specialkey
27158      * @hide
27159      */
27160     /**
27161      * @cfg {String} fieldClass @hide
27162      */
27163     /**
27164      * @cfg {String} focusClass @hide
27165      */
27166     /**
27167      * @cfg {String} autoCreate @hide
27168      */
27169     /**
27170      * @cfg {String} inputType @hide
27171      */
27172     /**
27173      * @cfg {String} invalidClass @hide
27174      */
27175     /**
27176      * @cfg {String} invalidText @hide
27177      */
27178     /**
27179      * @cfg {String} msgFx @hide
27180      */
27181     /**
27182      * @cfg {String} validateOnBlur @hide
27183      */
27184 });
27185
27186 Roo.HtmlEditorCore.white = [
27187         'area', 'br', 'img', 'input', 'hr', 'wbr',
27188         
27189        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27190        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27191        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27192        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27193        'table',   'ul',         'xmp', 
27194        
27195        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27196       'thead',   'tr', 
27197      
27198       'dir', 'menu', 'ol', 'ul', 'dl',
27199        
27200       'embed',  'object'
27201 ];
27202
27203
27204 Roo.HtmlEditorCore.black = [
27205     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27206         'applet', // 
27207         'base',   'basefont', 'bgsound', 'blink',  'body', 
27208         'frame',  'frameset', 'head',    'html',   'ilayer', 
27209         'iframe', 'layer',  'link',     'meta',    'object',   
27210         'script', 'style' ,'title',  'xml' // clean later..
27211 ];
27212 Roo.HtmlEditorCore.clean = [
27213     'script', 'style', 'title', 'xml'
27214 ];
27215 Roo.HtmlEditorCore.remove = [
27216     'font'
27217 ];
27218 // attributes..
27219
27220 Roo.HtmlEditorCore.ablack = [
27221     'on'
27222 ];
27223     
27224 Roo.HtmlEditorCore.aclean = [ 
27225     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27226 ];
27227
27228 // protocols..
27229 Roo.HtmlEditorCore.pwhite= [
27230         'http',  'https',  'mailto'
27231 ];
27232
27233 // white listed style attributes.
27234 Roo.HtmlEditorCore.cwhite= [
27235       //  'text-align', /// default is to allow most things..
27236       
27237          
27238 //        'font-size'//??
27239 ];
27240
27241 // black listed style attributes.
27242 Roo.HtmlEditorCore.cblack= [
27243       //  'font-size' -- this can be set by the project 
27244 ];
27245
27246
27247 Roo.HtmlEditorCore.swapCodes   =[ 
27248     [    8211, "&#8211;" ], 
27249     [    8212, "&#8212;" ], 
27250     [    8216,  "'" ],  
27251     [    8217, "'" ],  
27252     [    8220, '"' ],  
27253     [    8221, '"' ],  
27254     [    8226, "*" ],  
27255     [    8230, "..." ]
27256 ]; 
27257
27258     /*
27259  * - LGPL
27260  *
27261  * HtmlEditor
27262  * 
27263  */
27264
27265 /**
27266  * @class Roo.bootstrap.HtmlEditor
27267  * @extends Roo.bootstrap.TextArea
27268  * Bootstrap HtmlEditor class
27269
27270  * @constructor
27271  * Create a new HtmlEditor
27272  * @param {Object} config The config object
27273  */
27274
27275 Roo.bootstrap.HtmlEditor = function(config){
27276     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27277     if (!this.toolbars) {
27278         this.toolbars = [];
27279     }
27280     
27281     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27282     this.addEvents({
27283             /**
27284              * @event initialize
27285              * Fires when the editor is fully initialized (including the iframe)
27286              * @param {HtmlEditor} this
27287              */
27288             initialize: true,
27289             /**
27290              * @event activate
27291              * Fires when the editor is first receives the focus. Any insertion must wait
27292              * until after this event.
27293              * @param {HtmlEditor} this
27294              */
27295             activate: true,
27296              /**
27297              * @event beforesync
27298              * Fires before the textarea is updated with content from the editor iframe. Return false
27299              * to cancel the sync.
27300              * @param {HtmlEditor} this
27301              * @param {String} html
27302              */
27303             beforesync: true,
27304              /**
27305              * @event beforepush
27306              * Fires before the iframe editor is updated with content from the textarea. Return false
27307              * to cancel the push.
27308              * @param {HtmlEditor} this
27309              * @param {String} html
27310              */
27311             beforepush: true,
27312              /**
27313              * @event sync
27314              * Fires when the textarea is updated with content from the editor iframe.
27315              * @param {HtmlEditor} this
27316              * @param {String} html
27317              */
27318             sync: true,
27319              /**
27320              * @event push
27321              * Fires when the iframe editor is updated with content from the textarea.
27322              * @param {HtmlEditor} this
27323              * @param {String} html
27324              */
27325             push: true,
27326              /**
27327              * @event editmodechange
27328              * Fires when the editor switches edit modes
27329              * @param {HtmlEditor} this
27330              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27331              */
27332             editmodechange: true,
27333             /**
27334              * @event editorevent
27335              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27336              * @param {HtmlEditor} this
27337              */
27338             editorevent: true,
27339             /**
27340              * @event firstfocus
27341              * Fires when on first focus - needed by toolbars..
27342              * @param {HtmlEditor} this
27343              */
27344             firstfocus: true,
27345             /**
27346              * @event autosave
27347              * Auto save the htmlEditor value as a file into Events
27348              * @param {HtmlEditor} this
27349              */
27350             autosave: true,
27351             /**
27352              * @event savedpreview
27353              * preview the saved version of htmlEditor
27354              * @param {HtmlEditor} this
27355              */
27356             savedpreview: true
27357         });
27358 };
27359
27360
27361 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27362     
27363     
27364       /**
27365      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27366      */
27367     toolbars : false,
27368     
27369      /**
27370     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27371     */
27372     btns : [],
27373    
27374      /**
27375      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27376      *                        Roo.resizable.
27377      */
27378     resizable : false,
27379      /**
27380      * @cfg {Number} height (in pixels)
27381      */   
27382     height: 300,
27383    /**
27384      * @cfg {Number} width (in pixels)
27385      */   
27386     width: false,
27387     
27388     /**
27389      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27390      * 
27391      */
27392     stylesheets: false,
27393     
27394     // id of frame..
27395     frameId: false,
27396     
27397     // private properties
27398     validationEvent : false,
27399     deferHeight: true,
27400     initialized : false,
27401     activated : false,
27402     
27403     onFocus : Roo.emptyFn,
27404     iframePad:3,
27405     hideMode:'offsets',
27406     
27407     tbContainer : false,
27408     
27409     bodyCls : '',
27410     
27411     toolbarContainer :function() {
27412         return this.wrap.select('.x-html-editor-tb',true).first();
27413     },
27414
27415     /**
27416      * Protected method that will not generally be called directly. It
27417      * is called when the editor creates its toolbar. Override this method if you need to
27418      * add custom toolbar buttons.
27419      * @param {HtmlEditor} editor
27420      */
27421     createToolbar : function(){
27422         Roo.log('renewing');
27423         Roo.log("create toolbars");
27424         
27425         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27426         this.toolbars[0].render(this.toolbarContainer());
27427         
27428         return;
27429         
27430 //        if (!editor.toolbars || !editor.toolbars.length) {
27431 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27432 //        }
27433 //        
27434 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27435 //            editor.toolbars[i] = Roo.factory(
27436 //                    typeof(editor.toolbars[i]) == 'string' ?
27437 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27438 //                Roo.bootstrap.HtmlEditor);
27439 //            editor.toolbars[i].init(editor);
27440 //        }
27441     },
27442
27443      
27444     // private
27445     onRender : function(ct, position)
27446     {
27447        // Roo.log("Call onRender: " + this.xtype);
27448         var _t = this;
27449         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27450       
27451         this.wrap = this.inputEl().wrap({
27452             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27453         });
27454         
27455         this.editorcore.onRender(ct, position);
27456          
27457         if (this.resizable) {
27458             this.resizeEl = new Roo.Resizable(this.wrap, {
27459                 pinned : true,
27460                 wrap: true,
27461                 dynamic : true,
27462                 minHeight : this.height,
27463                 height: this.height,
27464                 handles : this.resizable,
27465                 width: this.width,
27466                 listeners : {
27467                     resize : function(r, w, h) {
27468                         _t.onResize(w,h); // -something
27469                     }
27470                 }
27471             });
27472             
27473         }
27474         this.createToolbar(this);
27475        
27476         
27477         if(!this.width && this.resizable){
27478             this.setSize(this.wrap.getSize());
27479         }
27480         if (this.resizeEl) {
27481             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27482             // should trigger onReize..
27483         }
27484         
27485     },
27486
27487     // private
27488     onResize : function(w, h)
27489     {
27490         Roo.log('resize: ' +w + ',' + h );
27491         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27492         var ew = false;
27493         var eh = false;
27494         
27495         if(this.inputEl() ){
27496             if(typeof w == 'number'){
27497                 var aw = w - this.wrap.getFrameWidth('lr');
27498                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27499                 ew = aw;
27500             }
27501             if(typeof h == 'number'){
27502                  var tbh = -11;  // fixme it needs to tool bar size!
27503                 for (var i =0; i < this.toolbars.length;i++) {
27504                     // fixme - ask toolbars for heights?
27505                     tbh += this.toolbars[i].el.getHeight();
27506                     //if (this.toolbars[i].footer) {
27507                     //    tbh += this.toolbars[i].footer.el.getHeight();
27508                     //}
27509                 }
27510               
27511                 
27512                 
27513                 
27514                 
27515                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27516                 ah -= 5; // knock a few pixes off for look..
27517                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27518                 var eh = ah;
27519             }
27520         }
27521         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27522         this.editorcore.onResize(ew,eh);
27523         
27524     },
27525
27526     /**
27527      * Toggles the editor between standard and source edit mode.
27528      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27529      */
27530     toggleSourceEdit : function(sourceEditMode)
27531     {
27532         this.editorcore.toggleSourceEdit(sourceEditMode);
27533         
27534         if(this.editorcore.sourceEditMode){
27535             Roo.log('editor - showing textarea');
27536             
27537 //            Roo.log('in');
27538 //            Roo.log(this.syncValue());
27539             this.syncValue();
27540             this.inputEl().removeClass(['hide', 'x-hidden']);
27541             this.inputEl().dom.removeAttribute('tabIndex');
27542             this.inputEl().focus();
27543         }else{
27544             Roo.log('editor - hiding textarea');
27545 //            Roo.log('out')
27546 //            Roo.log(this.pushValue()); 
27547             this.pushValue();
27548             
27549             this.inputEl().addClass(['hide', 'x-hidden']);
27550             this.inputEl().dom.setAttribute('tabIndex', -1);
27551             //this.deferFocus();
27552         }
27553          
27554         if(this.resizable){
27555             this.setSize(this.wrap.getSize());
27556         }
27557         
27558         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27559     },
27560  
27561     // private (for BoxComponent)
27562     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27563
27564     // private (for BoxComponent)
27565     getResizeEl : function(){
27566         return this.wrap;
27567     },
27568
27569     // private (for BoxComponent)
27570     getPositionEl : function(){
27571         return this.wrap;
27572     },
27573
27574     // private
27575     initEvents : function(){
27576         this.originalValue = this.getValue();
27577     },
27578
27579 //    /**
27580 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27581 //     * @method
27582 //     */
27583 //    markInvalid : Roo.emptyFn,
27584 //    /**
27585 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27586 //     * @method
27587 //     */
27588 //    clearInvalid : Roo.emptyFn,
27589
27590     setValue : function(v){
27591         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27592         this.editorcore.pushValue();
27593     },
27594
27595      
27596     // private
27597     deferFocus : function(){
27598         this.focus.defer(10, this);
27599     },
27600
27601     // doc'ed in Field
27602     focus : function(){
27603         this.editorcore.focus();
27604         
27605     },
27606       
27607
27608     // private
27609     onDestroy : function(){
27610         
27611         
27612         
27613         if(this.rendered){
27614             
27615             for (var i =0; i < this.toolbars.length;i++) {
27616                 // fixme - ask toolbars for heights?
27617                 this.toolbars[i].onDestroy();
27618             }
27619             
27620             this.wrap.dom.innerHTML = '';
27621             this.wrap.remove();
27622         }
27623     },
27624
27625     // private
27626     onFirstFocus : function(){
27627         //Roo.log("onFirstFocus");
27628         this.editorcore.onFirstFocus();
27629          for (var i =0; i < this.toolbars.length;i++) {
27630             this.toolbars[i].onFirstFocus();
27631         }
27632         
27633     },
27634     
27635     // private
27636     syncValue : function()
27637     {   
27638         this.editorcore.syncValue();
27639     },
27640     
27641     pushValue : function()
27642     {   
27643         this.editorcore.pushValue();
27644     }
27645      
27646     
27647     // hide stuff that is not compatible
27648     /**
27649      * @event blur
27650      * @hide
27651      */
27652     /**
27653      * @event change
27654      * @hide
27655      */
27656     /**
27657      * @event focus
27658      * @hide
27659      */
27660     /**
27661      * @event specialkey
27662      * @hide
27663      */
27664     /**
27665      * @cfg {String} fieldClass @hide
27666      */
27667     /**
27668      * @cfg {String} focusClass @hide
27669      */
27670     /**
27671      * @cfg {String} autoCreate @hide
27672      */
27673     /**
27674      * @cfg {String} inputType @hide
27675      */
27676      
27677     /**
27678      * @cfg {String} invalidText @hide
27679      */
27680     /**
27681      * @cfg {String} msgFx @hide
27682      */
27683     /**
27684      * @cfg {String} validateOnBlur @hide
27685      */
27686 });
27687  
27688     
27689    
27690    
27691    
27692       
27693 Roo.namespace('Roo.bootstrap.htmleditor');
27694 /**
27695  * @class Roo.bootstrap.HtmlEditorToolbar1
27696  * Basic Toolbar
27697  * 
27698  * @example
27699  * Usage:
27700  *
27701  new Roo.bootstrap.HtmlEditor({
27702     ....
27703     toolbars : [
27704         new Roo.bootstrap.HtmlEditorToolbar1({
27705             disable : { fonts: 1 , format: 1, ..., ... , ...],
27706             btns : [ .... ]
27707         })
27708     }
27709      
27710  * 
27711  * @cfg {Object} disable List of elements to disable..
27712  * @cfg {Array} btns List of additional buttons.
27713  * 
27714  * 
27715  * NEEDS Extra CSS? 
27716  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27717  */
27718  
27719 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27720 {
27721     
27722     Roo.apply(this, config);
27723     
27724     // default disabled, based on 'good practice'..
27725     this.disable = this.disable || {};
27726     Roo.applyIf(this.disable, {
27727         fontSize : true,
27728         colors : true,
27729         specialElements : true
27730     });
27731     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27732     
27733     this.editor = config.editor;
27734     this.editorcore = config.editor.editorcore;
27735     
27736     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27737     
27738     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27739     // dont call parent... till later.
27740 }
27741 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27742      
27743     bar : true,
27744     
27745     editor : false,
27746     editorcore : false,
27747     
27748     
27749     formats : [
27750         "p" ,  
27751         "h1","h2","h3","h4","h5","h6", 
27752         "pre", "code", 
27753         "abbr", "acronym", "address", "cite", "samp", "var",
27754         'div','span'
27755     ],
27756     
27757     onRender : function(ct, position)
27758     {
27759        // Roo.log("Call onRender: " + this.xtype);
27760         
27761        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27762        Roo.log(this.el);
27763        this.el.dom.style.marginBottom = '0';
27764        var _this = this;
27765        var editorcore = this.editorcore;
27766        var editor= this.editor;
27767        
27768        var children = [];
27769        var btn = function(id,cmd , toggle, handler, html){
27770        
27771             var  event = toggle ? 'toggle' : 'click';
27772        
27773             var a = {
27774                 size : 'sm',
27775                 xtype: 'Button',
27776                 xns: Roo.bootstrap,
27777                 //glyphicon : id,
27778                 fa: id,
27779                 cmd : id || cmd,
27780                 enableToggle:toggle !== false,
27781                 html : html || '',
27782                 pressed : toggle ? false : null,
27783                 listeners : {}
27784             };
27785             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27786                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27787             };
27788             children.push(a);
27789             return a;
27790        }
27791        
27792     //    var cb_box = function...
27793         
27794         var style = {
27795                 xtype: 'Button',
27796                 size : 'sm',
27797                 xns: Roo.bootstrap,
27798                 fa : 'font',
27799                 //html : 'submit'
27800                 menu : {
27801                     xtype: 'Menu',
27802                     xns: Roo.bootstrap,
27803                     items:  []
27804                 }
27805         };
27806         Roo.each(this.formats, function(f) {
27807             style.menu.items.push({
27808                 xtype :'MenuItem',
27809                 xns: Roo.bootstrap,
27810                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27811                 tagname : f,
27812                 listeners : {
27813                     click : function()
27814                     {
27815                         editorcore.insertTag(this.tagname);
27816                         editor.focus();
27817                     }
27818                 }
27819                 
27820             });
27821         });
27822         children.push(style);   
27823         
27824         btn('bold',false,true);
27825         btn('italic',false,true);
27826         btn('align-left', 'justifyleft',true);
27827         btn('align-center', 'justifycenter',true);
27828         btn('align-right' , 'justifyright',true);
27829         btn('link', false, false, function(btn) {
27830             //Roo.log("create link?");
27831             var url = prompt(this.createLinkText, this.defaultLinkValue);
27832             if(url && url != 'http:/'+'/'){
27833                 this.editorcore.relayCmd('createlink', url);
27834             }
27835         }),
27836         btn('list','insertunorderedlist',true);
27837         btn('pencil', false,true, function(btn){
27838                 Roo.log(this);
27839                 this.toggleSourceEdit(btn.pressed);
27840         });
27841         
27842         if (this.editor.btns.length > 0) {
27843             for (var i = 0; i<this.editor.btns.length; i++) {
27844                 children.push(this.editor.btns[i]);
27845             }
27846         }
27847         
27848         /*
27849         var cog = {
27850                 xtype: 'Button',
27851                 size : 'sm',
27852                 xns: Roo.bootstrap,
27853                 glyphicon : 'cog',
27854                 //html : 'submit'
27855                 menu : {
27856                     xtype: 'Menu',
27857                     xns: Roo.bootstrap,
27858                     items:  []
27859                 }
27860         };
27861         
27862         cog.menu.items.push({
27863             xtype :'MenuItem',
27864             xns: Roo.bootstrap,
27865             html : Clean styles,
27866             tagname : f,
27867             listeners : {
27868                 click : function()
27869                 {
27870                     editorcore.insertTag(this.tagname);
27871                     editor.focus();
27872                 }
27873             }
27874             
27875         });
27876        */
27877         
27878          
27879        this.xtype = 'NavSimplebar';
27880         
27881         for(var i=0;i< children.length;i++) {
27882             
27883             this.buttons.add(this.addxtypeChild(children[i]));
27884             
27885         }
27886         
27887         editor.on('editorevent', this.updateToolbar, this);
27888     },
27889     onBtnClick : function(id)
27890     {
27891        this.editorcore.relayCmd(id);
27892        this.editorcore.focus();
27893     },
27894     
27895     /**
27896      * Protected method that will not generally be called directly. It triggers
27897      * a toolbar update by reading the markup state of the current selection in the editor.
27898      */
27899     updateToolbar: function(){
27900
27901         if(!this.editorcore.activated){
27902             this.editor.onFirstFocus(); // is this neeed?
27903             return;
27904         }
27905
27906         var btns = this.buttons; 
27907         var doc = this.editorcore.doc;
27908         btns.get('bold').setActive(doc.queryCommandState('bold'));
27909         btns.get('italic').setActive(doc.queryCommandState('italic'));
27910         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27911         
27912         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27913         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27914         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27915         
27916         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27917         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27918          /*
27919         
27920         var ans = this.editorcore.getAllAncestors();
27921         if (this.formatCombo) {
27922             
27923             
27924             var store = this.formatCombo.store;
27925             this.formatCombo.setValue("");
27926             for (var i =0; i < ans.length;i++) {
27927                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27928                     // select it..
27929                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27930                     break;
27931                 }
27932             }
27933         }
27934         
27935         
27936         
27937         // hides menus... - so this cant be on a menu...
27938         Roo.bootstrap.MenuMgr.hideAll();
27939         */
27940         Roo.bootstrap.MenuMgr.hideAll();
27941         //this.editorsyncValue();
27942     },
27943     onFirstFocus: function() {
27944         this.buttons.each(function(item){
27945            item.enable();
27946         });
27947     },
27948     toggleSourceEdit : function(sourceEditMode){
27949         
27950           
27951         if(sourceEditMode){
27952             Roo.log("disabling buttons");
27953            this.buttons.each( function(item){
27954                 if(item.cmd != 'pencil'){
27955                     item.disable();
27956                 }
27957             });
27958           
27959         }else{
27960             Roo.log("enabling buttons");
27961             if(this.editorcore.initialized){
27962                 this.buttons.each( function(item){
27963                     item.enable();
27964                 });
27965             }
27966             
27967         }
27968         Roo.log("calling toggole on editor");
27969         // tell the editor that it's been pressed..
27970         this.editor.toggleSourceEdit(sourceEditMode);
27971        
27972     }
27973 });
27974
27975
27976
27977
27978  
27979 /*
27980  * - LGPL
27981  */
27982
27983 /**
27984  * @class Roo.bootstrap.Markdown
27985  * @extends Roo.bootstrap.TextArea
27986  * Bootstrap Showdown editable area
27987  * @cfg {string} content
27988  * 
27989  * @constructor
27990  * Create a new Showdown
27991  */
27992
27993 Roo.bootstrap.Markdown = function(config){
27994     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27995    
27996 };
27997
27998 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27999     
28000     editing :false,
28001     
28002     initEvents : function()
28003     {
28004         
28005         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28006         this.markdownEl = this.el.createChild({
28007             cls : 'roo-markdown-area'
28008         });
28009         this.inputEl().addClass('d-none');
28010         if (this.getValue() == '') {
28011             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28012             
28013         } else {
28014             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28015         }
28016         this.markdownEl.on('click', this.toggleTextEdit, this);
28017         this.on('blur', this.toggleTextEdit, this);
28018         this.on('specialkey', this.resizeTextArea, this);
28019     },
28020     
28021     toggleTextEdit : function()
28022     {
28023         var sh = this.markdownEl.getHeight();
28024         this.inputEl().addClass('d-none');
28025         this.markdownEl.addClass('d-none');
28026         if (!this.editing) {
28027             // show editor?
28028             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28029             this.inputEl().removeClass('d-none');
28030             this.inputEl().focus();
28031             this.editing = true;
28032             return;
28033         }
28034         // show showdown...
28035         this.updateMarkdown();
28036         this.markdownEl.removeClass('d-none');
28037         this.editing = false;
28038         return;
28039     },
28040     updateMarkdown : function()
28041     {
28042         if (this.getValue() == '') {
28043             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28044             return;
28045         }
28046  
28047         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28048     },
28049     
28050     resizeTextArea: function () {
28051         
28052         var sh = 100;
28053         Roo.log([sh, this.getValue().split("\n").length * 30]);
28054         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28055     },
28056     setValue : function(val)
28057     {
28058         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28059         if (!this.editing) {
28060             this.updateMarkdown();
28061         }
28062         
28063     },
28064     focus : function()
28065     {
28066         if (!this.editing) {
28067             this.toggleTextEdit();
28068         }
28069         
28070     }
28071
28072
28073 });/*
28074  * Based on:
28075  * Ext JS Library 1.1.1
28076  * Copyright(c) 2006-2007, Ext JS, LLC.
28077  *
28078  * Originally Released Under LGPL - original licence link has changed is not relivant.
28079  *
28080  * Fork - LGPL
28081  * <script type="text/javascript">
28082  */
28083  
28084 /**
28085  * @class Roo.bootstrap.PagingToolbar
28086  * @extends Roo.bootstrap.NavSimplebar
28087  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28088  * @constructor
28089  * Create a new PagingToolbar
28090  * @param {Object} config The config object
28091  * @param {Roo.data.Store} store
28092  */
28093 Roo.bootstrap.PagingToolbar = function(config)
28094 {
28095     // old args format still supported... - xtype is prefered..
28096         // created from xtype...
28097     
28098     this.ds = config.dataSource;
28099     
28100     if (config.store && !this.ds) {
28101         this.store= Roo.factory(config.store, Roo.data);
28102         this.ds = this.store;
28103         this.ds.xmodule = this.xmodule || false;
28104     }
28105     
28106     this.toolbarItems = [];
28107     if (config.items) {
28108         this.toolbarItems = config.items;
28109     }
28110     
28111     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28112     
28113     this.cursor = 0;
28114     
28115     if (this.ds) { 
28116         this.bind(this.ds);
28117     }
28118     
28119     if (Roo.bootstrap.version == 4) {
28120         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28121     } else {
28122         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28123     }
28124     
28125 };
28126
28127 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28128     /**
28129      * @cfg {Roo.data.Store} dataSource
28130      * The underlying data store providing the paged data
28131      */
28132     /**
28133      * @cfg {String/HTMLElement/Element} container
28134      * container The id or element that will contain the toolbar
28135      */
28136     /**
28137      * @cfg {Boolean} displayInfo
28138      * True to display the displayMsg (defaults to false)
28139      */
28140     /**
28141      * @cfg {Number} pageSize
28142      * The number of records to display per page (defaults to 20)
28143      */
28144     pageSize: 20,
28145     /**
28146      * @cfg {String} displayMsg
28147      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28148      */
28149     displayMsg : 'Displaying {0} - {1} of {2}',
28150     /**
28151      * @cfg {String} emptyMsg
28152      * The message to display when no records are found (defaults to "No data to display")
28153      */
28154     emptyMsg : 'No data to display',
28155     /**
28156      * Customizable piece of the default paging text (defaults to "Page")
28157      * @type String
28158      */
28159     beforePageText : "Page",
28160     /**
28161      * Customizable piece of the default paging text (defaults to "of %0")
28162      * @type String
28163      */
28164     afterPageText : "of {0}",
28165     /**
28166      * Customizable piece of the default paging text (defaults to "First Page")
28167      * @type String
28168      */
28169     firstText : "First Page",
28170     /**
28171      * Customizable piece of the default paging text (defaults to "Previous Page")
28172      * @type String
28173      */
28174     prevText : "Previous Page",
28175     /**
28176      * Customizable piece of the default paging text (defaults to "Next Page")
28177      * @type String
28178      */
28179     nextText : "Next Page",
28180     /**
28181      * Customizable piece of the default paging text (defaults to "Last Page")
28182      * @type String
28183      */
28184     lastText : "Last Page",
28185     /**
28186      * Customizable piece of the default paging text (defaults to "Refresh")
28187      * @type String
28188      */
28189     refreshText : "Refresh",
28190
28191     buttons : false,
28192     // private
28193     onRender : function(ct, position) 
28194     {
28195         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28196         this.navgroup.parentId = this.id;
28197         this.navgroup.onRender(this.el, null);
28198         // add the buttons to the navgroup
28199         
28200         if(this.displayInfo){
28201             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28202             this.displayEl = this.el.select('.x-paging-info', true).first();
28203 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28204 //            this.displayEl = navel.el.select('span',true).first();
28205         }
28206         
28207         var _this = this;
28208         
28209         if(this.buttons){
28210             Roo.each(_this.buttons, function(e){ // this might need to use render????
28211                Roo.factory(e).render(_this.el);
28212             });
28213         }
28214             
28215         Roo.each(_this.toolbarItems, function(e) {
28216             _this.navgroup.addItem(e);
28217         });
28218         
28219         
28220         this.first = this.navgroup.addItem({
28221             tooltip: this.firstText,
28222             cls: "prev btn-outline-secondary",
28223             html : ' <i class="fa fa-step-backward"></i>',
28224             disabled: true,
28225             preventDefault: true,
28226             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28227         });
28228         
28229         this.prev =  this.navgroup.addItem({
28230             tooltip: this.prevText,
28231             cls: "prev btn-outline-secondary",
28232             html : ' <i class="fa fa-backward"></i>',
28233             disabled: true,
28234             preventDefault: true,
28235             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28236         });
28237     //this.addSeparator();
28238         
28239         
28240         var field = this.navgroup.addItem( {
28241             tagtype : 'span',
28242             cls : 'x-paging-position  btn-outline-secondary',
28243              disabled: true,
28244             html : this.beforePageText  +
28245                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28246                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28247          } ); //?? escaped?
28248         
28249         this.field = field.el.select('input', true).first();
28250         this.field.on("keydown", this.onPagingKeydown, this);
28251         this.field.on("focus", function(){this.dom.select();});
28252     
28253     
28254         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28255         //this.field.setHeight(18);
28256         //this.addSeparator();
28257         this.next = this.navgroup.addItem({
28258             tooltip: this.nextText,
28259             cls: "next btn-outline-secondary",
28260             html : ' <i class="fa fa-forward"></i>',
28261             disabled: true,
28262             preventDefault: true,
28263             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28264         });
28265         this.last = this.navgroup.addItem({
28266             tooltip: this.lastText,
28267             html : ' <i class="fa fa-step-forward"></i>',
28268             cls: "next btn-outline-secondary",
28269             disabled: true,
28270             preventDefault: true,
28271             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28272         });
28273     //this.addSeparator();
28274         this.loading = this.navgroup.addItem({
28275             tooltip: this.refreshText,
28276             cls: "btn-outline-secondary",
28277             html : ' <i class="fa fa-refresh"></i>',
28278             preventDefault: true,
28279             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28280         });
28281         
28282     },
28283
28284     // private
28285     updateInfo : function(){
28286         if(this.displayEl){
28287             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28288             var msg = count == 0 ?
28289                 this.emptyMsg :
28290                 String.format(
28291                     this.displayMsg,
28292                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28293                 );
28294             this.displayEl.update(msg);
28295         }
28296     },
28297
28298     // private
28299     onLoad : function(ds, r, o)
28300     {
28301         this.cursor = o.params && o.params.start ? o.params.start : 0;
28302         
28303         var d = this.getPageData(),
28304             ap = d.activePage,
28305             ps = d.pages;
28306         
28307         
28308         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28309         this.field.dom.value = ap;
28310         this.first.setDisabled(ap == 1);
28311         this.prev.setDisabled(ap == 1);
28312         this.next.setDisabled(ap == ps);
28313         this.last.setDisabled(ap == ps);
28314         this.loading.enable();
28315         this.updateInfo();
28316     },
28317
28318     // private
28319     getPageData : function(){
28320         var total = this.ds.getTotalCount();
28321         return {
28322             total : total,
28323             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28324             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28325         };
28326     },
28327
28328     // private
28329     onLoadError : function(){
28330         this.loading.enable();
28331     },
28332
28333     // private
28334     onPagingKeydown : function(e){
28335         var k = e.getKey();
28336         var d = this.getPageData();
28337         if(k == e.RETURN){
28338             var v = this.field.dom.value, pageNum;
28339             if(!v || isNaN(pageNum = parseInt(v, 10))){
28340                 this.field.dom.value = d.activePage;
28341                 return;
28342             }
28343             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28344             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28345             e.stopEvent();
28346         }
28347         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))
28348         {
28349           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28350           this.field.dom.value = pageNum;
28351           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28352           e.stopEvent();
28353         }
28354         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28355         {
28356           var v = this.field.dom.value, pageNum; 
28357           var increment = (e.shiftKey) ? 10 : 1;
28358           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28359                 increment *= -1;
28360           }
28361           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28362             this.field.dom.value = d.activePage;
28363             return;
28364           }
28365           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28366           {
28367             this.field.dom.value = parseInt(v, 10) + increment;
28368             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28369             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28370           }
28371           e.stopEvent();
28372         }
28373     },
28374
28375     // private
28376     beforeLoad : function(){
28377         if(this.loading){
28378             this.loading.disable();
28379         }
28380     },
28381
28382     // private
28383     onClick : function(which){
28384         
28385         var ds = this.ds;
28386         if (!ds) {
28387             return;
28388         }
28389         
28390         switch(which){
28391             case "first":
28392                 ds.load({params:{start: 0, limit: this.pageSize}});
28393             break;
28394             case "prev":
28395                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28396             break;
28397             case "next":
28398                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28399             break;
28400             case "last":
28401                 var total = ds.getTotalCount();
28402                 var extra = total % this.pageSize;
28403                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28404                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28405             break;
28406             case "refresh":
28407                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28408             break;
28409         }
28410     },
28411
28412     /**
28413      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28414      * @param {Roo.data.Store} store The data store to unbind
28415      */
28416     unbind : function(ds){
28417         ds.un("beforeload", this.beforeLoad, this);
28418         ds.un("load", this.onLoad, this);
28419         ds.un("loadexception", this.onLoadError, this);
28420         ds.un("remove", this.updateInfo, this);
28421         ds.un("add", this.updateInfo, this);
28422         this.ds = undefined;
28423     },
28424
28425     /**
28426      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28427      * @param {Roo.data.Store} store The data store to bind
28428      */
28429     bind : function(ds){
28430         ds.on("beforeload", this.beforeLoad, this);
28431         ds.on("load", this.onLoad, this);
28432         ds.on("loadexception", this.onLoadError, this);
28433         ds.on("remove", this.updateInfo, this);
28434         ds.on("add", this.updateInfo, this);
28435         this.ds = ds;
28436     }
28437 });/*
28438  * - LGPL
28439  *
28440  * element
28441  * 
28442  */
28443
28444 /**
28445  * @class Roo.bootstrap.MessageBar
28446  * @extends Roo.bootstrap.Component
28447  * Bootstrap MessageBar class
28448  * @cfg {String} html contents of the MessageBar
28449  * @cfg {String} weight (info | success | warning | danger) default info
28450  * @cfg {String} beforeClass insert the bar before the given class
28451  * @cfg {Boolean} closable (true | false) default false
28452  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28453  * 
28454  * @constructor
28455  * Create a new Element
28456  * @param {Object} config The config object
28457  */
28458
28459 Roo.bootstrap.MessageBar = function(config){
28460     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28461 };
28462
28463 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28464     
28465     html: '',
28466     weight: 'info',
28467     closable: false,
28468     fixed: false,
28469     beforeClass: 'bootstrap-sticky-wrap',
28470     
28471     getAutoCreate : function(){
28472         
28473         var cfg = {
28474             tag: 'div',
28475             cls: 'alert alert-dismissable alert-' + this.weight,
28476             cn: [
28477                 {
28478                     tag: 'span',
28479                     cls: 'message',
28480                     html: this.html || ''
28481                 }
28482             ]
28483         };
28484         
28485         if(this.fixed){
28486             cfg.cls += ' alert-messages-fixed';
28487         }
28488         
28489         if(this.closable){
28490             cfg.cn.push({
28491                 tag: 'button',
28492                 cls: 'close',
28493                 html: 'x'
28494             });
28495         }
28496         
28497         return cfg;
28498     },
28499     
28500     onRender : function(ct, position)
28501     {
28502         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28503         
28504         if(!this.el){
28505             var cfg = Roo.apply({},  this.getAutoCreate());
28506             cfg.id = Roo.id();
28507             
28508             if (this.cls) {
28509                 cfg.cls += ' ' + this.cls;
28510             }
28511             if (this.style) {
28512                 cfg.style = this.style;
28513             }
28514             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28515             
28516             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28517         }
28518         
28519         this.el.select('>button.close').on('click', this.hide, this);
28520         
28521     },
28522     
28523     show : function()
28524     {
28525         if (!this.rendered) {
28526             this.render();
28527         }
28528         
28529         this.el.show();
28530         
28531         this.fireEvent('show', this);
28532         
28533     },
28534     
28535     hide : function()
28536     {
28537         if (!this.rendered) {
28538             this.render();
28539         }
28540         
28541         this.el.hide();
28542         
28543         this.fireEvent('hide', this);
28544     },
28545     
28546     update : function()
28547     {
28548 //        var e = this.el.dom.firstChild;
28549 //        
28550 //        if(this.closable){
28551 //            e = e.nextSibling;
28552 //        }
28553 //        
28554 //        e.data = this.html || '';
28555
28556         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28557     }
28558    
28559 });
28560
28561  
28562
28563      /*
28564  * - LGPL
28565  *
28566  * Graph
28567  * 
28568  */
28569
28570
28571 /**
28572  * @class Roo.bootstrap.Graph
28573  * @extends Roo.bootstrap.Component
28574  * Bootstrap Graph class
28575 > Prameters
28576  -sm {number} sm 4
28577  -md {number} md 5
28578  @cfg {String} graphtype  bar | vbar | pie
28579  @cfg {number} g_x coodinator | centre x (pie)
28580  @cfg {number} g_y coodinator | centre y (pie)
28581  @cfg {number} g_r radius (pie)
28582  @cfg {number} g_height height of the chart (respected by all elements in the set)
28583  @cfg {number} g_width width of the chart (respected by all elements in the set)
28584  @cfg {Object} title The title of the chart
28585     
28586  -{Array}  values
28587  -opts (object) options for the chart 
28588      o {
28589      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28590      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28591      o vgutter (number)
28592      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.
28593      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28594      o to
28595      o stretch (boolean)
28596      o }
28597  -opts (object) options for the pie
28598      o{
28599      o cut
28600      o startAngle (number)
28601      o endAngle (number)
28602      } 
28603  *
28604  * @constructor
28605  * Create a new Input
28606  * @param {Object} config The config object
28607  */
28608
28609 Roo.bootstrap.Graph = function(config){
28610     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28611     
28612     this.addEvents({
28613         // img events
28614         /**
28615          * @event click
28616          * The img click event for the img.
28617          * @param {Roo.EventObject} e
28618          */
28619         "click" : true
28620     });
28621 };
28622
28623 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28624     
28625     sm: 4,
28626     md: 5,
28627     graphtype: 'bar',
28628     g_height: 250,
28629     g_width: 400,
28630     g_x: 50,
28631     g_y: 50,
28632     g_r: 30,
28633     opts:{
28634         //g_colors: this.colors,
28635         g_type: 'soft',
28636         g_gutter: '20%'
28637
28638     },
28639     title : false,
28640
28641     getAutoCreate : function(){
28642         
28643         var cfg = {
28644             tag: 'div',
28645             html : null
28646         };
28647         
28648         
28649         return  cfg;
28650     },
28651
28652     onRender : function(ct,position){
28653         
28654         
28655         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28656         
28657         if (typeof(Raphael) == 'undefined') {
28658             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28659             return;
28660         }
28661         
28662         this.raphael = Raphael(this.el.dom);
28663         
28664                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28665                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28666                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28667                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28668                 /*
28669                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28670                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28671                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28672                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28673                 
28674                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28675                 r.barchart(330, 10, 300, 220, data1);
28676                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28677                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28678                 */
28679                 
28680                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28681                 // r.barchart(30, 30, 560, 250,  xdata, {
28682                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28683                 //     axis : "0 0 1 1",
28684                 //     axisxlabels :  xdata
28685                 //     //yvalues : cols,
28686                    
28687                 // });
28688 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28689 //        
28690 //        this.load(null,xdata,{
28691 //                axis : "0 0 1 1",
28692 //                axisxlabels :  xdata
28693 //                });
28694
28695     },
28696
28697     load : function(graphtype,xdata,opts)
28698     {
28699         this.raphael.clear();
28700         if(!graphtype) {
28701             graphtype = this.graphtype;
28702         }
28703         if(!opts){
28704             opts = this.opts;
28705         }
28706         var r = this.raphael,
28707             fin = function () {
28708                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28709             },
28710             fout = function () {
28711                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28712             },
28713             pfin = function() {
28714                 this.sector.stop();
28715                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28716
28717                 if (this.label) {
28718                     this.label[0].stop();
28719                     this.label[0].attr({ r: 7.5 });
28720                     this.label[1].attr({ "font-weight": 800 });
28721                 }
28722             },
28723             pfout = function() {
28724                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28725
28726                 if (this.label) {
28727                     this.label[0].animate({ r: 5 }, 500, "bounce");
28728                     this.label[1].attr({ "font-weight": 400 });
28729                 }
28730             };
28731
28732         switch(graphtype){
28733             case 'bar':
28734                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28735                 break;
28736             case 'hbar':
28737                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28738                 break;
28739             case 'pie':
28740 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28741 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28742 //            
28743                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28744                 
28745                 break;
28746
28747         }
28748         
28749         if(this.title){
28750             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28751         }
28752         
28753     },
28754     
28755     setTitle: function(o)
28756     {
28757         this.title = o;
28758     },
28759     
28760     initEvents: function() {
28761         
28762         if(!this.href){
28763             this.el.on('click', this.onClick, this);
28764         }
28765     },
28766     
28767     onClick : function(e)
28768     {
28769         Roo.log('img onclick');
28770         this.fireEvent('click', this, e);
28771     }
28772    
28773 });
28774
28775  
28776 /*
28777  * - LGPL
28778  *
28779  * numberBox
28780  * 
28781  */
28782 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28783
28784 /**
28785  * @class Roo.bootstrap.dash.NumberBox
28786  * @extends Roo.bootstrap.Component
28787  * Bootstrap NumberBox class
28788  * @cfg {String} headline Box headline
28789  * @cfg {String} content Box content
28790  * @cfg {String} icon Box icon
28791  * @cfg {String} footer Footer text
28792  * @cfg {String} fhref Footer href
28793  * 
28794  * @constructor
28795  * Create a new NumberBox
28796  * @param {Object} config The config object
28797  */
28798
28799
28800 Roo.bootstrap.dash.NumberBox = function(config){
28801     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28802     
28803 };
28804
28805 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28806     
28807     headline : '',
28808     content : '',
28809     icon : '',
28810     footer : '',
28811     fhref : '',
28812     ficon : '',
28813     
28814     getAutoCreate : function(){
28815         
28816         var cfg = {
28817             tag : 'div',
28818             cls : 'small-box ',
28819             cn : [
28820                 {
28821                     tag : 'div',
28822                     cls : 'inner',
28823                     cn :[
28824                         {
28825                             tag : 'h3',
28826                             cls : 'roo-headline',
28827                             html : this.headline
28828                         },
28829                         {
28830                             tag : 'p',
28831                             cls : 'roo-content',
28832                             html : this.content
28833                         }
28834                     ]
28835                 }
28836             ]
28837         };
28838         
28839         if(this.icon){
28840             cfg.cn.push({
28841                 tag : 'div',
28842                 cls : 'icon',
28843                 cn :[
28844                     {
28845                         tag : 'i',
28846                         cls : 'ion ' + this.icon
28847                     }
28848                 ]
28849             });
28850         }
28851         
28852         if(this.footer){
28853             var footer = {
28854                 tag : 'a',
28855                 cls : 'small-box-footer',
28856                 href : this.fhref || '#',
28857                 html : this.footer
28858             };
28859             
28860             cfg.cn.push(footer);
28861             
28862         }
28863         
28864         return  cfg;
28865     },
28866
28867     onRender : function(ct,position){
28868         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28869
28870
28871        
28872                 
28873     },
28874
28875     setHeadline: function (value)
28876     {
28877         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28878     },
28879     
28880     setFooter: function (value, href)
28881     {
28882         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28883         
28884         if(href){
28885             this.el.select('a.small-box-footer',true).first().attr('href', href);
28886         }
28887         
28888     },
28889
28890     setContent: function (value)
28891     {
28892         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28893     },
28894
28895     initEvents: function() 
28896     {   
28897         
28898     }
28899     
28900 });
28901
28902  
28903 /*
28904  * - LGPL
28905  *
28906  * TabBox
28907  * 
28908  */
28909 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28910
28911 /**
28912  * @class Roo.bootstrap.dash.TabBox
28913  * @extends Roo.bootstrap.Component
28914  * Bootstrap TabBox class
28915  * @cfg {String} title Title of the TabBox
28916  * @cfg {String} icon Icon of the TabBox
28917  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28918  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28919  * 
28920  * @constructor
28921  * Create a new TabBox
28922  * @param {Object} config The config object
28923  */
28924
28925
28926 Roo.bootstrap.dash.TabBox = function(config){
28927     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28928     this.addEvents({
28929         // raw events
28930         /**
28931          * @event addpane
28932          * When a pane is added
28933          * @param {Roo.bootstrap.dash.TabPane} pane
28934          */
28935         "addpane" : true,
28936         /**
28937          * @event activatepane
28938          * When a pane is activated
28939          * @param {Roo.bootstrap.dash.TabPane} pane
28940          */
28941         "activatepane" : true
28942         
28943          
28944     });
28945     
28946     this.panes = [];
28947 };
28948
28949 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28950
28951     title : '',
28952     icon : false,
28953     showtabs : true,
28954     tabScrollable : false,
28955     
28956     getChildContainer : function()
28957     {
28958         return this.el.select('.tab-content', true).first();
28959     },
28960     
28961     getAutoCreate : function(){
28962         
28963         var header = {
28964             tag: 'li',
28965             cls: 'pull-left header',
28966             html: this.title,
28967             cn : []
28968         };
28969         
28970         if(this.icon){
28971             header.cn.push({
28972                 tag: 'i',
28973                 cls: 'fa ' + this.icon
28974             });
28975         }
28976         
28977         var h = {
28978             tag: 'ul',
28979             cls: 'nav nav-tabs pull-right',
28980             cn: [
28981                 header
28982             ]
28983         };
28984         
28985         if(this.tabScrollable){
28986             h = {
28987                 tag: 'div',
28988                 cls: 'tab-header',
28989                 cn: [
28990                     {
28991                         tag: 'ul',
28992                         cls: 'nav nav-tabs pull-right',
28993                         cn: [
28994                             header
28995                         ]
28996                     }
28997                 ]
28998             };
28999         }
29000         
29001         var cfg = {
29002             tag: 'div',
29003             cls: 'nav-tabs-custom',
29004             cn: [
29005                 h,
29006                 {
29007                     tag: 'div',
29008                     cls: 'tab-content no-padding',
29009                     cn: []
29010                 }
29011             ]
29012         };
29013
29014         return  cfg;
29015     },
29016     initEvents : function()
29017     {
29018         //Roo.log('add add pane handler');
29019         this.on('addpane', this.onAddPane, this);
29020     },
29021      /**
29022      * Updates the box title
29023      * @param {String} html to set the title to.
29024      */
29025     setTitle : function(value)
29026     {
29027         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29028     },
29029     onAddPane : function(pane)
29030     {
29031         this.panes.push(pane);
29032         //Roo.log('addpane');
29033         //Roo.log(pane);
29034         // tabs are rendere left to right..
29035         if(!this.showtabs){
29036             return;
29037         }
29038         
29039         var ctr = this.el.select('.nav-tabs', true).first();
29040          
29041          
29042         var existing = ctr.select('.nav-tab',true);
29043         var qty = existing.getCount();;
29044         
29045         
29046         var tab = ctr.createChild({
29047             tag : 'li',
29048             cls : 'nav-tab' + (qty ? '' : ' active'),
29049             cn : [
29050                 {
29051                     tag : 'a',
29052                     href:'#',
29053                     html : pane.title
29054                 }
29055             ]
29056         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29057         pane.tab = tab;
29058         
29059         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29060         if (!qty) {
29061             pane.el.addClass('active');
29062         }
29063         
29064                 
29065     },
29066     onTabClick : function(ev,un,ob,pane)
29067     {
29068         //Roo.log('tab - prev default');
29069         ev.preventDefault();
29070         
29071         
29072         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29073         pane.tab.addClass('active');
29074         //Roo.log(pane.title);
29075         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29076         // technically we should have a deactivate event.. but maybe add later.
29077         // and it should not de-activate the selected tab...
29078         this.fireEvent('activatepane', pane);
29079         pane.el.addClass('active');
29080         pane.fireEvent('activate');
29081         
29082         
29083     },
29084     
29085     getActivePane : function()
29086     {
29087         var r = false;
29088         Roo.each(this.panes, function(p) {
29089             if(p.el.hasClass('active')){
29090                 r = p;
29091                 return false;
29092             }
29093             
29094             return;
29095         });
29096         
29097         return r;
29098     }
29099     
29100     
29101 });
29102
29103  
29104 /*
29105  * - LGPL
29106  *
29107  * Tab pane
29108  * 
29109  */
29110 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29111 /**
29112  * @class Roo.bootstrap.TabPane
29113  * @extends Roo.bootstrap.Component
29114  * Bootstrap TabPane class
29115  * @cfg {Boolean} active (false | true) Default false
29116  * @cfg {String} title title of panel
29117
29118  * 
29119  * @constructor
29120  * Create a new TabPane
29121  * @param {Object} config The config object
29122  */
29123
29124 Roo.bootstrap.dash.TabPane = function(config){
29125     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29126     
29127     this.addEvents({
29128         // raw events
29129         /**
29130          * @event activate
29131          * When a pane is activated
29132          * @param {Roo.bootstrap.dash.TabPane} pane
29133          */
29134         "activate" : true
29135          
29136     });
29137 };
29138
29139 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29140     
29141     active : false,
29142     title : '',
29143     
29144     // the tabBox that this is attached to.
29145     tab : false,
29146      
29147     getAutoCreate : function() 
29148     {
29149         var cfg = {
29150             tag: 'div',
29151             cls: 'tab-pane'
29152         };
29153         
29154         if(this.active){
29155             cfg.cls += ' active';
29156         }
29157         
29158         return cfg;
29159     },
29160     initEvents  : function()
29161     {
29162         //Roo.log('trigger add pane handler');
29163         this.parent().fireEvent('addpane', this)
29164     },
29165     
29166      /**
29167      * Updates the tab title 
29168      * @param {String} html to set the title to.
29169      */
29170     setTitle: function(str)
29171     {
29172         if (!this.tab) {
29173             return;
29174         }
29175         this.title = str;
29176         this.tab.select('a', true).first().dom.innerHTML = str;
29177         
29178     }
29179     
29180     
29181     
29182 });
29183
29184  
29185
29186
29187  /*
29188  * - LGPL
29189  *
29190  * menu
29191  * 
29192  */
29193 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29194
29195 /**
29196  * @class Roo.bootstrap.menu.Menu
29197  * @extends Roo.bootstrap.Component
29198  * Bootstrap Menu class - container for Menu
29199  * @cfg {String} html Text of the menu
29200  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29201  * @cfg {String} icon Font awesome icon
29202  * @cfg {String} pos Menu align to (top | bottom) default bottom
29203  * 
29204  * 
29205  * @constructor
29206  * Create a new Menu
29207  * @param {Object} config The config object
29208  */
29209
29210
29211 Roo.bootstrap.menu.Menu = function(config){
29212     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29213     
29214     this.addEvents({
29215         /**
29216          * @event beforeshow
29217          * Fires before this menu is displayed
29218          * @param {Roo.bootstrap.menu.Menu} this
29219          */
29220         beforeshow : true,
29221         /**
29222          * @event beforehide
29223          * Fires before this menu is hidden
29224          * @param {Roo.bootstrap.menu.Menu} this
29225          */
29226         beforehide : true,
29227         /**
29228          * @event show
29229          * Fires after this menu is displayed
29230          * @param {Roo.bootstrap.menu.Menu} this
29231          */
29232         show : true,
29233         /**
29234          * @event hide
29235          * Fires after this menu is hidden
29236          * @param {Roo.bootstrap.menu.Menu} this
29237          */
29238         hide : true,
29239         /**
29240          * @event click
29241          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29242          * @param {Roo.bootstrap.menu.Menu} this
29243          * @param {Roo.EventObject} e
29244          */
29245         click : true
29246     });
29247     
29248 };
29249
29250 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29251     
29252     submenu : false,
29253     html : '',
29254     weight : 'default',
29255     icon : false,
29256     pos : 'bottom',
29257     
29258     
29259     getChildContainer : function() {
29260         if(this.isSubMenu){
29261             return this.el;
29262         }
29263         
29264         return this.el.select('ul.dropdown-menu', true).first();  
29265     },
29266     
29267     getAutoCreate : function()
29268     {
29269         var text = [
29270             {
29271                 tag : 'span',
29272                 cls : 'roo-menu-text',
29273                 html : this.html
29274             }
29275         ];
29276         
29277         if(this.icon){
29278             text.unshift({
29279                 tag : 'i',
29280                 cls : 'fa ' + this.icon
29281             })
29282         }
29283         
29284         
29285         var cfg = {
29286             tag : 'div',
29287             cls : 'btn-group',
29288             cn : [
29289                 {
29290                     tag : 'button',
29291                     cls : 'dropdown-button btn btn-' + this.weight,
29292                     cn : text
29293                 },
29294                 {
29295                     tag : 'button',
29296                     cls : 'dropdown-toggle btn btn-' + this.weight,
29297                     cn : [
29298                         {
29299                             tag : 'span',
29300                             cls : 'caret'
29301                         }
29302                     ]
29303                 },
29304                 {
29305                     tag : 'ul',
29306                     cls : 'dropdown-menu'
29307                 }
29308             ]
29309             
29310         };
29311         
29312         if(this.pos == 'top'){
29313             cfg.cls += ' dropup';
29314         }
29315         
29316         if(this.isSubMenu){
29317             cfg = {
29318                 tag : 'ul',
29319                 cls : 'dropdown-menu'
29320             }
29321         }
29322         
29323         return cfg;
29324     },
29325     
29326     onRender : function(ct, position)
29327     {
29328         this.isSubMenu = ct.hasClass('dropdown-submenu');
29329         
29330         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29331     },
29332     
29333     initEvents : function() 
29334     {
29335         if(this.isSubMenu){
29336             return;
29337         }
29338         
29339         this.hidden = true;
29340         
29341         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29342         this.triggerEl.on('click', this.onTriggerPress, this);
29343         
29344         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29345         this.buttonEl.on('click', this.onClick, this);
29346         
29347     },
29348     
29349     list : function()
29350     {
29351         if(this.isSubMenu){
29352             return this.el;
29353         }
29354         
29355         return this.el.select('ul.dropdown-menu', true).first();
29356     },
29357     
29358     onClick : function(e)
29359     {
29360         this.fireEvent("click", this, e);
29361     },
29362     
29363     onTriggerPress  : function(e)
29364     {   
29365         if (this.isVisible()) {
29366             this.hide();
29367         } else {
29368             this.show();
29369         }
29370     },
29371     
29372     isVisible : function(){
29373         return !this.hidden;
29374     },
29375     
29376     show : function()
29377     {
29378         this.fireEvent("beforeshow", this);
29379         
29380         this.hidden = false;
29381         this.el.addClass('open');
29382         
29383         Roo.get(document).on("mouseup", this.onMouseUp, this);
29384         
29385         this.fireEvent("show", this);
29386         
29387         
29388     },
29389     
29390     hide : function()
29391     {
29392         this.fireEvent("beforehide", this);
29393         
29394         this.hidden = true;
29395         this.el.removeClass('open');
29396         
29397         Roo.get(document).un("mouseup", this.onMouseUp);
29398         
29399         this.fireEvent("hide", this);
29400     },
29401     
29402     onMouseUp : function()
29403     {
29404         this.hide();
29405     }
29406     
29407 });
29408
29409  
29410  /*
29411  * - LGPL
29412  *
29413  * menu item
29414  * 
29415  */
29416 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29417
29418 /**
29419  * @class Roo.bootstrap.menu.Item
29420  * @extends Roo.bootstrap.Component
29421  * Bootstrap MenuItem class
29422  * @cfg {Boolean} submenu (true | false) default false
29423  * @cfg {String} html text of the item
29424  * @cfg {String} href the link
29425  * @cfg {Boolean} disable (true | false) default false
29426  * @cfg {Boolean} preventDefault (true | false) default true
29427  * @cfg {String} icon Font awesome icon
29428  * @cfg {String} pos Submenu align to (left | right) default right 
29429  * 
29430  * 
29431  * @constructor
29432  * Create a new Item
29433  * @param {Object} config The config object
29434  */
29435
29436
29437 Roo.bootstrap.menu.Item = function(config){
29438     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29439     this.addEvents({
29440         /**
29441          * @event mouseover
29442          * Fires when the mouse is hovering over this menu
29443          * @param {Roo.bootstrap.menu.Item} this
29444          * @param {Roo.EventObject} e
29445          */
29446         mouseover : true,
29447         /**
29448          * @event mouseout
29449          * Fires when the mouse exits this menu
29450          * @param {Roo.bootstrap.menu.Item} this
29451          * @param {Roo.EventObject} e
29452          */
29453         mouseout : true,
29454         // raw events
29455         /**
29456          * @event click
29457          * The raw click event for the entire grid.
29458          * @param {Roo.EventObject} e
29459          */
29460         click : true
29461     });
29462 };
29463
29464 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29465     
29466     submenu : false,
29467     href : '',
29468     html : '',
29469     preventDefault: true,
29470     disable : false,
29471     icon : false,
29472     pos : 'right',
29473     
29474     getAutoCreate : function()
29475     {
29476         var text = [
29477             {
29478                 tag : 'span',
29479                 cls : 'roo-menu-item-text',
29480                 html : this.html
29481             }
29482         ];
29483         
29484         if(this.icon){
29485             text.unshift({
29486                 tag : 'i',
29487                 cls : 'fa ' + this.icon
29488             })
29489         }
29490         
29491         var cfg = {
29492             tag : 'li',
29493             cn : [
29494                 {
29495                     tag : 'a',
29496                     href : this.href || '#',
29497                     cn : text
29498                 }
29499             ]
29500         };
29501         
29502         if(this.disable){
29503             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29504         }
29505         
29506         if(this.submenu){
29507             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29508             
29509             if(this.pos == 'left'){
29510                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29511             }
29512         }
29513         
29514         return cfg;
29515     },
29516     
29517     initEvents : function() 
29518     {
29519         this.el.on('mouseover', this.onMouseOver, this);
29520         this.el.on('mouseout', this.onMouseOut, this);
29521         
29522         this.el.select('a', true).first().on('click', this.onClick, this);
29523         
29524     },
29525     
29526     onClick : function(e)
29527     {
29528         if(this.preventDefault){
29529             e.preventDefault();
29530         }
29531         
29532         this.fireEvent("click", this, e);
29533     },
29534     
29535     onMouseOver : function(e)
29536     {
29537         if(this.submenu && this.pos == 'left'){
29538             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29539         }
29540         
29541         this.fireEvent("mouseover", this, e);
29542     },
29543     
29544     onMouseOut : function(e)
29545     {
29546         this.fireEvent("mouseout", this, e);
29547     }
29548 });
29549
29550  
29551
29552  /*
29553  * - LGPL
29554  *
29555  * menu separator
29556  * 
29557  */
29558 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29559
29560 /**
29561  * @class Roo.bootstrap.menu.Separator
29562  * @extends Roo.bootstrap.Component
29563  * Bootstrap Separator class
29564  * 
29565  * @constructor
29566  * Create a new Separator
29567  * @param {Object} config The config object
29568  */
29569
29570
29571 Roo.bootstrap.menu.Separator = function(config){
29572     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29573 };
29574
29575 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29576     
29577     getAutoCreate : function(){
29578         var cfg = {
29579             tag : 'li',
29580             cls: 'dropdown-divider divider'
29581         };
29582         
29583         return cfg;
29584     }
29585    
29586 });
29587
29588  
29589
29590  /*
29591  * - LGPL
29592  *
29593  * Tooltip
29594  * 
29595  */
29596
29597 /**
29598  * @class Roo.bootstrap.Tooltip
29599  * Bootstrap Tooltip class
29600  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29601  * to determine which dom element triggers the tooltip.
29602  * 
29603  * It needs to add support for additional attributes like tooltip-position
29604  * 
29605  * @constructor
29606  * Create a new Toolti
29607  * @param {Object} config The config object
29608  */
29609
29610 Roo.bootstrap.Tooltip = function(config){
29611     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29612     
29613     this.alignment = Roo.bootstrap.Tooltip.alignment;
29614     
29615     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29616         this.alignment = config.alignment;
29617     }
29618     
29619 };
29620
29621 Roo.apply(Roo.bootstrap.Tooltip, {
29622     /**
29623      * @function init initialize tooltip monitoring.
29624      * @static
29625      */
29626     currentEl : false,
29627     currentTip : false,
29628     currentRegion : false,
29629     
29630     //  init : delay?
29631     
29632     init : function()
29633     {
29634         Roo.get(document).on('mouseover', this.enter ,this);
29635         Roo.get(document).on('mouseout', this.leave, this);
29636          
29637         
29638         this.currentTip = new Roo.bootstrap.Tooltip();
29639     },
29640     
29641     enter : function(ev)
29642     {
29643         var dom = ev.getTarget();
29644         
29645         //Roo.log(['enter',dom]);
29646         var el = Roo.fly(dom);
29647         if (this.currentEl) {
29648             //Roo.log(dom);
29649             //Roo.log(this.currentEl);
29650             //Roo.log(this.currentEl.contains(dom));
29651             if (this.currentEl == el) {
29652                 return;
29653             }
29654             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29655                 return;
29656             }
29657
29658         }
29659         
29660         if (this.currentTip.el) {
29661             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29662         }    
29663         //Roo.log(ev);
29664         
29665         if(!el || el.dom == document){
29666             return;
29667         }
29668         
29669         var bindEl = el; 
29670         var pel = false;
29671         if (!el.attr('tooltip')) {
29672             pel = el.findParent("[tooltip]");
29673             if (pel) {
29674                 bindEl = Roo.get(pel);
29675             }
29676         }
29677         
29678        
29679         
29680         // you can not look for children, as if el is the body.. then everythign is the child..
29681         if (!pel && !el.attr('tooltip')) { //
29682             if (!el.select("[tooltip]").elements.length) {
29683                 return;
29684             }
29685             // is the mouse over this child...?
29686             bindEl = el.select("[tooltip]").first();
29687             var xy = ev.getXY();
29688             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29689                 //Roo.log("not in region.");
29690                 return;
29691             }
29692             //Roo.log("child element over..");
29693             
29694         }
29695         this.currentEl = el;
29696         this.currentTip.bind(bindEl);
29697         this.currentRegion = Roo.lib.Region.getRegion(dom);
29698         this.currentTip.enter();
29699         
29700     },
29701     leave : function(ev)
29702     {
29703         var dom = ev.getTarget();
29704         //Roo.log(['leave',dom]);
29705         if (!this.currentEl) {
29706             return;
29707         }
29708         
29709         
29710         if (dom != this.currentEl.dom) {
29711             return;
29712         }
29713         var xy = ev.getXY();
29714         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29715             return;
29716         }
29717         // only activate leave if mouse cursor is outside... bounding box..
29718         
29719         
29720         
29721         
29722         if (this.currentTip) {
29723             this.currentTip.leave();
29724         }
29725         //Roo.log('clear currentEl');
29726         this.currentEl = false;
29727         
29728         
29729     },
29730     alignment : {
29731         'left' : ['r-l', [-2,0], 'right'],
29732         'right' : ['l-r', [2,0], 'left'],
29733         'bottom' : ['t-b', [0,2], 'top'],
29734         'top' : [ 'b-t', [0,-2], 'bottom']
29735     }
29736     
29737 });
29738
29739
29740 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29741     
29742     
29743     bindEl : false,
29744     
29745     delay : null, // can be { show : 300 , hide: 500}
29746     
29747     timeout : null,
29748     
29749     hoverState : null, //???
29750     
29751     placement : 'bottom', 
29752     
29753     alignment : false,
29754     
29755     getAutoCreate : function(){
29756     
29757         var cfg = {
29758            cls : 'tooltip',   
29759            role : 'tooltip',
29760            cn : [
29761                 {
29762                     cls : 'tooltip-arrow arrow'
29763                 },
29764                 {
29765                     cls : 'tooltip-inner'
29766                 }
29767            ]
29768         };
29769         
29770         return cfg;
29771     },
29772     bind : function(el)
29773     {
29774         this.bindEl = el;
29775     },
29776     
29777     initEvents : function()
29778     {
29779         this.arrowEl = this.el.select('.arrow', true).first();
29780         this.innerEl = this.el.select('.tooltip-inner', true).first();
29781     },
29782     
29783     enter : function () {
29784        
29785         if (this.timeout != null) {
29786             clearTimeout(this.timeout);
29787         }
29788         
29789         this.hoverState = 'in';
29790          //Roo.log("enter - show");
29791         if (!this.delay || !this.delay.show) {
29792             this.show();
29793             return;
29794         }
29795         var _t = this;
29796         this.timeout = setTimeout(function () {
29797             if (_t.hoverState == 'in') {
29798                 _t.show();
29799             }
29800         }, this.delay.show);
29801     },
29802     leave : function()
29803     {
29804         clearTimeout(this.timeout);
29805     
29806         this.hoverState = 'out';
29807          if (!this.delay || !this.delay.hide) {
29808             this.hide();
29809             return;
29810         }
29811        
29812         var _t = this;
29813         this.timeout = setTimeout(function () {
29814             //Roo.log("leave - timeout");
29815             
29816             if (_t.hoverState == 'out') {
29817                 _t.hide();
29818                 Roo.bootstrap.Tooltip.currentEl = false;
29819             }
29820         }, delay);
29821     },
29822     
29823     show : function (msg)
29824     {
29825         if (!this.el) {
29826             this.render(document.body);
29827         }
29828         // set content.
29829         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29830         
29831         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29832         
29833         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29834         
29835         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29836                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29837         
29838         var placement = typeof this.placement == 'function' ?
29839             this.placement.call(this, this.el, on_el) :
29840             this.placement;
29841             
29842         var autoToken = /\s?auto?\s?/i;
29843         var autoPlace = autoToken.test(placement);
29844         if (autoPlace) {
29845             placement = placement.replace(autoToken, '') || 'top';
29846         }
29847         
29848         //this.el.detach()
29849         //this.el.setXY([0,0]);
29850         this.el.show();
29851         //this.el.dom.style.display='block';
29852         
29853         //this.el.appendTo(on_el);
29854         
29855         var p = this.getPosition();
29856         var box = this.el.getBox();
29857         
29858         if (autoPlace) {
29859             // fixme..
29860         }
29861         
29862         var align = this.alignment[placement];
29863         
29864         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29865         
29866         if(placement == 'top' || placement == 'bottom'){
29867             if(xy[0] < 0){
29868                 placement = 'right';
29869             }
29870             
29871             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29872                 placement = 'left';
29873             }
29874             
29875             var scroll = Roo.select('body', true).first().getScroll();
29876             
29877             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29878                 placement = 'top';
29879             }
29880             
29881             align = this.alignment[placement];
29882             
29883             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29884             
29885         }
29886         
29887         var elems = document.getElementsByTagName('div');
29888         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29889         for (var i = 0; i < elems.length; i++) {
29890           var zindex = Number.parseInt(
29891                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29892                 10
29893           );
29894           if (zindex > highest) {
29895             highest = zindex;
29896           }
29897         }
29898         
29899         
29900         
29901         this.el.dom.style.zIndex = highest;
29902         
29903         this.el.alignTo(this.bindEl, align[0],align[1]);
29904         //var arrow = this.el.select('.arrow',true).first();
29905         //arrow.set(align[2], 
29906         
29907         this.el.addClass(placement);
29908         this.el.addClass("bs-tooltip-"+ placement);
29909         
29910         this.el.addClass('in fade show');
29911         
29912         this.hoverState = null;
29913         
29914         if (this.el.hasClass('fade')) {
29915             // fade it?
29916         }
29917         
29918         
29919         
29920         
29921         
29922     },
29923     hide : function()
29924     {
29925          
29926         if (!this.el) {
29927             return;
29928         }
29929         //this.el.setXY([0,0]);
29930         this.el.removeClass(['show', 'in']);
29931         //this.el.hide();
29932         
29933     }
29934     
29935 });
29936  
29937
29938  /*
29939  * - LGPL
29940  *
29941  * Location Picker
29942  * 
29943  */
29944
29945 /**
29946  * @class Roo.bootstrap.LocationPicker
29947  * @extends Roo.bootstrap.Component
29948  * Bootstrap LocationPicker class
29949  * @cfg {Number} latitude Position when init default 0
29950  * @cfg {Number} longitude Position when init default 0
29951  * @cfg {Number} zoom default 15
29952  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29953  * @cfg {Boolean} mapTypeControl default false
29954  * @cfg {Boolean} disableDoubleClickZoom default false
29955  * @cfg {Boolean} scrollwheel default true
29956  * @cfg {Boolean} streetViewControl default false
29957  * @cfg {Number} radius default 0
29958  * @cfg {String} locationName
29959  * @cfg {Boolean} draggable default true
29960  * @cfg {Boolean} enableAutocomplete default false
29961  * @cfg {Boolean} enableReverseGeocode default true
29962  * @cfg {String} markerTitle
29963  * 
29964  * @constructor
29965  * Create a new LocationPicker
29966  * @param {Object} config The config object
29967  */
29968
29969
29970 Roo.bootstrap.LocationPicker = function(config){
29971     
29972     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29973     
29974     this.addEvents({
29975         /**
29976          * @event initial
29977          * Fires when the picker initialized.
29978          * @param {Roo.bootstrap.LocationPicker} this
29979          * @param {Google Location} location
29980          */
29981         initial : true,
29982         /**
29983          * @event positionchanged
29984          * Fires when the picker position changed.
29985          * @param {Roo.bootstrap.LocationPicker} this
29986          * @param {Google Location} location
29987          */
29988         positionchanged : true,
29989         /**
29990          * @event resize
29991          * Fires when the map resize.
29992          * @param {Roo.bootstrap.LocationPicker} this
29993          */
29994         resize : true,
29995         /**
29996          * @event show
29997          * Fires when the map show.
29998          * @param {Roo.bootstrap.LocationPicker} this
29999          */
30000         show : true,
30001         /**
30002          * @event hide
30003          * Fires when the map hide.
30004          * @param {Roo.bootstrap.LocationPicker} this
30005          */
30006         hide : true,
30007         /**
30008          * @event mapClick
30009          * Fires when click the map.
30010          * @param {Roo.bootstrap.LocationPicker} this
30011          * @param {Map event} e
30012          */
30013         mapClick : true,
30014         /**
30015          * @event mapRightClick
30016          * Fires when right click the map.
30017          * @param {Roo.bootstrap.LocationPicker} this
30018          * @param {Map event} e
30019          */
30020         mapRightClick : true,
30021         /**
30022          * @event markerClick
30023          * Fires when click the marker.
30024          * @param {Roo.bootstrap.LocationPicker} this
30025          * @param {Map event} e
30026          */
30027         markerClick : true,
30028         /**
30029          * @event markerRightClick
30030          * Fires when right click the marker.
30031          * @param {Roo.bootstrap.LocationPicker} this
30032          * @param {Map event} e
30033          */
30034         markerRightClick : true,
30035         /**
30036          * @event OverlayViewDraw
30037          * Fires when OverlayView Draw
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          */
30040         OverlayViewDraw : true,
30041         /**
30042          * @event OverlayViewOnAdd
30043          * Fires when OverlayView Draw
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          */
30046         OverlayViewOnAdd : true,
30047         /**
30048          * @event OverlayViewOnRemove
30049          * Fires when OverlayView Draw
30050          * @param {Roo.bootstrap.LocationPicker} this
30051          */
30052         OverlayViewOnRemove : true,
30053         /**
30054          * @event OverlayViewShow
30055          * Fires when OverlayView Draw
30056          * @param {Roo.bootstrap.LocationPicker} this
30057          * @param {Pixel} cpx
30058          */
30059         OverlayViewShow : true,
30060         /**
30061          * @event OverlayViewHide
30062          * Fires when OverlayView Draw
30063          * @param {Roo.bootstrap.LocationPicker} this
30064          */
30065         OverlayViewHide : true,
30066         /**
30067          * @event loadexception
30068          * Fires when load google lib failed.
30069          * @param {Roo.bootstrap.LocationPicker} this
30070          */
30071         loadexception : true
30072     });
30073         
30074 };
30075
30076 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30077     
30078     gMapContext: false,
30079     
30080     latitude: 0,
30081     longitude: 0,
30082     zoom: 15,
30083     mapTypeId: false,
30084     mapTypeControl: false,
30085     disableDoubleClickZoom: false,
30086     scrollwheel: true,
30087     streetViewControl: false,
30088     radius: 0,
30089     locationName: '',
30090     draggable: true,
30091     enableAutocomplete: false,
30092     enableReverseGeocode: true,
30093     markerTitle: '',
30094     
30095     getAutoCreate: function()
30096     {
30097
30098         var cfg = {
30099             tag: 'div',
30100             cls: 'roo-location-picker'
30101         };
30102         
30103         return cfg
30104     },
30105     
30106     initEvents: function(ct, position)
30107     {       
30108         if(!this.el.getWidth() || this.isApplied()){
30109             return;
30110         }
30111         
30112         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30113         
30114         this.initial();
30115     },
30116     
30117     initial: function()
30118     {
30119         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30120             this.fireEvent('loadexception', this);
30121             return;
30122         }
30123         
30124         if(!this.mapTypeId){
30125             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30126         }
30127         
30128         this.gMapContext = this.GMapContext();
30129         
30130         this.initOverlayView();
30131         
30132         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30133         
30134         var _this = this;
30135                 
30136         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30137             _this.setPosition(_this.gMapContext.marker.position);
30138         });
30139         
30140         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30141             _this.fireEvent('mapClick', this, event);
30142             
30143         });
30144
30145         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30146             _this.fireEvent('mapRightClick', this, event);
30147             
30148         });
30149         
30150         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30151             _this.fireEvent('markerClick', this, event);
30152             
30153         });
30154
30155         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30156             _this.fireEvent('markerRightClick', this, event);
30157             
30158         });
30159         
30160         this.setPosition(this.gMapContext.location);
30161         
30162         this.fireEvent('initial', this, this.gMapContext.location);
30163     },
30164     
30165     initOverlayView: function()
30166     {
30167         var _this = this;
30168         
30169         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30170             
30171             draw: function()
30172             {
30173                 _this.fireEvent('OverlayViewDraw', _this);
30174             },
30175             
30176             onAdd: function()
30177             {
30178                 _this.fireEvent('OverlayViewOnAdd', _this);
30179             },
30180             
30181             onRemove: function()
30182             {
30183                 _this.fireEvent('OverlayViewOnRemove', _this);
30184             },
30185             
30186             show: function(cpx)
30187             {
30188                 _this.fireEvent('OverlayViewShow', _this, cpx);
30189             },
30190             
30191             hide: function()
30192             {
30193                 _this.fireEvent('OverlayViewHide', _this);
30194             }
30195             
30196         });
30197     },
30198     
30199     fromLatLngToContainerPixel: function(event)
30200     {
30201         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30202     },
30203     
30204     isApplied: function() 
30205     {
30206         return this.getGmapContext() == false ? false : true;
30207     },
30208     
30209     getGmapContext: function() 
30210     {
30211         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30212     },
30213     
30214     GMapContext: function() 
30215     {
30216         var position = new google.maps.LatLng(this.latitude, this.longitude);
30217         
30218         var _map = new google.maps.Map(this.el.dom, {
30219             center: position,
30220             zoom: this.zoom,
30221             mapTypeId: this.mapTypeId,
30222             mapTypeControl: this.mapTypeControl,
30223             disableDoubleClickZoom: this.disableDoubleClickZoom,
30224             scrollwheel: this.scrollwheel,
30225             streetViewControl: this.streetViewControl,
30226             locationName: this.locationName,
30227             draggable: this.draggable,
30228             enableAutocomplete: this.enableAutocomplete,
30229             enableReverseGeocode: this.enableReverseGeocode
30230         });
30231         
30232         var _marker = new google.maps.Marker({
30233             position: position,
30234             map: _map,
30235             title: this.markerTitle,
30236             draggable: this.draggable
30237         });
30238         
30239         return {
30240             map: _map,
30241             marker: _marker,
30242             circle: null,
30243             location: position,
30244             radius: this.radius,
30245             locationName: this.locationName,
30246             addressComponents: {
30247                 formatted_address: null,
30248                 addressLine1: null,
30249                 addressLine2: null,
30250                 streetName: null,
30251                 streetNumber: null,
30252                 city: null,
30253                 district: null,
30254                 state: null,
30255                 stateOrProvince: null
30256             },
30257             settings: this,
30258             domContainer: this.el.dom,
30259             geodecoder: new google.maps.Geocoder()
30260         };
30261     },
30262     
30263     drawCircle: function(center, radius, options) 
30264     {
30265         if (this.gMapContext.circle != null) {
30266             this.gMapContext.circle.setMap(null);
30267         }
30268         if (radius > 0) {
30269             radius *= 1;
30270             options = Roo.apply({}, options, {
30271                 strokeColor: "#0000FF",
30272                 strokeOpacity: .35,
30273                 strokeWeight: 2,
30274                 fillColor: "#0000FF",
30275                 fillOpacity: .2
30276             });
30277             
30278             options.map = this.gMapContext.map;
30279             options.radius = radius;
30280             options.center = center;
30281             this.gMapContext.circle = new google.maps.Circle(options);
30282             return this.gMapContext.circle;
30283         }
30284         
30285         return null;
30286     },
30287     
30288     setPosition: function(location) 
30289     {
30290         this.gMapContext.location = location;
30291         this.gMapContext.marker.setPosition(location);
30292         this.gMapContext.map.panTo(location);
30293         this.drawCircle(location, this.gMapContext.radius, {});
30294         
30295         var _this = this;
30296         
30297         if (this.gMapContext.settings.enableReverseGeocode) {
30298             this.gMapContext.geodecoder.geocode({
30299                 latLng: this.gMapContext.location
30300             }, function(results, status) {
30301                 
30302                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30303                     _this.gMapContext.locationName = results[0].formatted_address;
30304                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30305                     
30306                     _this.fireEvent('positionchanged', this, location);
30307                 }
30308             });
30309             
30310             return;
30311         }
30312         
30313         this.fireEvent('positionchanged', this, location);
30314     },
30315     
30316     resize: function()
30317     {
30318         google.maps.event.trigger(this.gMapContext.map, "resize");
30319         
30320         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30321         
30322         this.fireEvent('resize', this);
30323     },
30324     
30325     setPositionByLatLng: function(latitude, longitude)
30326     {
30327         this.setPosition(new google.maps.LatLng(latitude, longitude));
30328     },
30329     
30330     getCurrentPosition: function() 
30331     {
30332         return {
30333             latitude: this.gMapContext.location.lat(),
30334             longitude: this.gMapContext.location.lng()
30335         };
30336     },
30337     
30338     getAddressName: function() 
30339     {
30340         return this.gMapContext.locationName;
30341     },
30342     
30343     getAddressComponents: function() 
30344     {
30345         return this.gMapContext.addressComponents;
30346     },
30347     
30348     address_component_from_google_geocode: function(address_components) 
30349     {
30350         var result = {};
30351         
30352         for (var i = 0; i < address_components.length; i++) {
30353             var component = address_components[i];
30354             if (component.types.indexOf("postal_code") >= 0) {
30355                 result.postalCode = component.short_name;
30356             } else if (component.types.indexOf("street_number") >= 0) {
30357                 result.streetNumber = component.short_name;
30358             } else if (component.types.indexOf("route") >= 0) {
30359                 result.streetName = component.short_name;
30360             } else if (component.types.indexOf("neighborhood") >= 0) {
30361                 result.city = component.short_name;
30362             } else if (component.types.indexOf("locality") >= 0) {
30363                 result.city = component.short_name;
30364             } else if (component.types.indexOf("sublocality") >= 0) {
30365                 result.district = component.short_name;
30366             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30367                 result.stateOrProvince = component.short_name;
30368             } else if (component.types.indexOf("country") >= 0) {
30369                 result.country = component.short_name;
30370             }
30371         }
30372         
30373         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30374         result.addressLine2 = "";
30375         return result;
30376     },
30377     
30378     setZoomLevel: function(zoom)
30379     {
30380         this.gMapContext.map.setZoom(zoom);
30381     },
30382     
30383     show: function()
30384     {
30385         if(!this.el){
30386             return;
30387         }
30388         
30389         this.el.show();
30390         
30391         this.resize();
30392         
30393         this.fireEvent('show', this);
30394     },
30395     
30396     hide: function()
30397     {
30398         if(!this.el){
30399             return;
30400         }
30401         
30402         this.el.hide();
30403         
30404         this.fireEvent('hide', this);
30405     }
30406     
30407 });
30408
30409 Roo.apply(Roo.bootstrap.LocationPicker, {
30410     
30411     OverlayView : function(map, options)
30412     {
30413         options = options || {};
30414         
30415         this.setMap(map);
30416     }
30417     
30418     
30419 });/**
30420  * @class Roo.bootstrap.Alert
30421  * @extends Roo.bootstrap.Component
30422  * Bootstrap Alert class - shows an alert area box
30423  * eg
30424  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30425   Enter a valid email address
30426 </div>
30427  * @licence LGPL
30428  * @cfg {String} title The title of alert
30429  * @cfg {String} html The content of alert
30430  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30431  * @cfg {String} fa font-awesomeicon
30432  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30433  * @cfg {Boolean} close true to show a x closer
30434  * 
30435  * 
30436  * @constructor
30437  * Create a new alert
30438  * @param {Object} config The config object
30439  */
30440
30441
30442 Roo.bootstrap.Alert = function(config){
30443     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30444     
30445 };
30446
30447 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30448     
30449     title: '',
30450     html: '',
30451     weight: false,
30452     fa: false,
30453     faicon: false, // BC
30454     close : false,
30455     
30456     
30457     getAutoCreate : function()
30458     {
30459         
30460         var cfg = {
30461             tag : 'div',
30462             cls : 'alert',
30463             cn : [
30464                 {
30465                     tag: 'button',
30466                     type :  "button",
30467                     cls: "close",
30468                     html : '×',
30469                     style : this.close ? '' : 'display:none'
30470                 },
30471                 {
30472                     tag : 'i',
30473                     cls : 'roo-alert-icon'
30474                     
30475                 },
30476                 {
30477                     tag : 'b',
30478                     cls : 'roo-alert-title',
30479                     html : this.title
30480                 },
30481                 {
30482                     tag : 'span',
30483                     cls : 'roo-alert-text',
30484                     html : this.html
30485                 }
30486             ]
30487         };
30488         
30489         if(this.faicon){
30490             cfg.cn[0].cls += ' fa ' + this.faicon;
30491         }
30492         if(this.fa){
30493             cfg.cn[0].cls += ' fa ' + this.fa;
30494         }
30495         
30496         if(this.weight){
30497             cfg.cls += ' alert-' + this.weight;
30498         }
30499         
30500         return cfg;
30501     },
30502     
30503     initEvents: function() 
30504     {
30505         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30506         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30507         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30508         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30509         if (this.seconds > 0) {
30510             this.hide.defer(this.seconds, this);
30511         }
30512     },
30513     /**
30514      * Set the Title Message HTML
30515      * @param {String} html
30516      */
30517     setTitle : function(str)
30518     {
30519         this.titleEl.dom.innerHTML = str;
30520     },
30521      
30522      /**
30523      * Set the Body Message HTML
30524      * @param {String} html
30525      */
30526     setHtml : function(str)
30527     {
30528         this.htmlEl.dom.innerHTML = str;
30529     },
30530     /**
30531      * Set the Weight of the alert
30532      * @param {String} (success|info|warning|danger) weight
30533      */
30534     
30535     setWeight : function(weight)
30536     {
30537         if(this.weight){
30538             this.el.removeClass('alert-' + this.weight);
30539         }
30540         
30541         this.weight = weight;
30542         
30543         this.el.addClass('alert-' + this.weight);
30544     },
30545       /**
30546      * Set the Icon of the alert
30547      * @param {String} see fontawsome names (name without the 'fa-' bit)
30548      */
30549     setIcon : function(icon)
30550     {
30551         if(this.faicon){
30552             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30553         }
30554         
30555         this.faicon = icon;
30556         
30557         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30558     },
30559     /**
30560      * Hide the Alert
30561      */
30562     hide: function() 
30563     {
30564         this.el.hide();   
30565     },
30566     /**
30567      * Show the Alert
30568      */
30569     show: function() 
30570     {  
30571         this.el.show();   
30572     }
30573     
30574 });
30575
30576  
30577 /*
30578 * Licence: LGPL
30579 */
30580
30581 /**
30582  * @class Roo.bootstrap.UploadCropbox
30583  * @extends Roo.bootstrap.Component
30584  * Bootstrap UploadCropbox class
30585  * @cfg {String} emptyText show when image has been loaded
30586  * @cfg {String} rotateNotify show when image too small to rotate
30587  * @cfg {Number} errorTimeout default 3000
30588  * @cfg {Number} minWidth default 300
30589  * @cfg {Number} minHeight default 300
30590  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30591  * @cfg {Boolean} isDocument (true|false) default false
30592  * @cfg {String} url action url
30593  * @cfg {String} paramName default 'imageUpload'
30594  * @cfg {String} method default POST
30595  * @cfg {Boolean} loadMask (true|false) default true
30596  * @cfg {Boolean} loadingText default 'Loading...'
30597  * 
30598  * @constructor
30599  * Create a new UploadCropbox
30600  * @param {Object} config The config object
30601  */
30602
30603 Roo.bootstrap.UploadCropbox = function(config){
30604     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30605     
30606     this.addEvents({
30607         /**
30608          * @event beforeselectfile
30609          * Fire before select file
30610          * @param {Roo.bootstrap.UploadCropbox} this
30611          */
30612         "beforeselectfile" : true,
30613         /**
30614          * @event initial
30615          * Fire after initEvent
30616          * @param {Roo.bootstrap.UploadCropbox} this
30617          */
30618         "initial" : true,
30619         /**
30620          * @event crop
30621          * Fire after initEvent
30622          * @param {Roo.bootstrap.UploadCropbox} this
30623          * @param {String} data
30624          */
30625         "crop" : true,
30626         /**
30627          * @event prepare
30628          * Fire when preparing the file data
30629          * @param {Roo.bootstrap.UploadCropbox} this
30630          * @param {Object} file
30631          */
30632         "prepare" : true,
30633         /**
30634          * @event exception
30635          * Fire when get exception
30636          * @param {Roo.bootstrap.UploadCropbox} this
30637          * @param {XMLHttpRequest} xhr
30638          */
30639         "exception" : true,
30640         /**
30641          * @event beforeloadcanvas
30642          * Fire before load the canvas
30643          * @param {Roo.bootstrap.UploadCropbox} this
30644          * @param {String} src
30645          */
30646         "beforeloadcanvas" : true,
30647         /**
30648          * @event trash
30649          * Fire when trash image
30650          * @param {Roo.bootstrap.UploadCropbox} this
30651          */
30652         "trash" : true,
30653         /**
30654          * @event download
30655          * Fire when download the image
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          */
30658         "download" : true,
30659         /**
30660          * @event footerbuttonclick
30661          * Fire when footerbuttonclick
30662          * @param {Roo.bootstrap.UploadCropbox} this
30663          * @param {String} type
30664          */
30665         "footerbuttonclick" : true,
30666         /**
30667          * @event resize
30668          * Fire when resize
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          */
30671         "resize" : true,
30672         /**
30673          * @event rotate
30674          * Fire when rotate the image
30675          * @param {Roo.bootstrap.UploadCropbox} this
30676          * @param {String} pos
30677          */
30678         "rotate" : true,
30679         /**
30680          * @event inspect
30681          * Fire when inspect the file
30682          * @param {Roo.bootstrap.UploadCropbox} this
30683          * @param {Object} file
30684          */
30685         "inspect" : true,
30686         /**
30687          * @event upload
30688          * Fire when xhr upload the file
30689          * @param {Roo.bootstrap.UploadCropbox} this
30690          * @param {Object} data
30691          */
30692         "upload" : true,
30693         /**
30694          * @event arrange
30695          * Fire when arrange the file data
30696          * @param {Roo.bootstrap.UploadCropbox} this
30697          * @param {Object} formData
30698          */
30699         "arrange" : true
30700     });
30701     
30702     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30703 };
30704
30705 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30706     
30707     emptyText : 'Click to upload image',
30708     rotateNotify : 'Image is too small to rotate',
30709     errorTimeout : 3000,
30710     scale : 0,
30711     baseScale : 1,
30712     rotate : 0,
30713     dragable : false,
30714     pinching : false,
30715     mouseX : 0,
30716     mouseY : 0,
30717     cropData : false,
30718     minWidth : 300,
30719     minHeight : 300,
30720     file : false,
30721     exif : {},
30722     baseRotate : 1,
30723     cropType : 'image/jpeg',
30724     buttons : false,
30725     canvasLoaded : false,
30726     isDocument : false,
30727     method : 'POST',
30728     paramName : 'imageUpload',
30729     loadMask : true,
30730     loadingText : 'Loading...',
30731     maskEl : false,
30732     
30733     getAutoCreate : function()
30734     {
30735         var cfg = {
30736             tag : 'div',
30737             cls : 'roo-upload-cropbox',
30738             cn : [
30739                 {
30740                     tag : 'input',
30741                     cls : 'roo-upload-cropbox-selector',
30742                     type : 'file'
30743                 },
30744                 {
30745                     tag : 'div',
30746                     cls : 'roo-upload-cropbox-body',
30747                     style : 'cursor:pointer',
30748                     cn : [
30749                         {
30750                             tag : 'div',
30751                             cls : 'roo-upload-cropbox-preview'
30752                         },
30753                         {
30754                             tag : 'div',
30755                             cls : 'roo-upload-cropbox-thumb'
30756                         },
30757                         {
30758                             tag : 'div',
30759                             cls : 'roo-upload-cropbox-empty-notify',
30760                             html : this.emptyText
30761                         },
30762                         {
30763                             tag : 'div',
30764                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30765                             html : this.rotateNotify
30766                         }
30767                     ]
30768                 },
30769                 {
30770                     tag : 'div',
30771                     cls : 'roo-upload-cropbox-footer',
30772                     cn : {
30773                         tag : 'div',
30774                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30775                         cn : []
30776                     }
30777                 }
30778             ]
30779         };
30780         
30781         return cfg;
30782     },
30783     
30784     onRender : function(ct, position)
30785     {
30786         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30787         
30788         if (this.buttons.length) {
30789             
30790             Roo.each(this.buttons, function(bb) {
30791                 
30792                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30793                 
30794                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30795                 
30796             }, this);
30797         }
30798         
30799         if(this.loadMask){
30800             this.maskEl = this.el;
30801         }
30802     },
30803     
30804     initEvents : function()
30805     {
30806         this.urlAPI = (window.createObjectURL && window) || 
30807                                 (window.URL && URL.revokeObjectURL && URL) || 
30808                                 (window.webkitURL && webkitURL);
30809                         
30810         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30811         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30812         
30813         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30814         this.selectorEl.hide();
30815         
30816         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30817         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30818         
30819         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30820         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30821         this.thumbEl.hide();
30822         
30823         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30824         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30825         
30826         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30827         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30828         this.errorEl.hide();
30829         
30830         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30831         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30832         this.footerEl.hide();
30833         
30834         this.setThumbBoxSize();
30835         
30836         this.bind();
30837         
30838         this.resize();
30839         
30840         this.fireEvent('initial', this);
30841     },
30842
30843     bind : function()
30844     {
30845         var _this = this;
30846         
30847         window.addEventListener("resize", function() { _this.resize(); } );
30848         
30849         this.bodyEl.on('click', this.beforeSelectFile, this);
30850         
30851         if(Roo.isTouch){
30852             this.bodyEl.on('touchstart', this.onTouchStart, this);
30853             this.bodyEl.on('touchmove', this.onTouchMove, this);
30854             this.bodyEl.on('touchend', this.onTouchEnd, this);
30855         }
30856         
30857         if(!Roo.isTouch){
30858             this.bodyEl.on('mousedown', this.onMouseDown, this);
30859             this.bodyEl.on('mousemove', this.onMouseMove, this);
30860             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30861             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30862             Roo.get(document).on('mouseup', this.onMouseUp, this);
30863         }
30864         
30865         this.selectorEl.on('change', this.onFileSelected, this);
30866     },
30867     
30868     reset : function()
30869     {    
30870         this.scale = 0;
30871         this.baseScale = 1;
30872         this.rotate = 0;
30873         this.baseRotate = 1;
30874         this.dragable = false;
30875         this.pinching = false;
30876         this.mouseX = 0;
30877         this.mouseY = 0;
30878         this.cropData = false;
30879         this.notifyEl.dom.innerHTML = this.emptyText;
30880         
30881         this.selectorEl.dom.value = '';
30882         
30883     },
30884     
30885     resize : function()
30886     {
30887         if(this.fireEvent('resize', this) != false){
30888             this.setThumbBoxPosition();
30889             this.setCanvasPosition();
30890         }
30891     },
30892     
30893     onFooterButtonClick : function(e, el, o, type)
30894     {
30895         switch (type) {
30896             case 'rotate-left' :
30897                 this.onRotateLeft(e);
30898                 break;
30899             case 'rotate-right' :
30900                 this.onRotateRight(e);
30901                 break;
30902             case 'picture' :
30903                 this.beforeSelectFile(e);
30904                 break;
30905             case 'trash' :
30906                 this.trash(e);
30907                 break;
30908             case 'crop' :
30909                 this.crop(e);
30910                 break;
30911             case 'download' :
30912                 this.download(e);
30913                 break;
30914             default :
30915                 break;
30916         }
30917         
30918         this.fireEvent('footerbuttonclick', this, type);
30919     },
30920     
30921     beforeSelectFile : function(e)
30922     {
30923         e.preventDefault();
30924         
30925         if(this.fireEvent('beforeselectfile', this) != false){
30926             this.selectorEl.dom.click();
30927         }
30928     },
30929     
30930     onFileSelected : function(e)
30931     {
30932         e.preventDefault();
30933         
30934         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30935             return;
30936         }
30937         
30938         var file = this.selectorEl.dom.files[0];
30939         
30940         if(this.fireEvent('inspect', this, file) != false){
30941             this.prepare(file);
30942         }
30943         
30944     },
30945     
30946     trash : function(e)
30947     {
30948         this.fireEvent('trash', this);
30949     },
30950     
30951     download : function(e)
30952     {
30953         this.fireEvent('download', this);
30954     },
30955     
30956     loadCanvas : function(src)
30957     {   
30958         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30959             
30960             this.reset();
30961             
30962             this.imageEl = document.createElement('img');
30963             
30964             var _this = this;
30965             
30966             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30967             
30968             this.imageEl.src = src;
30969         }
30970     },
30971     
30972     onLoadCanvas : function()
30973     {   
30974         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30975         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30976         
30977         this.bodyEl.un('click', this.beforeSelectFile, this);
30978         
30979         this.notifyEl.hide();
30980         this.thumbEl.show();
30981         this.footerEl.show();
30982         
30983         this.baseRotateLevel();
30984         
30985         if(this.isDocument){
30986             this.setThumbBoxSize();
30987         }
30988         
30989         this.setThumbBoxPosition();
30990         
30991         this.baseScaleLevel();
30992         
30993         this.draw();
30994         
30995         this.resize();
30996         
30997         this.canvasLoaded = true;
30998         
30999         if(this.loadMask){
31000             this.maskEl.unmask();
31001         }
31002         
31003     },
31004     
31005     setCanvasPosition : function()
31006     {   
31007         if(!this.canvasEl){
31008             return;
31009         }
31010         
31011         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31012         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31013         
31014         this.previewEl.setLeft(pw);
31015         this.previewEl.setTop(ph);
31016         
31017     },
31018     
31019     onMouseDown : function(e)
31020     {   
31021         e.stopEvent();
31022         
31023         this.dragable = true;
31024         this.pinching = false;
31025         
31026         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31027             this.dragable = false;
31028             return;
31029         }
31030         
31031         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31032         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31033         
31034     },
31035     
31036     onMouseMove : function(e)
31037     {   
31038         e.stopEvent();
31039         
31040         if(!this.canvasLoaded){
31041             return;
31042         }
31043         
31044         if (!this.dragable){
31045             return;
31046         }
31047         
31048         var minX = Math.ceil(this.thumbEl.getLeft(true));
31049         var minY = Math.ceil(this.thumbEl.getTop(true));
31050         
31051         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31052         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31053         
31054         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31055         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31056         
31057         x = x - this.mouseX;
31058         y = y - this.mouseY;
31059         
31060         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31061         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31062         
31063         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31064         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31065         
31066         this.previewEl.setLeft(bgX);
31067         this.previewEl.setTop(bgY);
31068         
31069         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31070         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31071     },
31072     
31073     onMouseUp : function(e)
31074     {   
31075         e.stopEvent();
31076         
31077         this.dragable = false;
31078     },
31079     
31080     onMouseWheel : function(e)
31081     {   
31082         e.stopEvent();
31083         
31084         this.startScale = this.scale;
31085         
31086         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31087         
31088         if(!this.zoomable()){
31089             this.scale = this.startScale;
31090             return;
31091         }
31092         
31093         this.draw();
31094         
31095         return;
31096     },
31097     
31098     zoomable : function()
31099     {
31100         var minScale = this.thumbEl.getWidth() / this.minWidth;
31101         
31102         if(this.minWidth < this.minHeight){
31103             minScale = this.thumbEl.getHeight() / this.minHeight;
31104         }
31105         
31106         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31107         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31108         
31109         if(
31110                 this.isDocument &&
31111                 (this.rotate == 0 || this.rotate == 180) && 
31112                 (
31113                     width > this.imageEl.OriginWidth || 
31114                     height > this.imageEl.OriginHeight ||
31115                     (width < this.minWidth && height < this.minHeight)
31116                 )
31117         ){
31118             return false;
31119         }
31120         
31121         if(
31122                 this.isDocument &&
31123                 (this.rotate == 90 || this.rotate == 270) && 
31124                 (
31125                     width > this.imageEl.OriginWidth || 
31126                     height > this.imageEl.OriginHeight ||
31127                     (width < this.minHeight && height < this.minWidth)
31128                 )
31129         ){
31130             return false;
31131         }
31132         
31133         if(
31134                 !this.isDocument &&
31135                 (this.rotate == 0 || this.rotate == 180) && 
31136                 (
31137                     width < this.minWidth || 
31138                     width > this.imageEl.OriginWidth || 
31139                     height < this.minHeight || 
31140                     height > this.imageEl.OriginHeight
31141                 )
31142         ){
31143             return false;
31144         }
31145         
31146         if(
31147                 !this.isDocument &&
31148                 (this.rotate == 90 || this.rotate == 270) && 
31149                 (
31150                     width < this.minHeight || 
31151                     width > this.imageEl.OriginWidth || 
31152                     height < this.minWidth || 
31153                     height > this.imageEl.OriginHeight
31154                 )
31155         ){
31156             return false;
31157         }
31158         
31159         return true;
31160         
31161     },
31162     
31163     onRotateLeft : function(e)
31164     {   
31165         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31166             
31167             var minScale = this.thumbEl.getWidth() / this.minWidth;
31168             
31169             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31170             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31171             
31172             this.startScale = this.scale;
31173             
31174             while (this.getScaleLevel() < minScale){
31175             
31176                 this.scale = this.scale + 1;
31177                 
31178                 if(!this.zoomable()){
31179                     break;
31180                 }
31181                 
31182                 if(
31183                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31184                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31185                 ){
31186                     continue;
31187                 }
31188                 
31189                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31190
31191                 this.draw();
31192                 
31193                 return;
31194             }
31195             
31196             this.scale = this.startScale;
31197             
31198             this.onRotateFail();
31199             
31200             return false;
31201         }
31202         
31203         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31204
31205         if(this.isDocument){
31206             this.setThumbBoxSize();
31207             this.setThumbBoxPosition();
31208             this.setCanvasPosition();
31209         }
31210         
31211         this.draw();
31212         
31213         this.fireEvent('rotate', this, 'left');
31214         
31215     },
31216     
31217     onRotateRight : function(e)
31218     {
31219         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31220             
31221             var minScale = this.thumbEl.getWidth() / this.minWidth;
31222         
31223             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31224             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31225             
31226             this.startScale = this.scale;
31227             
31228             while (this.getScaleLevel() < minScale){
31229             
31230                 this.scale = this.scale + 1;
31231                 
31232                 if(!this.zoomable()){
31233                     break;
31234                 }
31235                 
31236                 if(
31237                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31238                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31239                 ){
31240                     continue;
31241                 }
31242                 
31243                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31244
31245                 this.draw();
31246                 
31247                 return;
31248             }
31249             
31250             this.scale = this.startScale;
31251             
31252             this.onRotateFail();
31253             
31254             return false;
31255         }
31256         
31257         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31258
31259         if(this.isDocument){
31260             this.setThumbBoxSize();
31261             this.setThumbBoxPosition();
31262             this.setCanvasPosition();
31263         }
31264         
31265         this.draw();
31266         
31267         this.fireEvent('rotate', this, 'right');
31268     },
31269     
31270     onRotateFail : function()
31271     {
31272         this.errorEl.show(true);
31273         
31274         var _this = this;
31275         
31276         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31277     },
31278     
31279     draw : function()
31280     {
31281         this.previewEl.dom.innerHTML = '';
31282         
31283         var canvasEl = document.createElement("canvas");
31284         
31285         var contextEl = canvasEl.getContext("2d");
31286         
31287         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31288         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31289         var center = this.imageEl.OriginWidth / 2;
31290         
31291         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31292             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31293             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31294             center = this.imageEl.OriginHeight / 2;
31295         }
31296         
31297         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31298         
31299         contextEl.translate(center, center);
31300         contextEl.rotate(this.rotate * Math.PI / 180);
31301
31302         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31303         
31304         this.canvasEl = document.createElement("canvas");
31305         
31306         this.contextEl = this.canvasEl.getContext("2d");
31307         
31308         switch (this.rotate) {
31309             case 0 :
31310                 
31311                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31312                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31313                 
31314                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31315                 
31316                 break;
31317             case 90 : 
31318                 
31319                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31320                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31321                 
31322                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31323                     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);
31324                     break;
31325                 }
31326                 
31327                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31328                 
31329                 break;
31330             case 180 :
31331                 
31332                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31333                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31334                 
31335                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31336                     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);
31337                     break;
31338                 }
31339                 
31340                 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);
31341                 
31342                 break;
31343             case 270 :
31344                 
31345                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31346                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31347         
31348                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31349                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31350                     break;
31351                 }
31352                 
31353                 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);
31354                 
31355                 break;
31356             default : 
31357                 break;
31358         }
31359         
31360         this.previewEl.appendChild(this.canvasEl);
31361         
31362         this.setCanvasPosition();
31363     },
31364     
31365     crop : function()
31366     {
31367         if(!this.canvasLoaded){
31368             return;
31369         }
31370         
31371         var imageCanvas = document.createElement("canvas");
31372         
31373         var imageContext = imageCanvas.getContext("2d");
31374         
31375         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31376         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31377         
31378         var center = imageCanvas.width / 2;
31379         
31380         imageContext.translate(center, center);
31381         
31382         imageContext.rotate(this.rotate * Math.PI / 180);
31383         
31384         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31385         
31386         var canvas = document.createElement("canvas");
31387         
31388         var context = canvas.getContext("2d");
31389                 
31390         canvas.width = this.minWidth;
31391         canvas.height = this.minHeight;
31392
31393         switch (this.rotate) {
31394             case 0 :
31395                 
31396                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31397                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31398                 
31399                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31400                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31401                 
31402                 var targetWidth = this.minWidth - 2 * x;
31403                 var targetHeight = this.minHeight - 2 * y;
31404                 
31405                 var scale = 1;
31406                 
31407                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31408                     scale = targetWidth / width;
31409                 }
31410                 
31411                 if(x > 0 && y == 0){
31412                     scale = targetHeight / height;
31413                 }
31414                 
31415                 if(x > 0 && y > 0){
31416                     scale = targetWidth / width;
31417                     
31418                     if(width < height){
31419                         scale = targetHeight / height;
31420                     }
31421                 }
31422                 
31423                 context.scale(scale, scale);
31424                 
31425                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31426                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31427
31428                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31429                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31430
31431                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31432                 
31433                 break;
31434             case 90 : 
31435                 
31436                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31437                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31438                 
31439                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31440                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31441                 
31442                 var targetWidth = this.minWidth - 2 * x;
31443                 var targetHeight = this.minHeight - 2 * y;
31444                 
31445                 var scale = 1;
31446                 
31447                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31448                     scale = targetWidth / width;
31449                 }
31450                 
31451                 if(x > 0 && y == 0){
31452                     scale = targetHeight / height;
31453                 }
31454                 
31455                 if(x > 0 && y > 0){
31456                     scale = targetWidth / width;
31457                     
31458                     if(width < height){
31459                         scale = targetHeight / height;
31460                     }
31461                 }
31462                 
31463                 context.scale(scale, scale);
31464                 
31465                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31466                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31467
31468                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31469                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31470                 
31471                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31472                 
31473                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31474                 
31475                 break;
31476             case 180 :
31477                 
31478                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31479                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31480                 
31481                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31482                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31483                 
31484                 var targetWidth = this.minWidth - 2 * x;
31485                 var targetHeight = this.minHeight - 2 * y;
31486                 
31487                 var scale = 1;
31488                 
31489                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31490                     scale = targetWidth / width;
31491                 }
31492                 
31493                 if(x > 0 && y == 0){
31494                     scale = targetHeight / height;
31495                 }
31496                 
31497                 if(x > 0 && y > 0){
31498                     scale = targetWidth / width;
31499                     
31500                     if(width < height){
31501                         scale = targetHeight / height;
31502                     }
31503                 }
31504                 
31505                 context.scale(scale, scale);
31506                 
31507                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31508                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31509
31510                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31511                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31512
31513                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31514                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31515                 
31516                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31517                 
31518                 break;
31519             case 270 :
31520                 
31521                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31522                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31523                 
31524                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31525                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31526                 
31527                 var targetWidth = this.minWidth - 2 * x;
31528                 var targetHeight = this.minHeight - 2 * y;
31529                 
31530                 var scale = 1;
31531                 
31532                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31533                     scale = targetWidth / width;
31534                 }
31535                 
31536                 if(x > 0 && y == 0){
31537                     scale = targetHeight / height;
31538                 }
31539                 
31540                 if(x > 0 && y > 0){
31541                     scale = targetWidth / width;
31542                     
31543                     if(width < height){
31544                         scale = targetHeight / height;
31545                     }
31546                 }
31547                 
31548                 context.scale(scale, scale);
31549                 
31550                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31551                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31552
31553                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31554                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31555                 
31556                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31557                 
31558                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31559                 
31560                 break;
31561             default : 
31562                 break;
31563         }
31564         
31565         this.cropData = canvas.toDataURL(this.cropType);
31566         
31567         if(this.fireEvent('crop', this, this.cropData) !== false){
31568             this.process(this.file, this.cropData);
31569         }
31570         
31571         return;
31572         
31573     },
31574     
31575     setThumbBoxSize : function()
31576     {
31577         var width, height;
31578         
31579         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31580             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31581             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31582             
31583             this.minWidth = width;
31584             this.minHeight = height;
31585             
31586             if(this.rotate == 90 || this.rotate == 270){
31587                 this.minWidth = height;
31588                 this.minHeight = width;
31589             }
31590         }
31591         
31592         height = 300;
31593         width = Math.ceil(this.minWidth * height / this.minHeight);
31594         
31595         if(this.minWidth > this.minHeight){
31596             width = 300;
31597             height = Math.ceil(this.minHeight * width / this.minWidth);
31598         }
31599         
31600         this.thumbEl.setStyle({
31601             width : width + 'px',
31602             height : height + 'px'
31603         });
31604
31605         return;
31606             
31607     },
31608     
31609     setThumbBoxPosition : function()
31610     {
31611         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31612         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31613         
31614         this.thumbEl.setLeft(x);
31615         this.thumbEl.setTop(y);
31616         
31617     },
31618     
31619     baseRotateLevel : function()
31620     {
31621         this.baseRotate = 1;
31622         
31623         if(
31624                 typeof(this.exif) != 'undefined' &&
31625                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31626                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31627         ){
31628             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31629         }
31630         
31631         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31632         
31633     },
31634     
31635     baseScaleLevel : function()
31636     {
31637         var width, height;
31638         
31639         if(this.isDocument){
31640             
31641             if(this.baseRotate == 6 || this.baseRotate == 8){
31642             
31643                 height = this.thumbEl.getHeight();
31644                 this.baseScale = height / this.imageEl.OriginWidth;
31645
31646                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31647                     width = this.thumbEl.getWidth();
31648                     this.baseScale = width / this.imageEl.OriginHeight;
31649                 }
31650
31651                 return;
31652             }
31653
31654             height = this.thumbEl.getHeight();
31655             this.baseScale = height / this.imageEl.OriginHeight;
31656
31657             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31658                 width = this.thumbEl.getWidth();
31659                 this.baseScale = width / this.imageEl.OriginWidth;
31660             }
31661
31662             return;
31663         }
31664         
31665         if(this.baseRotate == 6 || this.baseRotate == 8){
31666             
31667             width = this.thumbEl.getHeight();
31668             this.baseScale = width / this.imageEl.OriginHeight;
31669             
31670             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31671                 height = this.thumbEl.getWidth();
31672                 this.baseScale = height / this.imageEl.OriginHeight;
31673             }
31674             
31675             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31676                 height = this.thumbEl.getWidth();
31677                 this.baseScale = height / this.imageEl.OriginHeight;
31678                 
31679                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31680                     width = this.thumbEl.getHeight();
31681                     this.baseScale = width / this.imageEl.OriginWidth;
31682                 }
31683             }
31684             
31685             return;
31686         }
31687         
31688         width = this.thumbEl.getWidth();
31689         this.baseScale = width / this.imageEl.OriginWidth;
31690         
31691         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31692             height = this.thumbEl.getHeight();
31693             this.baseScale = height / this.imageEl.OriginHeight;
31694         }
31695         
31696         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31697             
31698             height = this.thumbEl.getHeight();
31699             this.baseScale = height / this.imageEl.OriginHeight;
31700             
31701             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31702                 width = this.thumbEl.getWidth();
31703                 this.baseScale = width / this.imageEl.OriginWidth;
31704             }
31705             
31706         }
31707         
31708         return;
31709     },
31710     
31711     getScaleLevel : function()
31712     {
31713         return this.baseScale * Math.pow(1.1, this.scale);
31714     },
31715     
31716     onTouchStart : function(e)
31717     {
31718         if(!this.canvasLoaded){
31719             this.beforeSelectFile(e);
31720             return;
31721         }
31722         
31723         var touches = e.browserEvent.touches;
31724         
31725         if(!touches){
31726             return;
31727         }
31728         
31729         if(touches.length == 1){
31730             this.onMouseDown(e);
31731             return;
31732         }
31733         
31734         if(touches.length != 2){
31735             return;
31736         }
31737         
31738         var coords = [];
31739         
31740         for(var i = 0, finger; finger = touches[i]; i++){
31741             coords.push(finger.pageX, finger.pageY);
31742         }
31743         
31744         var x = Math.pow(coords[0] - coords[2], 2);
31745         var y = Math.pow(coords[1] - coords[3], 2);
31746         
31747         this.startDistance = Math.sqrt(x + y);
31748         
31749         this.startScale = this.scale;
31750         
31751         this.pinching = true;
31752         this.dragable = false;
31753         
31754     },
31755     
31756     onTouchMove : function(e)
31757     {
31758         if(!this.pinching && !this.dragable){
31759             return;
31760         }
31761         
31762         var touches = e.browserEvent.touches;
31763         
31764         if(!touches){
31765             return;
31766         }
31767         
31768         if(this.dragable){
31769             this.onMouseMove(e);
31770             return;
31771         }
31772         
31773         var coords = [];
31774         
31775         for(var i = 0, finger; finger = touches[i]; i++){
31776             coords.push(finger.pageX, finger.pageY);
31777         }
31778         
31779         var x = Math.pow(coords[0] - coords[2], 2);
31780         var y = Math.pow(coords[1] - coords[3], 2);
31781         
31782         this.endDistance = Math.sqrt(x + y);
31783         
31784         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31785         
31786         if(!this.zoomable()){
31787             this.scale = this.startScale;
31788             return;
31789         }
31790         
31791         this.draw();
31792         
31793     },
31794     
31795     onTouchEnd : function(e)
31796     {
31797         this.pinching = false;
31798         this.dragable = false;
31799         
31800     },
31801     
31802     process : function(file, crop)
31803     {
31804         if(this.loadMask){
31805             this.maskEl.mask(this.loadingText);
31806         }
31807         
31808         this.xhr = new XMLHttpRequest();
31809         
31810         file.xhr = this.xhr;
31811
31812         this.xhr.open(this.method, this.url, true);
31813         
31814         var headers = {
31815             "Accept": "application/json",
31816             "Cache-Control": "no-cache",
31817             "X-Requested-With": "XMLHttpRequest"
31818         };
31819         
31820         for (var headerName in headers) {
31821             var headerValue = headers[headerName];
31822             if (headerValue) {
31823                 this.xhr.setRequestHeader(headerName, headerValue);
31824             }
31825         }
31826         
31827         var _this = this;
31828         
31829         this.xhr.onload = function()
31830         {
31831             _this.xhrOnLoad(_this.xhr);
31832         }
31833         
31834         this.xhr.onerror = function()
31835         {
31836             _this.xhrOnError(_this.xhr);
31837         }
31838         
31839         var formData = new FormData();
31840
31841         formData.append('returnHTML', 'NO');
31842         
31843         if(crop){
31844             formData.append('crop', crop);
31845         }
31846         
31847         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31848             formData.append(this.paramName, file, file.name);
31849         }
31850         
31851         if(typeof(file.filename) != 'undefined'){
31852             formData.append('filename', file.filename);
31853         }
31854         
31855         if(typeof(file.mimetype) != 'undefined'){
31856             formData.append('mimetype', file.mimetype);
31857         }
31858         
31859         if(this.fireEvent('arrange', this, formData) != false){
31860             this.xhr.send(formData);
31861         };
31862     },
31863     
31864     xhrOnLoad : function(xhr)
31865     {
31866         if(this.loadMask){
31867             this.maskEl.unmask();
31868         }
31869         
31870         if (xhr.readyState !== 4) {
31871             this.fireEvent('exception', this, xhr);
31872             return;
31873         }
31874
31875         var response = Roo.decode(xhr.responseText);
31876         
31877         if(!response.success){
31878             this.fireEvent('exception', this, xhr);
31879             return;
31880         }
31881         
31882         var response = Roo.decode(xhr.responseText);
31883         
31884         this.fireEvent('upload', this, response);
31885         
31886     },
31887     
31888     xhrOnError : function()
31889     {
31890         if(this.loadMask){
31891             this.maskEl.unmask();
31892         }
31893         
31894         Roo.log('xhr on error');
31895         
31896         var response = Roo.decode(xhr.responseText);
31897           
31898         Roo.log(response);
31899         
31900     },
31901     
31902     prepare : function(file)
31903     {   
31904         if(this.loadMask){
31905             this.maskEl.mask(this.loadingText);
31906         }
31907         
31908         this.file = false;
31909         this.exif = {};
31910         
31911         if(typeof(file) === 'string'){
31912             this.loadCanvas(file);
31913             return;
31914         }
31915         
31916         if(!file || !this.urlAPI){
31917             return;
31918         }
31919         
31920         this.file = file;
31921         this.cropType = file.type;
31922         
31923         var _this = this;
31924         
31925         if(this.fireEvent('prepare', this, this.file) != false){
31926             
31927             var reader = new FileReader();
31928             
31929             reader.onload = function (e) {
31930                 if (e.target.error) {
31931                     Roo.log(e.target.error);
31932                     return;
31933                 }
31934                 
31935                 var buffer = e.target.result,
31936                     dataView = new DataView(buffer),
31937                     offset = 2,
31938                     maxOffset = dataView.byteLength - 4,
31939                     markerBytes,
31940                     markerLength;
31941                 
31942                 if (dataView.getUint16(0) === 0xffd8) {
31943                     while (offset < maxOffset) {
31944                         markerBytes = dataView.getUint16(offset);
31945                         
31946                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31947                             markerLength = dataView.getUint16(offset + 2) + 2;
31948                             if (offset + markerLength > dataView.byteLength) {
31949                                 Roo.log('Invalid meta data: Invalid segment size.');
31950                                 break;
31951                             }
31952                             
31953                             if(markerBytes == 0xffe1){
31954                                 _this.parseExifData(
31955                                     dataView,
31956                                     offset,
31957                                     markerLength
31958                                 );
31959                             }
31960                             
31961                             offset += markerLength;
31962                             
31963                             continue;
31964                         }
31965                         
31966                         break;
31967                     }
31968                     
31969                 }
31970                 
31971                 var url = _this.urlAPI.createObjectURL(_this.file);
31972                 
31973                 _this.loadCanvas(url);
31974                 
31975                 return;
31976             }
31977             
31978             reader.readAsArrayBuffer(this.file);
31979             
31980         }
31981         
31982     },
31983     
31984     parseExifData : function(dataView, offset, length)
31985     {
31986         var tiffOffset = offset + 10,
31987             littleEndian,
31988             dirOffset;
31989     
31990         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31991             // No Exif data, might be XMP data instead
31992             return;
31993         }
31994         
31995         // Check for the ASCII code for "Exif" (0x45786966):
31996         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31997             // No Exif data, might be XMP data instead
31998             return;
31999         }
32000         if (tiffOffset + 8 > dataView.byteLength) {
32001             Roo.log('Invalid Exif data: Invalid segment size.');
32002             return;
32003         }
32004         // Check for the two null bytes:
32005         if (dataView.getUint16(offset + 8) !== 0x0000) {
32006             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32007             return;
32008         }
32009         // Check the byte alignment:
32010         switch (dataView.getUint16(tiffOffset)) {
32011         case 0x4949:
32012             littleEndian = true;
32013             break;
32014         case 0x4D4D:
32015             littleEndian = false;
32016             break;
32017         default:
32018             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32019             return;
32020         }
32021         // Check for the TIFF tag marker (0x002A):
32022         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32023             Roo.log('Invalid Exif data: Missing TIFF marker.');
32024             return;
32025         }
32026         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32027         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32028         
32029         this.parseExifTags(
32030             dataView,
32031             tiffOffset,
32032             tiffOffset + dirOffset,
32033             littleEndian
32034         );
32035     },
32036     
32037     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32038     {
32039         var tagsNumber,
32040             dirEndOffset,
32041             i;
32042         if (dirOffset + 6 > dataView.byteLength) {
32043             Roo.log('Invalid Exif data: Invalid directory offset.');
32044             return;
32045         }
32046         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32047         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32048         if (dirEndOffset + 4 > dataView.byteLength) {
32049             Roo.log('Invalid Exif data: Invalid directory size.');
32050             return;
32051         }
32052         for (i = 0; i < tagsNumber; i += 1) {
32053             this.parseExifTag(
32054                 dataView,
32055                 tiffOffset,
32056                 dirOffset + 2 + 12 * i, // tag offset
32057                 littleEndian
32058             );
32059         }
32060         // Return the offset to the next directory:
32061         return dataView.getUint32(dirEndOffset, littleEndian);
32062     },
32063     
32064     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32065     {
32066         var tag = dataView.getUint16(offset, littleEndian);
32067         
32068         this.exif[tag] = this.getExifValue(
32069             dataView,
32070             tiffOffset,
32071             offset,
32072             dataView.getUint16(offset + 2, littleEndian), // tag type
32073             dataView.getUint32(offset + 4, littleEndian), // tag length
32074             littleEndian
32075         );
32076     },
32077     
32078     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32079     {
32080         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32081             tagSize,
32082             dataOffset,
32083             values,
32084             i,
32085             str,
32086             c;
32087     
32088         if (!tagType) {
32089             Roo.log('Invalid Exif data: Invalid tag type.');
32090             return;
32091         }
32092         
32093         tagSize = tagType.size * length;
32094         // Determine if the value is contained in the dataOffset bytes,
32095         // or if the value at the dataOffset is a pointer to the actual data:
32096         dataOffset = tagSize > 4 ?
32097                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32098         if (dataOffset + tagSize > dataView.byteLength) {
32099             Roo.log('Invalid Exif data: Invalid data offset.');
32100             return;
32101         }
32102         if (length === 1) {
32103             return tagType.getValue(dataView, dataOffset, littleEndian);
32104         }
32105         values = [];
32106         for (i = 0; i < length; i += 1) {
32107             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32108         }
32109         
32110         if (tagType.ascii) {
32111             str = '';
32112             // Concatenate the chars:
32113             for (i = 0; i < values.length; i += 1) {
32114                 c = values[i];
32115                 // Ignore the terminating NULL byte(s):
32116                 if (c === '\u0000') {
32117                     break;
32118                 }
32119                 str += c;
32120             }
32121             return str;
32122         }
32123         return values;
32124     }
32125     
32126 });
32127
32128 Roo.apply(Roo.bootstrap.UploadCropbox, {
32129     tags : {
32130         'Orientation': 0x0112
32131     },
32132     
32133     Orientation: {
32134             1: 0, //'top-left',
32135 //            2: 'top-right',
32136             3: 180, //'bottom-right',
32137 //            4: 'bottom-left',
32138 //            5: 'left-top',
32139             6: 90, //'right-top',
32140 //            7: 'right-bottom',
32141             8: 270 //'left-bottom'
32142     },
32143     
32144     exifTagTypes : {
32145         // byte, 8-bit unsigned int:
32146         1: {
32147             getValue: function (dataView, dataOffset) {
32148                 return dataView.getUint8(dataOffset);
32149             },
32150             size: 1
32151         },
32152         // ascii, 8-bit byte:
32153         2: {
32154             getValue: function (dataView, dataOffset) {
32155                 return String.fromCharCode(dataView.getUint8(dataOffset));
32156             },
32157             size: 1,
32158             ascii: true
32159         },
32160         // short, 16 bit int:
32161         3: {
32162             getValue: function (dataView, dataOffset, littleEndian) {
32163                 return dataView.getUint16(dataOffset, littleEndian);
32164             },
32165             size: 2
32166         },
32167         // long, 32 bit int:
32168         4: {
32169             getValue: function (dataView, dataOffset, littleEndian) {
32170                 return dataView.getUint32(dataOffset, littleEndian);
32171             },
32172             size: 4
32173         },
32174         // rational = two long values, first is numerator, second is denominator:
32175         5: {
32176             getValue: function (dataView, dataOffset, littleEndian) {
32177                 return dataView.getUint32(dataOffset, littleEndian) /
32178                     dataView.getUint32(dataOffset + 4, littleEndian);
32179             },
32180             size: 8
32181         },
32182         // slong, 32 bit signed int:
32183         9: {
32184             getValue: function (dataView, dataOffset, littleEndian) {
32185                 return dataView.getInt32(dataOffset, littleEndian);
32186             },
32187             size: 4
32188         },
32189         // srational, two slongs, first is numerator, second is denominator:
32190         10: {
32191             getValue: function (dataView, dataOffset, littleEndian) {
32192                 return dataView.getInt32(dataOffset, littleEndian) /
32193                     dataView.getInt32(dataOffset + 4, littleEndian);
32194             },
32195             size: 8
32196         }
32197     },
32198     
32199     footer : {
32200         STANDARD : [
32201             {
32202                 tag : 'div',
32203                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32204                 action : 'rotate-left',
32205                 cn : [
32206                     {
32207                         tag : 'button',
32208                         cls : 'btn btn-default',
32209                         html : '<i class="fa fa-undo"></i>'
32210                     }
32211                 ]
32212             },
32213             {
32214                 tag : 'div',
32215                 cls : 'btn-group roo-upload-cropbox-picture',
32216                 action : 'picture',
32217                 cn : [
32218                     {
32219                         tag : 'button',
32220                         cls : 'btn btn-default',
32221                         html : '<i class="fa fa-picture-o"></i>'
32222                     }
32223                 ]
32224             },
32225             {
32226                 tag : 'div',
32227                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32228                 action : 'rotate-right',
32229                 cn : [
32230                     {
32231                         tag : 'button',
32232                         cls : 'btn btn-default',
32233                         html : '<i class="fa fa-repeat"></i>'
32234                     }
32235                 ]
32236             }
32237         ],
32238         DOCUMENT : [
32239             {
32240                 tag : 'div',
32241                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32242                 action : 'rotate-left',
32243                 cn : [
32244                     {
32245                         tag : 'button',
32246                         cls : 'btn btn-default',
32247                         html : '<i class="fa fa-undo"></i>'
32248                     }
32249                 ]
32250             },
32251             {
32252                 tag : 'div',
32253                 cls : 'btn-group roo-upload-cropbox-download',
32254                 action : 'download',
32255                 cn : [
32256                     {
32257                         tag : 'button',
32258                         cls : 'btn btn-default',
32259                         html : '<i class="fa fa-download"></i>'
32260                     }
32261                 ]
32262             },
32263             {
32264                 tag : 'div',
32265                 cls : 'btn-group roo-upload-cropbox-crop',
32266                 action : 'crop',
32267                 cn : [
32268                     {
32269                         tag : 'button',
32270                         cls : 'btn btn-default',
32271                         html : '<i class="fa fa-crop"></i>'
32272                     }
32273                 ]
32274             },
32275             {
32276                 tag : 'div',
32277                 cls : 'btn-group roo-upload-cropbox-trash',
32278                 action : 'trash',
32279                 cn : [
32280                     {
32281                         tag : 'button',
32282                         cls : 'btn btn-default',
32283                         html : '<i class="fa fa-trash"></i>'
32284                     }
32285                 ]
32286             },
32287             {
32288                 tag : 'div',
32289                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32290                 action : 'rotate-right',
32291                 cn : [
32292                     {
32293                         tag : 'button',
32294                         cls : 'btn btn-default',
32295                         html : '<i class="fa fa-repeat"></i>'
32296                     }
32297                 ]
32298             }
32299         ],
32300         ROTATOR : [
32301             {
32302                 tag : 'div',
32303                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32304                 action : 'rotate-left',
32305                 cn : [
32306                     {
32307                         tag : 'button',
32308                         cls : 'btn btn-default',
32309                         html : '<i class="fa fa-undo"></i>'
32310                     }
32311                 ]
32312             },
32313             {
32314                 tag : 'div',
32315                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32316                 action : 'rotate-right',
32317                 cn : [
32318                     {
32319                         tag : 'button',
32320                         cls : 'btn btn-default',
32321                         html : '<i class="fa fa-repeat"></i>'
32322                     }
32323                 ]
32324             }
32325         ]
32326     }
32327 });
32328
32329 /*
32330 * Licence: LGPL
32331 */
32332
32333 /**
32334  * @class Roo.bootstrap.DocumentManager
32335  * @extends Roo.bootstrap.Component
32336  * Bootstrap DocumentManager class
32337  * @cfg {String} paramName default 'imageUpload'
32338  * @cfg {String} toolTipName default 'filename'
32339  * @cfg {String} method default POST
32340  * @cfg {String} url action url
32341  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32342  * @cfg {Boolean} multiple multiple upload default true
32343  * @cfg {Number} thumbSize default 300
32344  * @cfg {String} fieldLabel
32345  * @cfg {Number} labelWidth default 4
32346  * @cfg {String} labelAlign (left|top) default left
32347  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32348 * @cfg {Number} labellg set the width of label (1-12)
32349  * @cfg {Number} labelmd set the width of label (1-12)
32350  * @cfg {Number} labelsm set the width of label (1-12)
32351  * @cfg {Number} labelxs set the width of label (1-12)
32352  * 
32353  * @constructor
32354  * Create a new DocumentManager
32355  * @param {Object} config The config object
32356  */
32357
32358 Roo.bootstrap.DocumentManager = function(config){
32359     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32360     
32361     this.files = [];
32362     this.delegates = [];
32363     
32364     this.addEvents({
32365         /**
32366          * @event initial
32367          * Fire when initial the DocumentManager
32368          * @param {Roo.bootstrap.DocumentManager} this
32369          */
32370         "initial" : true,
32371         /**
32372          * @event inspect
32373          * inspect selected file
32374          * @param {Roo.bootstrap.DocumentManager} this
32375          * @param {File} file
32376          */
32377         "inspect" : true,
32378         /**
32379          * @event exception
32380          * Fire when xhr load exception
32381          * @param {Roo.bootstrap.DocumentManager} this
32382          * @param {XMLHttpRequest} xhr
32383          */
32384         "exception" : true,
32385         /**
32386          * @event afterupload
32387          * Fire when xhr load exception
32388          * @param {Roo.bootstrap.DocumentManager} this
32389          * @param {XMLHttpRequest} xhr
32390          */
32391         "afterupload" : true,
32392         /**
32393          * @event prepare
32394          * prepare the form data
32395          * @param {Roo.bootstrap.DocumentManager} this
32396          * @param {Object} formData
32397          */
32398         "prepare" : true,
32399         /**
32400          * @event remove
32401          * Fire when remove the file
32402          * @param {Roo.bootstrap.DocumentManager} this
32403          * @param {Object} file
32404          */
32405         "remove" : true,
32406         /**
32407          * @event refresh
32408          * Fire after refresh the file
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          */
32411         "refresh" : true,
32412         /**
32413          * @event click
32414          * Fire after click the image
32415          * @param {Roo.bootstrap.DocumentManager} this
32416          * @param {Object} file
32417          */
32418         "click" : true,
32419         /**
32420          * @event edit
32421          * Fire when upload a image and editable set to true
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {Object} file
32424          */
32425         "edit" : true,
32426         /**
32427          * @event beforeselectfile
32428          * Fire before select file
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          */
32431         "beforeselectfile" : true,
32432         /**
32433          * @event process
32434          * Fire before process file
32435          * @param {Roo.bootstrap.DocumentManager} this
32436          * @param {Object} file
32437          */
32438         "process" : true,
32439         /**
32440          * @event previewrendered
32441          * Fire when preview rendered
32442          * @param {Roo.bootstrap.DocumentManager} this
32443          * @param {Object} file
32444          */
32445         "previewrendered" : true,
32446         /**
32447          */
32448         "previewResize" : true
32449         
32450     });
32451 };
32452
32453 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32454     
32455     boxes : 0,
32456     inputName : '',
32457     thumbSize : 300,
32458     multiple : true,
32459     files : false,
32460     method : 'POST',
32461     url : '',
32462     paramName : 'imageUpload',
32463     toolTipName : 'filename',
32464     fieldLabel : '',
32465     labelWidth : 4,
32466     labelAlign : 'left',
32467     editable : true,
32468     delegates : false,
32469     xhr : false, 
32470     
32471     labellg : 0,
32472     labelmd : 0,
32473     labelsm : 0,
32474     labelxs : 0,
32475     
32476     getAutoCreate : function()
32477     {   
32478         var managerWidget = {
32479             tag : 'div',
32480             cls : 'roo-document-manager',
32481             cn : [
32482                 {
32483                     tag : 'input',
32484                     cls : 'roo-document-manager-selector',
32485                     type : 'file'
32486                 },
32487                 {
32488                     tag : 'div',
32489                     cls : 'roo-document-manager-uploader',
32490                     cn : [
32491                         {
32492                             tag : 'div',
32493                             cls : 'roo-document-manager-upload-btn',
32494                             html : '<i class="fa fa-plus"></i>'
32495                         }
32496                     ]
32497                     
32498                 }
32499             ]
32500         };
32501         
32502         var content = [
32503             {
32504                 tag : 'div',
32505                 cls : 'column col-md-12',
32506                 cn : managerWidget
32507             }
32508         ];
32509         
32510         if(this.fieldLabel.length){
32511             
32512             content = [
32513                 {
32514                     tag : 'div',
32515                     cls : 'column col-md-12',
32516                     html : this.fieldLabel
32517                 },
32518                 {
32519                     tag : 'div',
32520                     cls : 'column col-md-12',
32521                     cn : managerWidget
32522                 }
32523             ];
32524
32525             if(this.labelAlign == 'left'){
32526                 content = [
32527                     {
32528                         tag : 'div',
32529                         cls : 'column',
32530                         html : this.fieldLabel
32531                     },
32532                     {
32533                         tag : 'div',
32534                         cls : 'column',
32535                         cn : managerWidget
32536                     }
32537                 ];
32538                 
32539                 if(this.labelWidth > 12){
32540                     content[0].style = "width: " + this.labelWidth + 'px';
32541                 }
32542
32543                 if(this.labelWidth < 13 && this.labelmd == 0){
32544                     this.labelmd = this.labelWidth;
32545                 }
32546
32547                 if(this.labellg > 0){
32548                     content[0].cls += ' col-lg-' + this.labellg;
32549                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32550                 }
32551
32552                 if(this.labelmd > 0){
32553                     content[0].cls += ' col-md-' + this.labelmd;
32554                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32555                 }
32556
32557                 if(this.labelsm > 0){
32558                     content[0].cls += ' col-sm-' + this.labelsm;
32559                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32560                 }
32561
32562                 if(this.labelxs > 0){
32563                     content[0].cls += ' col-xs-' + this.labelxs;
32564                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32565                 }
32566                 
32567             }
32568         }
32569         
32570         var cfg = {
32571             tag : 'div',
32572             cls : 'row clearfix',
32573             cn : content
32574         };
32575         
32576         return cfg;
32577         
32578     },
32579     
32580     initEvents : function()
32581     {
32582         this.managerEl = this.el.select('.roo-document-manager', true).first();
32583         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32584         
32585         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32586         this.selectorEl.hide();
32587         
32588         if(this.multiple){
32589             this.selectorEl.attr('multiple', 'multiple');
32590         }
32591         
32592         this.selectorEl.on('change', this.onFileSelected, this);
32593         
32594         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32595         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32596         
32597         this.uploader.on('click', this.onUploaderClick, this);
32598         
32599         this.renderProgressDialog();
32600         
32601         var _this = this;
32602         
32603         window.addEventListener("resize", function() { _this.refresh(); } );
32604         
32605         this.fireEvent('initial', this);
32606     },
32607     
32608     renderProgressDialog : function()
32609     {
32610         var _this = this;
32611         
32612         this.progressDialog = new Roo.bootstrap.Modal({
32613             cls : 'roo-document-manager-progress-dialog',
32614             allow_close : false,
32615             animate : false,
32616             title : '',
32617             buttons : [
32618                 {
32619                     name  :'cancel',
32620                     weight : 'danger',
32621                     html : 'Cancel'
32622                 }
32623             ], 
32624             listeners : { 
32625                 btnclick : function() {
32626                     _this.uploadCancel();
32627                     this.hide();
32628                 }
32629             }
32630         });
32631          
32632         this.progressDialog.render(Roo.get(document.body));
32633          
32634         this.progress = new Roo.bootstrap.Progress({
32635             cls : 'roo-document-manager-progress',
32636             active : true,
32637             striped : true
32638         });
32639         
32640         this.progress.render(this.progressDialog.getChildContainer());
32641         
32642         this.progressBar = new Roo.bootstrap.ProgressBar({
32643             cls : 'roo-document-manager-progress-bar',
32644             aria_valuenow : 0,
32645             aria_valuemin : 0,
32646             aria_valuemax : 12,
32647             panel : 'success'
32648         });
32649         
32650         this.progressBar.render(this.progress.getChildContainer());
32651     },
32652     
32653     onUploaderClick : function(e)
32654     {
32655         e.preventDefault();
32656      
32657         if(this.fireEvent('beforeselectfile', this) != false){
32658             this.selectorEl.dom.click();
32659         }
32660         
32661     },
32662     
32663     onFileSelected : function(e)
32664     {
32665         e.preventDefault();
32666         
32667         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32668             return;
32669         }
32670         
32671         Roo.each(this.selectorEl.dom.files, function(file){
32672             if(this.fireEvent('inspect', this, file) != false){
32673                 this.files.push(file);
32674             }
32675         }, this);
32676         
32677         this.queue();
32678         
32679     },
32680     
32681     queue : function()
32682     {
32683         this.selectorEl.dom.value = '';
32684         
32685         if(!this.files || !this.files.length){
32686             return;
32687         }
32688         
32689         if(this.boxes > 0 && this.files.length > this.boxes){
32690             this.files = this.files.slice(0, this.boxes);
32691         }
32692         
32693         this.uploader.show();
32694         
32695         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32696             this.uploader.hide();
32697         }
32698         
32699         var _this = this;
32700         
32701         var files = [];
32702         
32703         var docs = [];
32704         
32705         Roo.each(this.files, function(file){
32706             
32707             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32708                 var f = this.renderPreview(file);
32709                 files.push(f);
32710                 return;
32711             }
32712             
32713             if(file.type.indexOf('image') != -1){
32714                 this.delegates.push(
32715                     (function(){
32716                         _this.process(file);
32717                     }).createDelegate(this)
32718                 );
32719         
32720                 return;
32721             }
32722             
32723             docs.push(
32724                 (function(){
32725                     _this.process(file);
32726                 }).createDelegate(this)
32727             );
32728             
32729         }, this);
32730         
32731         this.files = files;
32732         
32733         this.delegates = this.delegates.concat(docs);
32734         
32735         if(!this.delegates.length){
32736             this.refresh();
32737             return;
32738         }
32739         
32740         this.progressBar.aria_valuemax = this.delegates.length;
32741         
32742         this.arrange();
32743         
32744         return;
32745     },
32746     
32747     arrange : function()
32748     {
32749         if(!this.delegates.length){
32750             this.progressDialog.hide();
32751             this.refresh();
32752             return;
32753         }
32754         
32755         var delegate = this.delegates.shift();
32756         
32757         this.progressDialog.show();
32758         
32759         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32760         
32761         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32762         
32763         delegate();
32764     },
32765     
32766     refresh : function()
32767     {
32768         this.uploader.show();
32769         
32770         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32771             this.uploader.hide();
32772         }
32773         
32774         Roo.isTouch ? this.closable(false) : this.closable(true);
32775         
32776         this.fireEvent('refresh', this);
32777     },
32778     
32779     onRemove : function(e, el, o)
32780     {
32781         e.preventDefault();
32782         
32783         this.fireEvent('remove', this, o);
32784         
32785     },
32786     
32787     remove : function(o)
32788     {
32789         var files = [];
32790         
32791         Roo.each(this.files, function(file){
32792             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32793                 files.push(file);
32794                 return;
32795             }
32796
32797             o.target.remove();
32798
32799         }, this);
32800         
32801         this.files = files;
32802         
32803         this.refresh();
32804     },
32805     
32806     clear : function()
32807     {
32808         Roo.each(this.files, function(file){
32809             if(!file.target){
32810                 return;
32811             }
32812             
32813             file.target.remove();
32814
32815         }, this);
32816         
32817         this.files = [];
32818         
32819         this.refresh();
32820     },
32821     
32822     onClick : function(e, el, o)
32823     {
32824         e.preventDefault();
32825         
32826         this.fireEvent('click', this, o);
32827         
32828     },
32829     
32830     closable : function(closable)
32831     {
32832         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32833             
32834             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32835             
32836             if(closable){
32837                 el.show();
32838                 return;
32839             }
32840             
32841             el.hide();
32842             
32843         }, this);
32844     },
32845     
32846     xhrOnLoad : function(xhr)
32847     {
32848         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32849             el.remove();
32850         }, this);
32851         
32852         if (xhr.readyState !== 4) {
32853             this.arrange();
32854             this.fireEvent('exception', this, xhr);
32855             return;
32856         }
32857
32858         var response = Roo.decode(xhr.responseText);
32859         
32860         if(!response.success){
32861             this.arrange();
32862             this.fireEvent('exception', this, xhr);
32863             return;
32864         }
32865         
32866         var file = this.renderPreview(response.data);
32867         
32868         this.files.push(file);
32869         
32870         this.arrange();
32871         
32872         this.fireEvent('afterupload', this, xhr);
32873         
32874     },
32875     
32876     xhrOnError : function(xhr)
32877     {
32878         Roo.log('xhr on error');
32879         
32880         var response = Roo.decode(xhr.responseText);
32881           
32882         Roo.log(response);
32883         
32884         this.arrange();
32885     },
32886     
32887     process : function(file)
32888     {
32889         if(this.fireEvent('process', this, file) !== false){
32890             if(this.editable && file.type.indexOf('image') != -1){
32891                 this.fireEvent('edit', this, file);
32892                 return;
32893             }
32894
32895             this.uploadStart(file, false);
32896
32897             return;
32898         }
32899         
32900     },
32901     
32902     uploadStart : function(file, crop)
32903     {
32904         this.xhr = new XMLHttpRequest();
32905         
32906         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32907             this.arrange();
32908             return;
32909         }
32910         
32911         file.xhr = this.xhr;
32912             
32913         this.managerEl.createChild({
32914             tag : 'div',
32915             cls : 'roo-document-manager-loading',
32916             cn : [
32917                 {
32918                     tag : 'div',
32919                     tooltip : file.name,
32920                     cls : 'roo-document-manager-thumb',
32921                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32922                 }
32923             ]
32924
32925         });
32926
32927         this.xhr.open(this.method, this.url, true);
32928         
32929         var headers = {
32930             "Accept": "application/json",
32931             "Cache-Control": "no-cache",
32932             "X-Requested-With": "XMLHttpRequest"
32933         };
32934         
32935         for (var headerName in headers) {
32936             var headerValue = headers[headerName];
32937             if (headerValue) {
32938                 this.xhr.setRequestHeader(headerName, headerValue);
32939             }
32940         }
32941         
32942         var _this = this;
32943         
32944         this.xhr.onload = function()
32945         {
32946             _this.xhrOnLoad(_this.xhr);
32947         }
32948         
32949         this.xhr.onerror = function()
32950         {
32951             _this.xhrOnError(_this.xhr);
32952         }
32953         
32954         var formData = new FormData();
32955
32956         formData.append('returnHTML', 'NO');
32957         
32958         if(crop){
32959             formData.append('crop', crop);
32960         }
32961         
32962         formData.append(this.paramName, file, file.name);
32963         
32964         var options = {
32965             file : file, 
32966             manually : false
32967         };
32968         
32969         if(this.fireEvent('prepare', this, formData, options) != false){
32970             
32971             if(options.manually){
32972                 return;
32973             }
32974             
32975             this.xhr.send(formData);
32976             return;
32977         };
32978         
32979         this.uploadCancel();
32980     },
32981     
32982     uploadCancel : function()
32983     {
32984         if (this.xhr) {
32985             this.xhr.abort();
32986         }
32987         
32988         this.delegates = [];
32989         
32990         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32991             el.remove();
32992         }, this);
32993         
32994         this.arrange();
32995     },
32996     
32997     renderPreview : function(file)
32998     {
32999         if(typeof(file.target) != 'undefined' && file.target){
33000             return file;
33001         }
33002         
33003         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33004         
33005         var previewEl = this.managerEl.createChild({
33006             tag : 'div',
33007             cls : 'roo-document-manager-preview',
33008             cn : [
33009                 {
33010                     tag : 'div',
33011                     tooltip : file[this.toolTipName],
33012                     cls : 'roo-document-manager-thumb',
33013                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33014                 },
33015                 {
33016                     tag : 'button',
33017                     cls : 'close',
33018                     html : '<i class="fa fa-times-circle"></i>'
33019                 }
33020             ]
33021         });
33022
33023         var close = previewEl.select('button.close', true).first();
33024
33025         close.on('click', this.onRemove, this, file);
33026
33027         file.target = previewEl;
33028
33029         var image = previewEl.select('img', true).first();
33030         
33031         var _this = this;
33032         
33033         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33034         
33035         image.on('click', this.onClick, this, file);
33036         
33037         this.fireEvent('previewrendered', this, file);
33038         
33039         return file;
33040         
33041     },
33042     
33043     onPreviewLoad : function(file, image)
33044     {
33045         if(typeof(file.target) == 'undefined' || !file.target){
33046             return;
33047         }
33048         
33049         var width = image.dom.naturalWidth || image.dom.width;
33050         var height = image.dom.naturalHeight || image.dom.height;
33051         
33052         if(!this.previewResize) {
33053             return;
33054         }
33055         
33056         if(width > height){
33057             file.target.addClass('wide');
33058             return;
33059         }
33060         
33061         file.target.addClass('tall');
33062         return;
33063         
33064     },
33065     
33066     uploadFromSource : function(file, crop)
33067     {
33068         this.xhr = new XMLHttpRequest();
33069         
33070         this.managerEl.createChild({
33071             tag : 'div',
33072             cls : 'roo-document-manager-loading',
33073             cn : [
33074                 {
33075                     tag : 'div',
33076                     tooltip : file.name,
33077                     cls : 'roo-document-manager-thumb',
33078                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33079                 }
33080             ]
33081
33082         });
33083
33084         this.xhr.open(this.method, this.url, true);
33085         
33086         var headers = {
33087             "Accept": "application/json",
33088             "Cache-Control": "no-cache",
33089             "X-Requested-With": "XMLHttpRequest"
33090         };
33091         
33092         for (var headerName in headers) {
33093             var headerValue = headers[headerName];
33094             if (headerValue) {
33095                 this.xhr.setRequestHeader(headerName, headerValue);
33096             }
33097         }
33098         
33099         var _this = this;
33100         
33101         this.xhr.onload = function()
33102         {
33103             _this.xhrOnLoad(_this.xhr);
33104         }
33105         
33106         this.xhr.onerror = function()
33107         {
33108             _this.xhrOnError(_this.xhr);
33109         }
33110         
33111         var formData = new FormData();
33112
33113         formData.append('returnHTML', 'NO');
33114         
33115         formData.append('crop', crop);
33116         
33117         if(typeof(file.filename) != 'undefined'){
33118             formData.append('filename', file.filename);
33119         }
33120         
33121         if(typeof(file.mimetype) != 'undefined'){
33122             formData.append('mimetype', file.mimetype);
33123         }
33124         
33125         Roo.log(formData);
33126         
33127         if(this.fireEvent('prepare', this, formData) != false){
33128             this.xhr.send(formData);
33129         };
33130     }
33131 });
33132
33133 /*
33134 * Licence: LGPL
33135 */
33136
33137 /**
33138  * @class Roo.bootstrap.DocumentViewer
33139  * @extends Roo.bootstrap.Component
33140  * Bootstrap DocumentViewer class
33141  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33142  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33143  * 
33144  * @constructor
33145  * Create a new DocumentViewer
33146  * @param {Object} config The config object
33147  */
33148
33149 Roo.bootstrap.DocumentViewer = function(config){
33150     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33151     
33152     this.addEvents({
33153         /**
33154          * @event initial
33155          * Fire after initEvent
33156          * @param {Roo.bootstrap.DocumentViewer} this
33157          */
33158         "initial" : true,
33159         /**
33160          * @event click
33161          * Fire after click
33162          * @param {Roo.bootstrap.DocumentViewer} this
33163          */
33164         "click" : true,
33165         /**
33166          * @event download
33167          * Fire after download button
33168          * @param {Roo.bootstrap.DocumentViewer} this
33169          */
33170         "download" : true,
33171         /**
33172          * @event trash
33173          * Fire after trash button
33174          * @param {Roo.bootstrap.DocumentViewer} this
33175          */
33176         "trash" : true
33177         
33178     });
33179 };
33180
33181 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33182     
33183     showDownload : true,
33184     
33185     showTrash : true,
33186     
33187     getAutoCreate : function()
33188     {
33189         var cfg = {
33190             tag : 'div',
33191             cls : 'roo-document-viewer',
33192             cn : [
33193                 {
33194                     tag : 'div',
33195                     cls : 'roo-document-viewer-body',
33196                     cn : [
33197                         {
33198                             tag : 'div',
33199                             cls : 'roo-document-viewer-thumb',
33200                             cn : [
33201                                 {
33202                                     tag : 'img',
33203                                     cls : 'roo-document-viewer-image'
33204                                 }
33205                             ]
33206                         }
33207                     ]
33208                 },
33209                 {
33210                     tag : 'div',
33211                     cls : 'roo-document-viewer-footer',
33212                     cn : {
33213                         tag : 'div',
33214                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33215                         cn : [
33216                             {
33217                                 tag : 'div',
33218                                 cls : 'btn-group roo-document-viewer-download',
33219                                 cn : [
33220                                     {
33221                                         tag : 'button',
33222                                         cls : 'btn btn-default',
33223                                         html : '<i class="fa fa-download"></i>'
33224                                     }
33225                                 ]
33226                             },
33227                             {
33228                                 tag : 'div',
33229                                 cls : 'btn-group roo-document-viewer-trash',
33230                                 cn : [
33231                                     {
33232                                         tag : 'button',
33233                                         cls : 'btn btn-default',
33234                                         html : '<i class="fa fa-trash"></i>'
33235                                     }
33236                                 ]
33237                             }
33238                         ]
33239                     }
33240                 }
33241             ]
33242         };
33243         
33244         return cfg;
33245     },
33246     
33247     initEvents : function()
33248     {
33249         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33250         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33251         
33252         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33253         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33254         
33255         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33256         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33257         
33258         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33259         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33260         
33261         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33262         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33263         
33264         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33265         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33266         
33267         this.bodyEl.on('click', this.onClick, this);
33268         this.downloadBtn.on('click', this.onDownload, this);
33269         this.trashBtn.on('click', this.onTrash, this);
33270         
33271         this.downloadBtn.hide();
33272         this.trashBtn.hide();
33273         
33274         if(this.showDownload){
33275             this.downloadBtn.show();
33276         }
33277         
33278         if(this.showTrash){
33279             this.trashBtn.show();
33280         }
33281         
33282         if(!this.showDownload && !this.showTrash) {
33283             this.footerEl.hide();
33284         }
33285         
33286     },
33287     
33288     initial : function()
33289     {
33290         this.fireEvent('initial', this);
33291         
33292     },
33293     
33294     onClick : function(e)
33295     {
33296         e.preventDefault();
33297         
33298         this.fireEvent('click', this);
33299     },
33300     
33301     onDownload : function(e)
33302     {
33303         e.preventDefault();
33304         
33305         this.fireEvent('download', this);
33306     },
33307     
33308     onTrash : function(e)
33309     {
33310         e.preventDefault();
33311         
33312         this.fireEvent('trash', this);
33313     }
33314     
33315 });
33316 /*
33317  * - LGPL
33318  *
33319  * nav progress bar
33320  * 
33321  */
33322
33323 /**
33324  * @class Roo.bootstrap.NavProgressBar
33325  * @extends Roo.bootstrap.Component
33326  * Bootstrap NavProgressBar class
33327  * 
33328  * @constructor
33329  * Create a new nav progress bar
33330  * @param {Object} config The config object
33331  */
33332
33333 Roo.bootstrap.NavProgressBar = function(config){
33334     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33335
33336     this.bullets = this.bullets || [];
33337    
33338 //    Roo.bootstrap.NavProgressBar.register(this);
33339      this.addEvents({
33340         /**
33341              * @event changed
33342              * Fires when the active item changes
33343              * @param {Roo.bootstrap.NavProgressBar} this
33344              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33345              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33346          */
33347         'changed': true
33348      });
33349     
33350 };
33351
33352 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33353     
33354     bullets : [],
33355     barItems : [],
33356     
33357     getAutoCreate : function()
33358     {
33359         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33360         
33361         cfg = {
33362             tag : 'div',
33363             cls : 'roo-navigation-bar-group',
33364             cn : [
33365                 {
33366                     tag : 'div',
33367                     cls : 'roo-navigation-top-bar'
33368                 },
33369                 {
33370                     tag : 'div',
33371                     cls : 'roo-navigation-bullets-bar',
33372                     cn : [
33373                         {
33374                             tag : 'ul',
33375                             cls : 'roo-navigation-bar'
33376                         }
33377                     ]
33378                 },
33379                 
33380                 {
33381                     tag : 'div',
33382                     cls : 'roo-navigation-bottom-bar'
33383                 }
33384             ]
33385             
33386         };
33387         
33388         return cfg;
33389         
33390     },
33391     
33392     initEvents: function() 
33393     {
33394         
33395     },
33396     
33397     onRender : function(ct, position) 
33398     {
33399         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33400         
33401         if(this.bullets.length){
33402             Roo.each(this.bullets, function(b){
33403                this.addItem(b);
33404             }, this);
33405         }
33406         
33407         this.format();
33408         
33409     },
33410     
33411     addItem : function(cfg)
33412     {
33413         var item = new Roo.bootstrap.NavProgressItem(cfg);
33414         
33415         item.parentId = this.id;
33416         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33417         
33418         if(cfg.html){
33419             var top = new Roo.bootstrap.Element({
33420                 tag : 'div',
33421                 cls : 'roo-navigation-bar-text'
33422             });
33423             
33424             var bottom = new Roo.bootstrap.Element({
33425                 tag : 'div',
33426                 cls : 'roo-navigation-bar-text'
33427             });
33428             
33429             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33430             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33431             
33432             var topText = new Roo.bootstrap.Element({
33433                 tag : 'span',
33434                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33435             });
33436             
33437             var bottomText = new Roo.bootstrap.Element({
33438                 tag : 'span',
33439                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33440             });
33441             
33442             topText.onRender(top.el, null);
33443             bottomText.onRender(bottom.el, null);
33444             
33445             item.topEl = top;
33446             item.bottomEl = bottom;
33447         }
33448         
33449         this.barItems.push(item);
33450         
33451         return item;
33452     },
33453     
33454     getActive : function()
33455     {
33456         var active = false;
33457         
33458         Roo.each(this.barItems, function(v){
33459             
33460             if (!v.isActive()) {
33461                 return;
33462             }
33463             
33464             active = v;
33465             return false;
33466             
33467         });
33468         
33469         return active;
33470     },
33471     
33472     setActiveItem : function(item)
33473     {
33474         var prev = false;
33475         
33476         Roo.each(this.barItems, function(v){
33477             if (v.rid == item.rid) {
33478                 return ;
33479             }
33480             
33481             if (v.isActive()) {
33482                 v.setActive(false);
33483                 prev = v;
33484             }
33485         });
33486
33487         item.setActive(true);
33488         
33489         this.fireEvent('changed', this, item, prev);
33490     },
33491     
33492     getBarItem: function(rid)
33493     {
33494         var ret = false;
33495         
33496         Roo.each(this.barItems, function(e) {
33497             if (e.rid != rid) {
33498                 return;
33499             }
33500             
33501             ret =  e;
33502             return false;
33503         });
33504         
33505         return ret;
33506     },
33507     
33508     indexOfItem : function(item)
33509     {
33510         var index = false;
33511         
33512         Roo.each(this.barItems, function(v, i){
33513             
33514             if (v.rid != item.rid) {
33515                 return;
33516             }
33517             
33518             index = i;
33519             return false
33520         });
33521         
33522         return index;
33523     },
33524     
33525     setActiveNext : function()
33526     {
33527         var i = this.indexOfItem(this.getActive());
33528         
33529         if (i > this.barItems.length) {
33530             return;
33531         }
33532         
33533         this.setActiveItem(this.barItems[i+1]);
33534     },
33535     
33536     setActivePrev : function()
33537     {
33538         var i = this.indexOfItem(this.getActive());
33539         
33540         if (i  < 1) {
33541             return;
33542         }
33543         
33544         this.setActiveItem(this.barItems[i-1]);
33545     },
33546     
33547     format : function()
33548     {
33549         if(!this.barItems.length){
33550             return;
33551         }
33552      
33553         var width = 100 / this.barItems.length;
33554         
33555         Roo.each(this.barItems, function(i){
33556             i.el.setStyle('width', width + '%');
33557             i.topEl.el.setStyle('width', width + '%');
33558             i.bottomEl.el.setStyle('width', width + '%');
33559         }, this);
33560         
33561     }
33562     
33563 });
33564 /*
33565  * - LGPL
33566  *
33567  * Nav Progress Item
33568  * 
33569  */
33570
33571 /**
33572  * @class Roo.bootstrap.NavProgressItem
33573  * @extends Roo.bootstrap.Component
33574  * Bootstrap NavProgressItem class
33575  * @cfg {String} rid the reference id
33576  * @cfg {Boolean} active (true|false) Is item active default false
33577  * @cfg {Boolean} disabled (true|false) Is item active default false
33578  * @cfg {String} html
33579  * @cfg {String} position (top|bottom) text position default bottom
33580  * @cfg {String} icon show icon instead of number
33581  * 
33582  * @constructor
33583  * Create a new NavProgressItem
33584  * @param {Object} config The config object
33585  */
33586 Roo.bootstrap.NavProgressItem = function(config){
33587     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33588     this.addEvents({
33589         // raw events
33590         /**
33591          * @event click
33592          * The raw click event for the entire grid.
33593          * @param {Roo.bootstrap.NavProgressItem} this
33594          * @param {Roo.EventObject} e
33595          */
33596         "click" : true
33597     });
33598    
33599 };
33600
33601 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33602     
33603     rid : '',
33604     active : false,
33605     disabled : false,
33606     html : '',
33607     position : 'bottom',
33608     icon : false,
33609     
33610     getAutoCreate : function()
33611     {
33612         var iconCls = 'roo-navigation-bar-item-icon';
33613         
33614         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33615         
33616         var cfg = {
33617             tag: 'li',
33618             cls: 'roo-navigation-bar-item',
33619             cn : [
33620                 {
33621                     tag : 'i',
33622                     cls : iconCls
33623                 }
33624             ]
33625         };
33626         
33627         if(this.active){
33628             cfg.cls += ' active';
33629         }
33630         if(this.disabled){
33631             cfg.cls += ' disabled';
33632         }
33633         
33634         return cfg;
33635     },
33636     
33637     disable : function()
33638     {
33639         this.setDisabled(true);
33640     },
33641     
33642     enable : function()
33643     {
33644         this.setDisabled(false);
33645     },
33646     
33647     initEvents: function() 
33648     {
33649         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33650         
33651         this.iconEl.on('click', this.onClick, this);
33652     },
33653     
33654     onClick : function(e)
33655     {
33656         e.preventDefault();
33657         
33658         if(this.disabled){
33659             return;
33660         }
33661         
33662         if(this.fireEvent('click', this, e) === false){
33663             return;
33664         };
33665         
33666         this.parent().setActiveItem(this);
33667     },
33668     
33669     isActive: function () 
33670     {
33671         return this.active;
33672     },
33673     
33674     setActive : function(state)
33675     {
33676         if(this.active == state){
33677             return;
33678         }
33679         
33680         this.active = state;
33681         
33682         if (state) {
33683             this.el.addClass('active');
33684             return;
33685         }
33686         
33687         this.el.removeClass('active');
33688         
33689         return;
33690     },
33691     
33692     setDisabled : function(state)
33693     {
33694         if(this.disabled == state){
33695             return;
33696         }
33697         
33698         this.disabled = state;
33699         
33700         if (state) {
33701             this.el.addClass('disabled');
33702             return;
33703         }
33704         
33705         this.el.removeClass('disabled');
33706     },
33707     
33708     tooltipEl : function()
33709     {
33710         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33711     }
33712 });
33713  
33714
33715  /*
33716  * - LGPL
33717  *
33718  * FieldLabel
33719  * 
33720  */
33721
33722 /**
33723  * @class Roo.bootstrap.FieldLabel
33724  * @extends Roo.bootstrap.Component
33725  * Bootstrap FieldLabel class
33726  * @cfg {String} html contents of the element
33727  * @cfg {String} tag tag of the element default label
33728  * @cfg {String} cls class of the element
33729  * @cfg {String} target label target 
33730  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33731  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33732  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33733  * @cfg {String} iconTooltip default "This field is required"
33734  * @cfg {String} indicatorpos (left|right) default left
33735  * 
33736  * @constructor
33737  * Create a new FieldLabel
33738  * @param {Object} config The config object
33739  */
33740
33741 Roo.bootstrap.FieldLabel = function(config){
33742     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33743     
33744     this.addEvents({
33745             /**
33746              * @event invalid
33747              * Fires after the field has been marked as invalid.
33748              * @param {Roo.form.FieldLabel} this
33749              * @param {String} msg The validation message
33750              */
33751             invalid : true,
33752             /**
33753              * @event valid
33754              * Fires after the field has been validated with no errors.
33755              * @param {Roo.form.FieldLabel} this
33756              */
33757             valid : true
33758         });
33759 };
33760
33761 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33762     
33763     tag: 'label',
33764     cls: '',
33765     html: '',
33766     target: '',
33767     allowBlank : true,
33768     invalidClass : 'has-warning',
33769     validClass : 'has-success',
33770     iconTooltip : 'This field is required',
33771     indicatorpos : 'left',
33772     
33773     getAutoCreate : function(){
33774         
33775         var cls = "";
33776         if (!this.allowBlank) {
33777             cls  = "visible";
33778         }
33779         
33780         var cfg = {
33781             tag : this.tag,
33782             cls : 'roo-bootstrap-field-label ' + this.cls,
33783             for : this.target,
33784             cn : [
33785                 {
33786                     tag : 'i',
33787                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33788                     tooltip : this.iconTooltip
33789                 },
33790                 {
33791                     tag : 'span',
33792                     html : this.html
33793                 }
33794             ] 
33795         };
33796         
33797         if(this.indicatorpos == 'right'){
33798             var cfg = {
33799                 tag : this.tag,
33800                 cls : 'roo-bootstrap-field-label ' + this.cls,
33801                 for : this.target,
33802                 cn : [
33803                     {
33804                         tag : 'span',
33805                         html : this.html
33806                     },
33807                     {
33808                         tag : 'i',
33809                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33810                         tooltip : this.iconTooltip
33811                     }
33812                 ] 
33813             };
33814         }
33815         
33816         return cfg;
33817     },
33818     
33819     initEvents: function() 
33820     {
33821         Roo.bootstrap.Element.superclass.initEvents.call(this);
33822         
33823         this.indicator = this.indicatorEl();
33824         
33825         if(this.indicator){
33826             this.indicator.removeClass('visible');
33827             this.indicator.addClass('invisible');
33828         }
33829         
33830         Roo.bootstrap.FieldLabel.register(this);
33831     },
33832     
33833     indicatorEl : function()
33834     {
33835         var indicator = this.el.select('i.roo-required-indicator',true).first();
33836         
33837         if(!indicator){
33838             return false;
33839         }
33840         
33841         return indicator;
33842         
33843     },
33844     
33845     /**
33846      * Mark this field as valid
33847      */
33848     markValid : function()
33849     {
33850         if(this.indicator){
33851             this.indicator.removeClass('visible');
33852             this.indicator.addClass('invisible');
33853         }
33854         if (Roo.bootstrap.version == 3) {
33855             this.el.removeClass(this.invalidClass);
33856             this.el.addClass(this.validClass);
33857         } else {
33858             this.el.removeClass('is-invalid');
33859             this.el.addClass('is-valid');
33860         }
33861         
33862         
33863         this.fireEvent('valid', this);
33864     },
33865     
33866     /**
33867      * Mark this field as invalid
33868      * @param {String} msg The validation message
33869      */
33870     markInvalid : function(msg)
33871     {
33872         if(this.indicator){
33873             this.indicator.removeClass('invisible');
33874             this.indicator.addClass('visible');
33875         }
33876           if (Roo.bootstrap.version == 3) {
33877             this.el.removeClass(this.validClass);
33878             this.el.addClass(this.invalidClass);
33879         } else {
33880             this.el.removeClass('is-valid');
33881             this.el.addClass('is-invalid');
33882         }
33883         
33884         
33885         this.fireEvent('invalid', this, msg);
33886     }
33887     
33888    
33889 });
33890
33891 Roo.apply(Roo.bootstrap.FieldLabel, {
33892     
33893     groups: {},
33894     
33895      /**
33896     * register a FieldLabel Group
33897     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33898     */
33899     register : function(label)
33900     {
33901         if(this.groups.hasOwnProperty(label.target)){
33902             return;
33903         }
33904      
33905         this.groups[label.target] = label;
33906         
33907     },
33908     /**
33909     * fetch a FieldLabel Group based on the target
33910     * @param {string} target
33911     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33912     */
33913     get: function(target) {
33914         if (typeof(this.groups[target]) == 'undefined') {
33915             return false;
33916         }
33917         
33918         return this.groups[target] ;
33919     }
33920 });
33921
33922  
33923
33924  /*
33925  * - LGPL
33926  *
33927  * page DateSplitField.
33928  * 
33929  */
33930
33931
33932 /**
33933  * @class Roo.bootstrap.DateSplitField
33934  * @extends Roo.bootstrap.Component
33935  * Bootstrap DateSplitField class
33936  * @cfg {string} fieldLabel - the label associated
33937  * @cfg {Number} labelWidth set the width of label (0-12)
33938  * @cfg {String} labelAlign (top|left)
33939  * @cfg {Boolean} dayAllowBlank (true|false) default false
33940  * @cfg {Boolean} monthAllowBlank (true|false) default false
33941  * @cfg {Boolean} yearAllowBlank (true|false) default false
33942  * @cfg {string} dayPlaceholder 
33943  * @cfg {string} monthPlaceholder
33944  * @cfg {string} yearPlaceholder
33945  * @cfg {string} dayFormat default 'd'
33946  * @cfg {string} monthFormat default 'm'
33947  * @cfg {string} yearFormat default 'Y'
33948  * @cfg {Number} labellg set the width of label (1-12)
33949  * @cfg {Number} labelmd set the width of label (1-12)
33950  * @cfg {Number} labelsm set the width of label (1-12)
33951  * @cfg {Number} labelxs set the width of label (1-12)
33952
33953  *     
33954  * @constructor
33955  * Create a new DateSplitField
33956  * @param {Object} config The config object
33957  */
33958
33959 Roo.bootstrap.DateSplitField = function(config){
33960     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33961     
33962     this.addEvents({
33963         // raw events
33964          /**
33965          * @event years
33966          * getting the data of years
33967          * @param {Roo.bootstrap.DateSplitField} this
33968          * @param {Object} years
33969          */
33970         "years" : true,
33971         /**
33972          * @event days
33973          * getting the data of days
33974          * @param {Roo.bootstrap.DateSplitField} this
33975          * @param {Object} days
33976          */
33977         "days" : true,
33978         /**
33979          * @event invalid
33980          * Fires after the field has been marked as invalid.
33981          * @param {Roo.form.Field} this
33982          * @param {String} msg The validation message
33983          */
33984         invalid : true,
33985        /**
33986          * @event valid
33987          * Fires after the field has been validated with no errors.
33988          * @param {Roo.form.Field} this
33989          */
33990         valid : true
33991     });
33992 };
33993
33994 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33995     
33996     fieldLabel : '',
33997     labelAlign : 'top',
33998     labelWidth : 3,
33999     dayAllowBlank : false,
34000     monthAllowBlank : false,
34001     yearAllowBlank : false,
34002     dayPlaceholder : '',
34003     monthPlaceholder : '',
34004     yearPlaceholder : '',
34005     dayFormat : 'd',
34006     monthFormat : 'm',
34007     yearFormat : 'Y',
34008     isFormField : true,
34009     labellg : 0,
34010     labelmd : 0,
34011     labelsm : 0,
34012     labelxs : 0,
34013     
34014     getAutoCreate : function()
34015     {
34016         var cfg = {
34017             tag : 'div',
34018             cls : 'row roo-date-split-field-group',
34019             cn : [
34020                 {
34021                     tag : 'input',
34022                     type : 'hidden',
34023                     cls : 'form-hidden-field roo-date-split-field-group-value',
34024                     name : this.name
34025                 }
34026             ]
34027         };
34028         
34029         var labelCls = 'col-md-12';
34030         var contentCls = 'col-md-4';
34031         
34032         if(this.fieldLabel){
34033             
34034             var label = {
34035                 tag : 'div',
34036                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34037                 cn : [
34038                     {
34039                         tag : 'label',
34040                         html : this.fieldLabel
34041                     }
34042                 ]
34043             };
34044             
34045             if(this.labelAlign == 'left'){
34046             
34047                 if(this.labelWidth > 12){
34048                     label.style = "width: " + this.labelWidth + 'px';
34049                 }
34050
34051                 if(this.labelWidth < 13 && this.labelmd == 0){
34052                     this.labelmd = this.labelWidth;
34053                 }
34054
34055                 if(this.labellg > 0){
34056                     labelCls = ' col-lg-' + this.labellg;
34057                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34058                 }
34059
34060                 if(this.labelmd > 0){
34061                     labelCls = ' col-md-' + this.labelmd;
34062                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34063                 }
34064
34065                 if(this.labelsm > 0){
34066                     labelCls = ' col-sm-' + this.labelsm;
34067                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34068                 }
34069
34070                 if(this.labelxs > 0){
34071                     labelCls = ' col-xs-' + this.labelxs;
34072                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34073                 }
34074             }
34075             
34076             label.cls += ' ' + labelCls;
34077             
34078             cfg.cn.push(label);
34079         }
34080         
34081         Roo.each(['day', 'month', 'year'], function(t){
34082             cfg.cn.push({
34083                 tag : 'div',
34084                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34085             });
34086         }, this);
34087         
34088         return cfg;
34089     },
34090     
34091     inputEl: function ()
34092     {
34093         return this.el.select('.roo-date-split-field-group-value', true).first();
34094     },
34095     
34096     onRender : function(ct, position) 
34097     {
34098         var _this = this;
34099         
34100         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34101         
34102         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34103         
34104         this.dayField = new Roo.bootstrap.ComboBox({
34105             allowBlank : this.dayAllowBlank,
34106             alwaysQuery : true,
34107             displayField : 'value',
34108             editable : false,
34109             fieldLabel : '',
34110             forceSelection : true,
34111             mode : 'local',
34112             placeholder : this.dayPlaceholder,
34113             selectOnFocus : true,
34114             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34115             triggerAction : 'all',
34116             typeAhead : true,
34117             valueField : 'value',
34118             store : new Roo.data.SimpleStore({
34119                 data : (function() {    
34120                     var days = [];
34121                     _this.fireEvent('days', _this, days);
34122                     return days;
34123                 })(),
34124                 fields : [ 'value' ]
34125             }),
34126             listeners : {
34127                 select : function (_self, record, index)
34128                 {
34129                     _this.setValue(_this.getValue());
34130                 }
34131             }
34132         });
34133
34134         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34135         
34136         this.monthField = new Roo.bootstrap.MonthField({
34137             after : '<i class=\"fa fa-calendar\"></i>',
34138             allowBlank : this.monthAllowBlank,
34139             placeholder : this.monthPlaceholder,
34140             readOnly : true,
34141             listeners : {
34142                 render : function (_self)
34143                 {
34144                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34145                         e.preventDefault();
34146                         _self.focus();
34147                     });
34148                 },
34149                 select : function (_self, oldvalue, newvalue)
34150                 {
34151                     _this.setValue(_this.getValue());
34152                 }
34153             }
34154         });
34155         
34156         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34157         
34158         this.yearField = new Roo.bootstrap.ComboBox({
34159             allowBlank : this.yearAllowBlank,
34160             alwaysQuery : true,
34161             displayField : 'value',
34162             editable : false,
34163             fieldLabel : '',
34164             forceSelection : true,
34165             mode : 'local',
34166             placeholder : this.yearPlaceholder,
34167             selectOnFocus : true,
34168             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34169             triggerAction : 'all',
34170             typeAhead : true,
34171             valueField : 'value',
34172             store : new Roo.data.SimpleStore({
34173                 data : (function() {
34174                     var years = [];
34175                     _this.fireEvent('years', _this, years);
34176                     return years;
34177                 })(),
34178                 fields : [ 'value' ]
34179             }),
34180             listeners : {
34181                 select : function (_self, record, index)
34182                 {
34183                     _this.setValue(_this.getValue());
34184                 }
34185             }
34186         });
34187
34188         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34189     },
34190     
34191     setValue : function(v, format)
34192     {
34193         this.inputEl.dom.value = v;
34194         
34195         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34196         
34197         var d = Date.parseDate(v, f);
34198         
34199         if(!d){
34200             this.validate();
34201             return;
34202         }
34203         
34204         this.setDay(d.format(this.dayFormat));
34205         this.setMonth(d.format(this.monthFormat));
34206         this.setYear(d.format(this.yearFormat));
34207         
34208         this.validate();
34209         
34210         return;
34211     },
34212     
34213     setDay : function(v)
34214     {
34215         this.dayField.setValue(v);
34216         this.inputEl.dom.value = this.getValue();
34217         this.validate();
34218         return;
34219     },
34220     
34221     setMonth : function(v)
34222     {
34223         this.monthField.setValue(v, true);
34224         this.inputEl.dom.value = this.getValue();
34225         this.validate();
34226         return;
34227     },
34228     
34229     setYear : function(v)
34230     {
34231         this.yearField.setValue(v);
34232         this.inputEl.dom.value = this.getValue();
34233         this.validate();
34234         return;
34235     },
34236     
34237     getDay : function()
34238     {
34239         return this.dayField.getValue();
34240     },
34241     
34242     getMonth : function()
34243     {
34244         return this.monthField.getValue();
34245     },
34246     
34247     getYear : function()
34248     {
34249         return this.yearField.getValue();
34250     },
34251     
34252     getValue : function()
34253     {
34254         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34255         
34256         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34257         
34258         return date;
34259     },
34260     
34261     reset : function()
34262     {
34263         this.setDay('');
34264         this.setMonth('');
34265         this.setYear('');
34266         this.inputEl.dom.value = '';
34267         this.validate();
34268         return;
34269     },
34270     
34271     validate : function()
34272     {
34273         var d = this.dayField.validate();
34274         var m = this.monthField.validate();
34275         var y = this.yearField.validate();
34276         
34277         var valid = true;
34278         
34279         if(
34280                 (!this.dayAllowBlank && !d) ||
34281                 (!this.monthAllowBlank && !m) ||
34282                 (!this.yearAllowBlank && !y)
34283         ){
34284             valid = false;
34285         }
34286         
34287         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34288             return valid;
34289         }
34290         
34291         if(valid){
34292             this.markValid();
34293             return valid;
34294         }
34295         
34296         this.markInvalid();
34297         
34298         return valid;
34299     },
34300     
34301     markValid : function()
34302     {
34303         
34304         var label = this.el.select('label', true).first();
34305         var icon = this.el.select('i.fa-star', true).first();
34306
34307         if(label && icon){
34308             icon.remove();
34309         }
34310         
34311         this.fireEvent('valid', this);
34312     },
34313     
34314      /**
34315      * Mark this field as invalid
34316      * @param {String} msg The validation message
34317      */
34318     markInvalid : function(msg)
34319     {
34320         
34321         var label = this.el.select('label', true).first();
34322         var icon = this.el.select('i.fa-star', true).first();
34323
34324         if(label && !icon){
34325             this.el.select('.roo-date-split-field-label', true).createChild({
34326                 tag : 'i',
34327                 cls : 'text-danger fa fa-lg fa-star',
34328                 tooltip : 'This field is required',
34329                 style : 'margin-right:5px;'
34330             }, label, true);
34331         }
34332         
34333         this.fireEvent('invalid', this, msg);
34334     },
34335     
34336     clearInvalid : function()
34337     {
34338         var label = this.el.select('label', true).first();
34339         var icon = this.el.select('i.fa-star', true).first();
34340
34341         if(label && icon){
34342             icon.remove();
34343         }
34344         
34345         this.fireEvent('valid', this);
34346     },
34347     
34348     getName: function()
34349     {
34350         return this.name;
34351     }
34352     
34353 });
34354
34355  /**
34356  *
34357  * This is based on 
34358  * http://masonry.desandro.com
34359  *
34360  * The idea is to render all the bricks based on vertical width...
34361  *
34362  * The original code extends 'outlayer' - we might need to use that....
34363  * 
34364  */
34365
34366
34367 /**
34368  * @class Roo.bootstrap.LayoutMasonry
34369  * @extends Roo.bootstrap.Component
34370  * Bootstrap Layout Masonry class
34371  * 
34372  * @constructor
34373  * Create a new Element
34374  * @param {Object} config The config object
34375  */
34376
34377 Roo.bootstrap.LayoutMasonry = function(config){
34378     
34379     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34380     
34381     this.bricks = [];
34382     
34383     Roo.bootstrap.LayoutMasonry.register(this);
34384     
34385     this.addEvents({
34386         // raw events
34387         /**
34388          * @event layout
34389          * Fire after layout the items
34390          * @param {Roo.bootstrap.LayoutMasonry} this
34391          * @param {Roo.EventObject} e
34392          */
34393         "layout" : true
34394     });
34395     
34396 };
34397
34398 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34399     
34400     /**
34401      * @cfg {Boolean} isLayoutInstant = no animation?
34402      */   
34403     isLayoutInstant : false, // needed?
34404    
34405     /**
34406      * @cfg {Number} boxWidth  width of the columns
34407      */   
34408     boxWidth : 450,
34409     
34410       /**
34411      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34412      */   
34413     boxHeight : 0,
34414     
34415     /**
34416      * @cfg {Number} padWidth padding below box..
34417      */   
34418     padWidth : 10, 
34419     
34420     /**
34421      * @cfg {Number} gutter gutter width..
34422      */   
34423     gutter : 10,
34424     
34425      /**
34426      * @cfg {Number} maxCols maximum number of columns
34427      */   
34428     
34429     maxCols: 0,
34430     
34431     /**
34432      * @cfg {Boolean} isAutoInitial defalut true
34433      */   
34434     isAutoInitial : true, 
34435     
34436     containerWidth: 0,
34437     
34438     /**
34439      * @cfg {Boolean} isHorizontal defalut false
34440      */   
34441     isHorizontal : false, 
34442
34443     currentSize : null,
34444     
34445     tag: 'div',
34446     
34447     cls: '',
34448     
34449     bricks: null, //CompositeElement
34450     
34451     cols : 1,
34452     
34453     _isLayoutInited : false,
34454     
34455 //    isAlternative : false, // only use for vertical layout...
34456     
34457     /**
34458      * @cfg {Number} alternativePadWidth padding below box..
34459      */   
34460     alternativePadWidth : 50,
34461     
34462     selectedBrick : [],
34463     
34464     getAutoCreate : function(){
34465         
34466         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34467         
34468         var cfg = {
34469             tag: this.tag,
34470             cls: 'blog-masonary-wrapper ' + this.cls,
34471             cn : {
34472                 cls : 'mas-boxes masonary'
34473             }
34474         };
34475         
34476         return cfg;
34477     },
34478     
34479     getChildContainer: function( )
34480     {
34481         if (this.boxesEl) {
34482             return this.boxesEl;
34483         }
34484         
34485         this.boxesEl = this.el.select('.mas-boxes').first();
34486         
34487         return this.boxesEl;
34488     },
34489     
34490     
34491     initEvents : function()
34492     {
34493         var _this = this;
34494         
34495         if(this.isAutoInitial){
34496             Roo.log('hook children rendered');
34497             this.on('childrenrendered', function() {
34498                 Roo.log('children rendered');
34499                 _this.initial();
34500             } ,this);
34501         }
34502     },
34503     
34504     initial : function()
34505     {
34506         this.selectedBrick = [];
34507         
34508         this.currentSize = this.el.getBox(true);
34509         
34510         Roo.EventManager.onWindowResize(this.resize, this); 
34511
34512         if(!this.isAutoInitial){
34513             this.layout();
34514             return;
34515         }
34516         
34517         this.layout();
34518         
34519         return;
34520         //this.layout.defer(500,this);
34521         
34522     },
34523     
34524     resize : function()
34525     {
34526         var cs = this.el.getBox(true);
34527         
34528         if (
34529                 this.currentSize.width == cs.width && 
34530                 this.currentSize.x == cs.x && 
34531                 this.currentSize.height == cs.height && 
34532                 this.currentSize.y == cs.y 
34533         ) {
34534             Roo.log("no change in with or X or Y");
34535             return;
34536         }
34537         
34538         this.currentSize = cs;
34539         
34540         this.layout();
34541         
34542     },
34543     
34544     layout : function()
34545     {   
34546         this._resetLayout();
34547         
34548         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34549         
34550         this.layoutItems( isInstant );
34551       
34552         this._isLayoutInited = true;
34553         
34554         this.fireEvent('layout', this);
34555         
34556     },
34557     
34558     _resetLayout : function()
34559     {
34560         if(this.isHorizontal){
34561             this.horizontalMeasureColumns();
34562             return;
34563         }
34564         
34565         this.verticalMeasureColumns();
34566         
34567     },
34568     
34569     verticalMeasureColumns : function()
34570     {
34571         this.getContainerWidth();
34572         
34573 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34574 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34575 //            return;
34576 //        }
34577         
34578         var boxWidth = this.boxWidth + this.padWidth;
34579         
34580         if(this.containerWidth < this.boxWidth){
34581             boxWidth = this.containerWidth
34582         }
34583         
34584         var containerWidth = this.containerWidth;
34585         
34586         var cols = Math.floor(containerWidth / boxWidth);
34587         
34588         this.cols = Math.max( cols, 1 );
34589         
34590         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34591         
34592         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34593         
34594         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34595         
34596         this.colWidth = boxWidth + avail - this.padWidth;
34597         
34598         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34599         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34600     },
34601     
34602     horizontalMeasureColumns : function()
34603     {
34604         this.getContainerWidth();
34605         
34606         var boxWidth = this.boxWidth;
34607         
34608         if(this.containerWidth < boxWidth){
34609             boxWidth = this.containerWidth;
34610         }
34611         
34612         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34613         
34614         this.el.setHeight(boxWidth);
34615         
34616     },
34617     
34618     getContainerWidth : function()
34619     {
34620         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34621     },
34622     
34623     layoutItems : function( isInstant )
34624     {
34625         Roo.log(this.bricks);
34626         
34627         var items = Roo.apply([], this.bricks);
34628         
34629         if(this.isHorizontal){
34630             this._horizontalLayoutItems( items , isInstant );
34631             return;
34632         }
34633         
34634 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34635 //            this._verticalAlternativeLayoutItems( items , isInstant );
34636 //            return;
34637 //        }
34638         
34639         this._verticalLayoutItems( items , isInstant );
34640         
34641     },
34642     
34643     _verticalLayoutItems : function ( items , isInstant)
34644     {
34645         if ( !items || !items.length ) {
34646             return;
34647         }
34648         
34649         var standard = [
34650             ['xs', 'xs', 'xs', 'tall'],
34651             ['xs', 'xs', 'tall'],
34652             ['xs', 'xs', 'sm'],
34653             ['xs', 'xs', 'xs'],
34654             ['xs', 'tall'],
34655             ['xs', 'sm'],
34656             ['xs', 'xs'],
34657             ['xs'],
34658             
34659             ['sm', 'xs', 'xs'],
34660             ['sm', 'xs'],
34661             ['sm'],
34662             
34663             ['tall', 'xs', 'xs', 'xs'],
34664             ['tall', 'xs', 'xs'],
34665             ['tall', 'xs'],
34666             ['tall']
34667             
34668         ];
34669         
34670         var queue = [];
34671         
34672         var boxes = [];
34673         
34674         var box = [];
34675         
34676         Roo.each(items, function(item, k){
34677             
34678             switch (item.size) {
34679                 // these layouts take up a full box,
34680                 case 'md' :
34681                 case 'md-left' :
34682                 case 'md-right' :
34683                 case 'wide' :
34684                     
34685                     if(box.length){
34686                         boxes.push(box);
34687                         box = [];
34688                     }
34689                     
34690                     boxes.push([item]);
34691                     
34692                     break;
34693                     
34694                 case 'xs' :
34695                 case 'sm' :
34696                 case 'tall' :
34697                     
34698                     box.push(item);
34699                     
34700                     break;
34701                 default :
34702                     break;
34703                     
34704             }
34705             
34706         }, this);
34707         
34708         if(box.length){
34709             boxes.push(box);
34710             box = [];
34711         }
34712         
34713         var filterPattern = function(box, length)
34714         {
34715             if(!box.length){
34716                 return;
34717             }
34718             
34719             var match = false;
34720             
34721             var pattern = box.slice(0, length);
34722             
34723             var format = [];
34724             
34725             Roo.each(pattern, function(i){
34726                 format.push(i.size);
34727             }, this);
34728             
34729             Roo.each(standard, function(s){
34730                 
34731                 if(String(s) != String(format)){
34732                     return;
34733                 }
34734                 
34735                 match = true;
34736                 return false;
34737                 
34738             }, this);
34739             
34740             if(!match && length == 1){
34741                 return;
34742             }
34743             
34744             if(!match){
34745                 filterPattern(box, length - 1);
34746                 return;
34747             }
34748                 
34749             queue.push(pattern);
34750
34751             box = box.slice(length, box.length);
34752
34753             filterPattern(box, 4);
34754
34755             return;
34756             
34757         }
34758         
34759         Roo.each(boxes, function(box, k){
34760             
34761             if(!box.length){
34762                 return;
34763             }
34764             
34765             if(box.length == 1){
34766                 queue.push(box);
34767                 return;
34768             }
34769             
34770             filterPattern(box, 4);
34771             
34772         }, this);
34773         
34774         this._processVerticalLayoutQueue( queue, isInstant );
34775         
34776     },
34777     
34778 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34779 //    {
34780 //        if ( !items || !items.length ) {
34781 //            return;
34782 //        }
34783 //
34784 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34785 //        
34786 //    },
34787     
34788     _horizontalLayoutItems : function ( items , isInstant)
34789     {
34790         if ( !items || !items.length || items.length < 3) {
34791             return;
34792         }
34793         
34794         items.reverse();
34795         
34796         var eItems = items.slice(0, 3);
34797         
34798         items = items.slice(3, items.length);
34799         
34800         var standard = [
34801             ['xs', 'xs', 'xs', 'wide'],
34802             ['xs', 'xs', 'wide'],
34803             ['xs', 'xs', 'sm'],
34804             ['xs', 'xs', 'xs'],
34805             ['xs', 'wide'],
34806             ['xs', 'sm'],
34807             ['xs', 'xs'],
34808             ['xs'],
34809             
34810             ['sm', 'xs', 'xs'],
34811             ['sm', 'xs'],
34812             ['sm'],
34813             
34814             ['wide', 'xs', 'xs', 'xs'],
34815             ['wide', 'xs', 'xs'],
34816             ['wide', 'xs'],
34817             ['wide'],
34818             
34819             ['wide-thin']
34820         ];
34821         
34822         var queue = [];
34823         
34824         var boxes = [];
34825         
34826         var box = [];
34827         
34828         Roo.each(items, function(item, k){
34829             
34830             switch (item.size) {
34831                 case 'md' :
34832                 case 'md-left' :
34833                 case 'md-right' :
34834                 case 'tall' :
34835                     
34836                     if(box.length){
34837                         boxes.push(box);
34838                         box = [];
34839                     }
34840                     
34841                     boxes.push([item]);
34842                     
34843                     break;
34844                     
34845                 case 'xs' :
34846                 case 'sm' :
34847                 case 'wide' :
34848                 case 'wide-thin' :
34849                     
34850                     box.push(item);
34851                     
34852                     break;
34853                 default :
34854                     break;
34855                     
34856             }
34857             
34858         }, this);
34859         
34860         if(box.length){
34861             boxes.push(box);
34862             box = [];
34863         }
34864         
34865         var filterPattern = function(box, length)
34866         {
34867             if(!box.length){
34868                 return;
34869             }
34870             
34871             var match = false;
34872             
34873             var pattern = box.slice(0, length);
34874             
34875             var format = [];
34876             
34877             Roo.each(pattern, function(i){
34878                 format.push(i.size);
34879             }, this);
34880             
34881             Roo.each(standard, function(s){
34882                 
34883                 if(String(s) != String(format)){
34884                     return;
34885                 }
34886                 
34887                 match = true;
34888                 return false;
34889                 
34890             }, this);
34891             
34892             if(!match && length == 1){
34893                 return;
34894             }
34895             
34896             if(!match){
34897                 filterPattern(box, length - 1);
34898                 return;
34899             }
34900                 
34901             queue.push(pattern);
34902
34903             box = box.slice(length, box.length);
34904
34905             filterPattern(box, 4);
34906
34907             return;
34908             
34909         }
34910         
34911         Roo.each(boxes, function(box, k){
34912             
34913             if(!box.length){
34914                 return;
34915             }
34916             
34917             if(box.length == 1){
34918                 queue.push(box);
34919                 return;
34920             }
34921             
34922             filterPattern(box, 4);
34923             
34924         }, this);
34925         
34926         
34927         var prune = [];
34928         
34929         var pos = this.el.getBox(true);
34930         
34931         var minX = pos.x;
34932         
34933         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34934         
34935         var hit_end = false;
34936         
34937         Roo.each(queue, function(box){
34938             
34939             if(hit_end){
34940                 
34941                 Roo.each(box, function(b){
34942                 
34943                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34944                     b.el.hide();
34945
34946                 }, this);
34947
34948                 return;
34949             }
34950             
34951             var mx = 0;
34952             
34953             Roo.each(box, function(b){
34954                 
34955                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34956                 b.el.show();
34957
34958                 mx = Math.max(mx, b.x);
34959                 
34960             }, this);
34961             
34962             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34963             
34964             if(maxX < minX){
34965                 
34966                 Roo.each(box, function(b){
34967                 
34968                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34969                     b.el.hide();
34970                     
34971                 }, this);
34972                 
34973                 hit_end = true;
34974                 
34975                 return;
34976             }
34977             
34978             prune.push(box);
34979             
34980         }, this);
34981         
34982         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34983     },
34984     
34985     /** Sets position of item in DOM
34986     * @param {Element} item
34987     * @param {Number} x - horizontal position
34988     * @param {Number} y - vertical position
34989     * @param {Boolean} isInstant - disables transitions
34990     */
34991     _processVerticalLayoutQueue : function( queue, isInstant )
34992     {
34993         var pos = this.el.getBox(true);
34994         var x = pos.x;
34995         var y = pos.y;
34996         var maxY = [];
34997         
34998         for (var i = 0; i < this.cols; i++){
34999             maxY[i] = pos.y;
35000         }
35001         
35002         Roo.each(queue, function(box, k){
35003             
35004             var col = k % this.cols;
35005             
35006             Roo.each(box, function(b,kk){
35007                 
35008                 b.el.position('absolute');
35009                 
35010                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35011                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35012                 
35013                 if(b.size == 'md-left' || b.size == 'md-right'){
35014                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35015                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35016                 }
35017                 
35018                 b.el.setWidth(width);
35019                 b.el.setHeight(height);
35020                 // iframe?
35021                 b.el.select('iframe',true).setSize(width,height);
35022                 
35023             }, this);
35024             
35025             for (var i = 0; i < this.cols; i++){
35026                 
35027                 if(maxY[i] < maxY[col]){
35028                     col = i;
35029                     continue;
35030                 }
35031                 
35032                 col = Math.min(col, i);
35033                 
35034             }
35035             
35036             x = pos.x + col * (this.colWidth + this.padWidth);
35037             
35038             y = maxY[col];
35039             
35040             var positions = [];
35041             
35042             switch (box.length){
35043                 case 1 :
35044                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35045                     break;
35046                 case 2 :
35047                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35048                     break;
35049                 case 3 :
35050                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35051                     break;
35052                 case 4 :
35053                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35054                     break;
35055                 default :
35056                     break;
35057             }
35058             
35059             Roo.each(box, function(b,kk){
35060                 
35061                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35062                 
35063                 var sz = b.el.getSize();
35064                 
35065                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35066                 
35067             }, this);
35068             
35069         }, this);
35070         
35071         var mY = 0;
35072         
35073         for (var i = 0; i < this.cols; i++){
35074             mY = Math.max(mY, maxY[i]);
35075         }
35076         
35077         this.el.setHeight(mY - pos.y);
35078         
35079     },
35080     
35081 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35082 //    {
35083 //        var pos = this.el.getBox(true);
35084 //        var x = pos.x;
35085 //        var y = pos.y;
35086 //        var maxX = pos.right;
35087 //        
35088 //        var maxHeight = 0;
35089 //        
35090 //        Roo.each(items, function(item, k){
35091 //            
35092 //            var c = k % 2;
35093 //            
35094 //            item.el.position('absolute');
35095 //                
35096 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35097 //
35098 //            item.el.setWidth(width);
35099 //
35100 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35101 //
35102 //            item.el.setHeight(height);
35103 //            
35104 //            if(c == 0){
35105 //                item.el.setXY([x, y], isInstant ? false : true);
35106 //            } else {
35107 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35108 //            }
35109 //            
35110 //            y = y + height + this.alternativePadWidth;
35111 //            
35112 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35113 //            
35114 //        }, this);
35115 //        
35116 //        this.el.setHeight(maxHeight);
35117 //        
35118 //    },
35119     
35120     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35121     {
35122         var pos = this.el.getBox(true);
35123         
35124         var minX = pos.x;
35125         var minY = pos.y;
35126         
35127         var maxX = pos.right;
35128         
35129         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35130         
35131         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35132         
35133         Roo.each(queue, function(box, k){
35134             
35135             Roo.each(box, function(b, kk){
35136                 
35137                 b.el.position('absolute');
35138                 
35139                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35140                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35141                 
35142                 if(b.size == 'md-left' || b.size == 'md-right'){
35143                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35144                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35145                 }
35146                 
35147                 b.el.setWidth(width);
35148                 b.el.setHeight(height);
35149                 
35150             }, this);
35151             
35152             if(!box.length){
35153                 return;
35154             }
35155             
35156             var positions = [];
35157             
35158             switch (box.length){
35159                 case 1 :
35160                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35161                     break;
35162                 case 2 :
35163                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35164                     break;
35165                 case 3 :
35166                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35167                     break;
35168                 case 4 :
35169                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35170                     break;
35171                 default :
35172                     break;
35173             }
35174             
35175             Roo.each(box, function(b,kk){
35176                 
35177                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35178                 
35179                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35180                 
35181             }, this);
35182             
35183         }, this);
35184         
35185     },
35186     
35187     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35188     {
35189         Roo.each(eItems, function(b,k){
35190             
35191             b.size = (k == 0) ? 'sm' : 'xs';
35192             b.x = (k == 0) ? 2 : 1;
35193             b.y = (k == 0) ? 2 : 1;
35194             
35195             b.el.position('absolute');
35196             
35197             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35198                 
35199             b.el.setWidth(width);
35200             
35201             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35202             
35203             b.el.setHeight(height);
35204             
35205         }, this);
35206
35207         var positions = [];
35208         
35209         positions.push({
35210             x : maxX - this.unitWidth * 2 - this.gutter,
35211             y : minY
35212         });
35213         
35214         positions.push({
35215             x : maxX - this.unitWidth,
35216             y : minY + (this.unitWidth + this.gutter) * 2
35217         });
35218         
35219         positions.push({
35220             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35221             y : minY
35222         });
35223         
35224         Roo.each(eItems, function(b,k){
35225             
35226             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35227
35228         }, this);
35229         
35230     },
35231     
35232     getVerticalOneBoxColPositions : function(x, y, box)
35233     {
35234         var pos = [];
35235         
35236         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35237         
35238         if(box[0].size == 'md-left'){
35239             rand = 0;
35240         }
35241         
35242         if(box[0].size == 'md-right'){
35243             rand = 1;
35244         }
35245         
35246         pos.push({
35247             x : x + (this.unitWidth + this.gutter) * rand,
35248             y : y
35249         });
35250         
35251         return pos;
35252     },
35253     
35254     getVerticalTwoBoxColPositions : function(x, y, box)
35255     {
35256         var pos = [];
35257         
35258         if(box[0].size == 'xs'){
35259             
35260             pos.push({
35261                 x : x,
35262                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35263             });
35264
35265             pos.push({
35266                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35267                 y : y
35268             });
35269             
35270             return pos;
35271             
35272         }
35273         
35274         pos.push({
35275             x : x,
35276             y : y
35277         });
35278
35279         pos.push({
35280             x : x + (this.unitWidth + this.gutter) * 2,
35281             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35282         });
35283         
35284         return pos;
35285         
35286     },
35287     
35288     getVerticalThreeBoxColPositions : function(x, y, box)
35289     {
35290         var pos = [];
35291         
35292         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35293             
35294             pos.push({
35295                 x : x,
35296                 y : y
35297             });
35298
35299             pos.push({
35300                 x : x + (this.unitWidth + this.gutter) * 1,
35301                 y : y
35302             });
35303             
35304             pos.push({
35305                 x : x + (this.unitWidth + this.gutter) * 2,
35306                 y : y
35307             });
35308             
35309             return pos;
35310             
35311         }
35312         
35313         if(box[0].size == 'xs' && box[1].size == 'xs'){
35314             
35315             pos.push({
35316                 x : x,
35317                 y : y
35318             });
35319
35320             pos.push({
35321                 x : x,
35322                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35323             });
35324             
35325             pos.push({
35326                 x : x + (this.unitWidth + this.gutter) * 1,
35327                 y : y
35328             });
35329             
35330             return pos;
35331             
35332         }
35333         
35334         pos.push({
35335             x : x,
35336             y : y
35337         });
35338
35339         pos.push({
35340             x : x + (this.unitWidth + this.gutter) * 2,
35341             y : y
35342         });
35343
35344         pos.push({
35345             x : x + (this.unitWidth + this.gutter) * 2,
35346             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35347         });
35348             
35349         return pos;
35350         
35351     },
35352     
35353     getVerticalFourBoxColPositions : function(x, y, box)
35354     {
35355         var pos = [];
35356         
35357         if(box[0].size == 'xs'){
35358             
35359             pos.push({
35360                 x : x,
35361                 y : y
35362             });
35363
35364             pos.push({
35365                 x : x,
35366                 y : y + (this.unitHeight + this.gutter) * 1
35367             });
35368             
35369             pos.push({
35370                 x : x,
35371                 y : y + (this.unitHeight + this.gutter) * 2
35372             });
35373             
35374             pos.push({
35375                 x : x + (this.unitWidth + this.gutter) * 1,
35376                 y : y
35377             });
35378             
35379             return pos;
35380             
35381         }
35382         
35383         pos.push({
35384             x : x,
35385             y : y
35386         });
35387
35388         pos.push({
35389             x : x + (this.unitWidth + this.gutter) * 2,
35390             y : y
35391         });
35392
35393         pos.push({
35394             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35395             y : y + (this.unitHeight + this.gutter) * 1
35396         });
35397
35398         pos.push({
35399             x : x + (this.unitWidth + this.gutter) * 2,
35400             y : y + (this.unitWidth + this.gutter) * 2
35401         });
35402
35403         return pos;
35404         
35405     },
35406     
35407     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35408     {
35409         var pos = [];
35410         
35411         if(box[0].size == 'md-left'){
35412             pos.push({
35413                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35414                 y : minY
35415             });
35416             
35417             return pos;
35418         }
35419         
35420         if(box[0].size == 'md-right'){
35421             pos.push({
35422                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35423                 y : minY + (this.unitWidth + this.gutter) * 1
35424             });
35425             
35426             return pos;
35427         }
35428         
35429         var rand = Math.floor(Math.random() * (4 - box[0].y));
35430         
35431         pos.push({
35432             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35433             y : minY + (this.unitWidth + this.gutter) * rand
35434         });
35435         
35436         return pos;
35437         
35438     },
35439     
35440     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35441     {
35442         var pos = [];
35443         
35444         if(box[0].size == 'xs'){
35445             
35446             pos.push({
35447                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35448                 y : minY
35449             });
35450
35451             pos.push({
35452                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35453                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35454             });
35455             
35456             return pos;
35457             
35458         }
35459         
35460         pos.push({
35461             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35462             y : minY
35463         });
35464
35465         pos.push({
35466             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35467             y : minY + (this.unitWidth + this.gutter) * 2
35468         });
35469         
35470         return pos;
35471         
35472     },
35473     
35474     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35475     {
35476         var pos = [];
35477         
35478         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35479             
35480             pos.push({
35481                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35482                 y : minY
35483             });
35484
35485             pos.push({
35486                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35487                 y : minY + (this.unitWidth + this.gutter) * 1
35488             });
35489             
35490             pos.push({
35491                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35492                 y : minY + (this.unitWidth + this.gutter) * 2
35493             });
35494             
35495             return pos;
35496             
35497         }
35498         
35499         if(box[0].size == 'xs' && box[1].size == 'xs'){
35500             
35501             pos.push({
35502                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35503                 y : minY
35504             });
35505
35506             pos.push({
35507                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35508                 y : minY
35509             });
35510             
35511             pos.push({
35512                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35513                 y : minY + (this.unitWidth + this.gutter) * 1
35514             });
35515             
35516             return pos;
35517             
35518         }
35519         
35520         pos.push({
35521             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35522             y : minY
35523         });
35524
35525         pos.push({
35526             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35527             y : minY + (this.unitWidth + this.gutter) * 2
35528         });
35529
35530         pos.push({
35531             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35532             y : minY + (this.unitWidth + this.gutter) * 2
35533         });
35534             
35535         return pos;
35536         
35537     },
35538     
35539     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35540     {
35541         var pos = [];
35542         
35543         if(box[0].size == 'xs'){
35544             
35545             pos.push({
35546                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35547                 y : minY
35548             });
35549
35550             pos.push({
35551                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35552                 y : minY
35553             });
35554             
35555             pos.push({
35556                 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),
35557                 y : minY
35558             });
35559             
35560             pos.push({
35561                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35562                 y : minY + (this.unitWidth + this.gutter) * 1
35563             });
35564             
35565             return pos;
35566             
35567         }
35568         
35569         pos.push({
35570             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35571             y : minY
35572         });
35573         
35574         pos.push({
35575             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35576             y : minY + (this.unitWidth + this.gutter) * 2
35577         });
35578         
35579         pos.push({
35580             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35581             y : minY + (this.unitWidth + this.gutter) * 2
35582         });
35583         
35584         pos.push({
35585             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),
35586             y : minY + (this.unitWidth + this.gutter) * 2
35587         });
35588
35589         return pos;
35590         
35591     },
35592     
35593     /**
35594     * remove a Masonry Brick
35595     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35596     */
35597     removeBrick : function(brick_id)
35598     {
35599         if (!brick_id) {
35600             return;
35601         }
35602         
35603         for (var i = 0; i<this.bricks.length; i++) {
35604             if (this.bricks[i].id == brick_id) {
35605                 this.bricks.splice(i,1);
35606                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35607                 this.initial();
35608             }
35609         }
35610     },
35611     
35612     /**
35613     * adds a Masonry Brick
35614     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35615     */
35616     addBrick : function(cfg)
35617     {
35618         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35619         //this.register(cn);
35620         cn.parentId = this.id;
35621         cn.render(this.el);
35622         return cn;
35623     },
35624     
35625     /**
35626     * register a Masonry Brick
35627     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35628     */
35629     
35630     register : function(brick)
35631     {
35632         this.bricks.push(brick);
35633         brick.masonryId = this.id;
35634     },
35635     
35636     /**
35637     * clear all the Masonry Brick
35638     */
35639     clearAll : function()
35640     {
35641         this.bricks = [];
35642         //this.getChildContainer().dom.innerHTML = "";
35643         this.el.dom.innerHTML = '';
35644     },
35645     
35646     getSelected : function()
35647     {
35648         if (!this.selectedBrick) {
35649             return false;
35650         }
35651         
35652         return this.selectedBrick;
35653     }
35654 });
35655
35656 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35657     
35658     groups: {},
35659      /**
35660     * register a Masonry Layout
35661     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35662     */
35663     
35664     register : function(layout)
35665     {
35666         this.groups[layout.id] = layout;
35667     },
35668     /**
35669     * fetch a  Masonry Layout based on the masonry layout ID
35670     * @param {string} the masonry layout to add
35671     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35672     */
35673     
35674     get: function(layout_id) {
35675         if (typeof(this.groups[layout_id]) == 'undefined') {
35676             return false;
35677         }
35678         return this.groups[layout_id] ;
35679     }
35680     
35681     
35682     
35683 });
35684
35685  
35686
35687  /**
35688  *
35689  * This is based on 
35690  * http://masonry.desandro.com
35691  *
35692  * The idea is to render all the bricks based on vertical width...
35693  *
35694  * The original code extends 'outlayer' - we might need to use that....
35695  * 
35696  */
35697
35698
35699 /**
35700  * @class Roo.bootstrap.LayoutMasonryAuto
35701  * @extends Roo.bootstrap.Component
35702  * Bootstrap Layout Masonry class
35703  * 
35704  * @constructor
35705  * Create a new Element
35706  * @param {Object} config The config object
35707  */
35708
35709 Roo.bootstrap.LayoutMasonryAuto = function(config){
35710     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35711 };
35712
35713 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35714     
35715       /**
35716      * @cfg {Boolean} isFitWidth  - resize the width..
35717      */   
35718     isFitWidth : false,  // options..
35719     /**
35720      * @cfg {Boolean} isOriginLeft = left align?
35721      */   
35722     isOriginLeft : true,
35723     /**
35724      * @cfg {Boolean} isOriginTop = top align?
35725      */   
35726     isOriginTop : false,
35727     /**
35728      * @cfg {Boolean} isLayoutInstant = no animation?
35729      */   
35730     isLayoutInstant : false, // needed?
35731     /**
35732      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35733      */   
35734     isResizingContainer : true,
35735     /**
35736      * @cfg {Number} columnWidth  width of the columns 
35737      */   
35738     
35739     columnWidth : 0,
35740     
35741     /**
35742      * @cfg {Number} maxCols maximum number of columns
35743      */   
35744     
35745     maxCols: 0,
35746     /**
35747      * @cfg {Number} padHeight padding below box..
35748      */   
35749     
35750     padHeight : 10, 
35751     
35752     /**
35753      * @cfg {Boolean} isAutoInitial defalut true
35754      */   
35755     
35756     isAutoInitial : true, 
35757     
35758     // private?
35759     gutter : 0,
35760     
35761     containerWidth: 0,
35762     initialColumnWidth : 0,
35763     currentSize : null,
35764     
35765     colYs : null, // array.
35766     maxY : 0,
35767     padWidth: 10,
35768     
35769     
35770     tag: 'div',
35771     cls: '',
35772     bricks: null, //CompositeElement
35773     cols : 0, // array?
35774     // element : null, // wrapped now this.el
35775     _isLayoutInited : null, 
35776     
35777     
35778     getAutoCreate : function(){
35779         
35780         var cfg = {
35781             tag: this.tag,
35782             cls: 'blog-masonary-wrapper ' + this.cls,
35783             cn : {
35784                 cls : 'mas-boxes masonary'
35785             }
35786         };
35787         
35788         return cfg;
35789     },
35790     
35791     getChildContainer: function( )
35792     {
35793         if (this.boxesEl) {
35794             return this.boxesEl;
35795         }
35796         
35797         this.boxesEl = this.el.select('.mas-boxes').first();
35798         
35799         return this.boxesEl;
35800     },
35801     
35802     
35803     initEvents : function()
35804     {
35805         var _this = this;
35806         
35807         if(this.isAutoInitial){
35808             Roo.log('hook children rendered');
35809             this.on('childrenrendered', function() {
35810                 Roo.log('children rendered');
35811                 _this.initial();
35812             } ,this);
35813         }
35814         
35815     },
35816     
35817     initial : function()
35818     {
35819         this.reloadItems();
35820
35821         this.currentSize = this.el.getBox(true);
35822
35823         /// was window resize... - let's see if this works..
35824         Roo.EventManager.onWindowResize(this.resize, this); 
35825
35826         if(!this.isAutoInitial){
35827             this.layout();
35828             return;
35829         }
35830         
35831         this.layout.defer(500,this);
35832     },
35833     
35834     reloadItems: function()
35835     {
35836         this.bricks = this.el.select('.masonry-brick', true);
35837         
35838         this.bricks.each(function(b) {
35839             //Roo.log(b.getSize());
35840             if (!b.attr('originalwidth')) {
35841                 b.attr('originalwidth',  b.getSize().width);
35842             }
35843             
35844         });
35845         
35846         Roo.log(this.bricks.elements.length);
35847     },
35848     
35849     resize : function()
35850     {
35851         Roo.log('resize');
35852         var cs = this.el.getBox(true);
35853         
35854         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35855             Roo.log("no change in with or X");
35856             return;
35857         }
35858         this.currentSize = cs;
35859         this.layout();
35860     },
35861     
35862     layout : function()
35863     {
35864          Roo.log('layout');
35865         this._resetLayout();
35866         //this._manageStamps();
35867       
35868         // don't animate first layout
35869         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35870         this.layoutItems( isInstant );
35871       
35872         // flag for initalized
35873         this._isLayoutInited = true;
35874     },
35875     
35876     layoutItems : function( isInstant )
35877     {
35878         //var items = this._getItemsForLayout( this.items );
35879         // original code supports filtering layout items.. we just ignore it..
35880         
35881         this._layoutItems( this.bricks , isInstant );
35882       
35883         this._postLayout();
35884     },
35885     _layoutItems : function ( items , isInstant)
35886     {
35887        //this.fireEvent( 'layout', this, items );
35888     
35889
35890         if ( !items || !items.elements.length ) {
35891           // no items, emit event with empty array
35892             return;
35893         }
35894
35895         var queue = [];
35896         items.each(function(item) {
35897             Roo.log("layout item");
35898             Roo.log(item);
35899             // get x/y object from method
35900             var position = this._getItemLayoutPosition( item );
35901             // enqueue
35902             position.item = item;
35903             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35904             queue.push( position );
35905         }, this);
35906       
35907         this._processLayoutQueue( queue );
35908     },
35909     /** Sets position of item in DOM
35910     * @param {Element} item
35911     * @param {Number} x - horizontal position
35912     * @param {Number} y - vertical position
35913     * @param {Boolean} isInstant - disables transitions
35914     */
35915     _processLayoutQueue : function( queue )
35916     {
35917         for ( var i=0, len = queue.length; i < len; i++ ) {
35918             var obj = queue[i];
35919             obj.item.position('absolute');
35920             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35921         }
35922     },
35923       
35924     
35925     /**
35926     * Any logic you want to do after each layout,
35927     * i.e. size the container
35928     */
35929     _postLayout : function()
35930     {
35931         this.resizeContainer();
35932     },
35933     
35934     resizeContainer : function()
35935     {
35936         if ( !this.isResizingContainer ) {
35937             return;
35938         }
35939         var size = this._getContainerSize();
35940         if ( size ) {
35941             this.el.setSize(size.width,size.height);
35942             this.boxesEl.setSize(size.width,size.height);
35943         }
35944     },
35945     
35946     
35947     
35948     _resetLayout : function()
35949     {
35950         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35951         this.colWidth = this.el.getWidth();
35952         //this.gutter = this.el.getWidth(); 
35953         
35954         this.measureColumns();
35955
35956         // reset column Y
35957         var i = this.cols;
35958         this.colYs = [];
35959         while (i--) {
35960             this.colYs.push( 0 );
35961         }
35962     
35963         this.maxY = 0;
35964     },
35965
35966     measureColumns : function()
35967     {
35968         this.getContainerWidth();
35969       // if columnWidth is 0, default to outerWidth of first item
35970         if ( !this.columnWidth ) {
35971             var firstItem = this.bricks.first();
35972             Roo.log(firstItem);
35973             this.columnWidth  = this.containerWidth;
35974             if (firstItem && firstItem.attr('originalwidth') ) {
35975                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35976             }
35977             // columnWidth fall back to item of first element
35978             Roo.log("set column width?");
35979                         this.initialColumnWidth = this.columnWidth  ;
35980
35981             // if first elem has no width, default to size of container
35982             
35983         }
35984         
35985         
35986         if (this.initialColumnWidth) {
35987             this.columnWidth = this.initialColumnWidth;
35988         }
35989         
35990         
35991             
35992         // column width is fixed at the top - however if container width get's smaller we should
35993         // reduce it...
35994         
35995         // this bit calcs how man columns..
35996             
35997         var columnWidth = this.columnWidth += this.gutter;
35998       
35999         // calculate columns
36000         var containerWidth = this.containerWidth + this.gutter;
36001         
36002         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36003         // fix rounding errors, typically with gutters
36004         var excess = columnWidth - containerWidth % columnWidth;
36005         
36006         
36007         // if overshoot is less than a pixel, round up, otherwise floor it
36008         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36009         cols = Math[ mathMethod ]( cols );
36010         this.cols = Math.max( cols, 1 );
36011         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36012         
36013          // padding positioning..
36014         var totalColWidth = this.cols * this.columnWidth;
36015         var padavail = this.containerWidth - totalColWidth;
36016         // so for 2 columns - we need 3 'pads'
36017         
36018         var padNeeded = (1+this.cols) * this.padWidth;
36019         
36020         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36021         
36022         this.columnWidth += padExtra
36023         //this.padWidth = Math.floor(padavail /  ( this.cols));
36024         
36025         // adjust colum width so that padding is fixed??
36026         
36027         // we have 3 columns ... total = width * 3
36028         // we have X left over... that should be used by 
36029         
36030         //if (this.expandC) {
36031             
36032         //}
36033         
36034         
36035         
36036     },
36037     
36038     getContainerWidth : function()
36039     {
36040        /* // container is parent if fit width
36041         var container = this.isFitWidth ? this.element.parentNode : this.element;
36042         // check that this.size and size are there
36043         // IE8 triggers resize on body size change, so they might not be
36044         
36045         var size = getSize( container );  //FIXME
36046         this.containerWidth = size && size.innerWidth; //FIXME
36047         */
36048          
36049         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36050         
36051     },
36052     
36053     _getItemLayoutPosition : function( item )  // what is item?
36054     {
36055         // we resize the item to our columnWidth..
36056       
36057         item.setWidth(this.columnWidth);
36058         item.autoBoxAdjust  = false;
36059         
36060         var sz = item.getSize();
36061  
36062         // how many columns does this brick span
36063         var remainder = this.containerWidth % this.columnWidth;
36064         
36065         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36066         // round if off by 1 pixel, otherwise use ceil
36067         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36068         colSpan = Math.min( colSpan, this.cols );
36069         
36070         // normally this should be '1' as we dont' currently allow multi width columns..
36071         
36072         var colGroup = this._getColGroup( colSpan );
36073         // get the minimum Y value from the columns
36074         var minimumY = Math.min.apply( Math, colGroup );
36075         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36076         
36077         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36078          
36079         // position the brick
36080         var position = {
36081             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36082             y: this.currentSize.y + minimumY + this.padHeight
36083         };
36084         
36085         Roo.log(position);
36086         // apply setHeight to necessary columns
36087         var setHeight = minimumY + sz.height + this.padHeight;
36088         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36089         
36090         var setSpan = this.cols + 1 - colGroup.length;
36091         for ( var i = 0; i < setSpan; i++ ) {
36092           this.colYs[ shortColIndex + i ] = setHeight ;
36093         }
36094       
36095         return position;
36096     },
36097     
36098     /**
36099      * @param {Number} colSpan - number of columns the element spans
36100      * @returns {Array} colGroup
36101      */
36102     _getColGroup : function( colSpan )
36103     {
36104         if ( colSpan < 2 ) {
36105           // if brick spans only one column, use all the column Ys
36106           return this.colYs;
36107         }
36108       
36109         var colGroup = [];
36110         // how many different places could this brick fit horizontally
36111         var groupCount = this.cols + 1 - colSpan;
36112         // for each group potential horizontal position
36113         for ( var i = 0; i < groupCount; i++ ) {
36114           // make an array of colY values for that one group
36115           var groupColYs = this.colYs.slice( i, i + colSpan );
36116           // and get the max value of the array
36117           colGroup[i] = Math.max.apply( Math, groupColYs );
36118         }
36119         return colGroup;
36120     },
36121     /*
36122     _manageStamp : function( stamp )
36123     {
36124         var stampSize =  stamp.getSize();
36125         var offset = stamp.getBox();
36126         // get the columns that this stamp affects
36127         var firstX = this.isOriginLeft ? offset.x : offset.right;
36128         var lastX = firstX + stampSize.width;
36129         var firstCol = Math.floor( firstX / this.columnWidth );
36130         firstCol = Math.max( 0, firstCol );
36131         
36132         var lastCol = Math.floor( lastX / this.columnWidth );
36133         // lastCol should not go over if multiple of columnWidth #425
36134         lastCol -= lastX % this.columnWidth ? 0 : 1;
36135         lastCol = Math.min( this.cols - 1, lastCol );
36136         
36137         // set colYs to bottom of the stamp
36138         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36139             stampSize.height;
36140             
36141         for ( var i = firstCol; i <= lastCol; i++ ) {
36142           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36143         }
36144     },
36145     */
36146     
36147     _getContainerSize : function()
36148     {
36149         this.maxY = Math.max.apply( Math, this.colYs );
36150         var size = {
36151             height: this.maxY
36152         };
36153       
36154         if ( this.isFitWidth ) {
36155             size.width = this._getContainerFitWidth();
36156         }
36157       
36158         return size;
36159     },
36160     
36161     _getContainerFitWidth : function()
36162     {
36163         var unusedCols = 0;
36164         // count unused columns
36165         var i = this.cols;
36166         while ( --i ) {
36167           if ( this.colYs[i] !== 0 ) {
36168             break;
36169           }
36170           unusedCols++;
36171         }
36172         // fit container to columns that have been used
36173         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36174     },
36175     
36176     needsResizeLayout : function()
36177     {
36178         var previousWidth = this.containerWidth;
36179         this.getContainerWidth();
36180         return previousWidth !== this.containerWidth;
36181     }
36182  
36183 });
36184
36185  
36186
36187  /*
36188  * - LGPL
36189  *
36190  * element
36191  * 
36192  */
36193
36194 /**
36195  * @class Roo.bootstrap.MasonryBrick
36196  * @extends Roo.bootstrap.Component
36197  * Bootstrap MasonryBrick class
36198  * 
36199  * @constructor
36200  * Create a new MasonryBrick
36201  * @param {Object} config The config object
36202  */
36203
36204 Roo.bootstrap.MasonryBrick = function(config){
36205     
36206     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36207     
36208     Roo.bootstrap.MasonryBrick.register(this);
36209     
36210     this.addEvents({
36211         // raw events
36212         /**
36213          * @event click
36214          * When a MasonryBrick is clcik
36215          * @param {Roo.bootstrap.MasonryBrick} this
36216          * @param {Roo.EventObject} e
36217          */
36218         "click" : true
36219     });
36220 };
36221
36222 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36223     
36224     /**
36225      * @cfg {String} title
36226      */   
36227     title : '',
36228     /**
36229      * @cfg {String} html
36230      */   
36231     html : '',
36232     /**
36233      * @cfg {String} bgimage
36234      */   
36235     bgimage : '',
36236     /**
36237      * @cfg {String} videourl
36238      */   
36239     videourl : '',
36240     /**
36241      * @cfg {String} cls
36242      */   
36243     cls : '',
36244     /**
36245      * @cfg {String} href
36246      */   
36247     href : '',
36248     /**
36249      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36250      */   
36251     size : 'xs',
36252     
36253     /**
36254      * @cfg {String} placetitle (center|bottom)
36255      */   
36256     placetitle : '',
36257     
36258     /**
36259      * @cfg {Boolean} isFitContainer defalut true
36260      */   
36261     isFitContainer : true, 
36262     
36263     /**
36264      * @cfg {Boolean} preventDefault defalut false
36265      */   
36266     preventDefault : false, 
36267     
36268     /**
36269      * @cfg {Boolean} inverse defalut false
36270      */   
36271     maskInverse : false, 
36272     
36273     getAutoCreate : function()
36274     {
36275         if(!this.isFitContainer){
36276             return this.getSplitAutoCreate();
36277         }
36278         
36279         var cls = 'masonry-brick masonry-brick-full';
36280         
36281         if(this.href.length){
36282             cls += ' masonry-brick-link';
36283         }
36284         
36285         if(this.bgimage.length){
36286             cls += ' masonry-brick-image';
36287         }
36288         
36289         if(this.maskInverse){
36290             cls += ' mask-inverse';
36291         }
36292         
36293         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36294             cls += ' enable-mask';
36295         }
36296         
36297         if(this.size){
36298             cls += ' masonry-' + this.size + '-brick';
36299         }
36300         
36301         if(this.placetitle.length){
36302             
36303             switch (this.placetitle) {
36304                 case 'center' :
36305                     cls += ' masonry-center-title';
36306                     break;
36307                 case 'bottom' :
36308                     cls += ' masonry-bottom-title';
36309                     break;
36310                 default:
36311                     break;
36312             }
36313             
36314         } else {
36315             if(!this.html.length && !this.bgimage.length){
36316                 cls += ' masonry-center-title';
36317             }
36318
36319             if(!this.html.length && this.bgimage.length){
36320                 cls += ' masonry-bottom-title';
36321             }
36322         }
36323         
36324         if(this.cls){
36325             cls += ' ' + this.cls;
36326         }
36327         
36328         var cfg = {
36329             tag: (this.href.length) ? 'a' : 'div',
36330             cls: cls,
36331             cn: [
36332                 {
36333                     tag: 'div',
36334                     cls: 'masonry-brick-mask'
36335                 },
36336                 {
36337                     tag: 'div',
36338                     cls: 'masonry-brick-paragraph',
36339                     cn: []
36340                 }
36341             ]
36342         };
36343         
36344         if(this.href.length){
36345             cfg.href = this.href;
36346         }
36347         
36348         var cn = cfg.cn[1].cn;
36349         
36350         if(this.title.length){
36351             cn.push({
36352                 tag: 'h4',
36353                 cls: 'masonry-brick-title',
36354                 html: this.title
36355             });
36356         }
36357         
36358         if(this.html.length){
36359             cn.push({
36360                 tag: 'p',
36361                 cls: 'masonry-brick-text',
36362                 html: this.html
36363             });
36364         }
36365         
36366         if (!this.title.length && !this.html.length) {
36367             cfg.cn[1].cls += ' hide';
36368         }
36369         
36370         if(this.bgimage.length){
36371             cfg.cn.push({
36372                 tag: 'img',
36373                 cls: 'masonry-brick-image-view',
36374                 src: this.bgimage
36375             });
36376         }
36377         
36378         if(this.videourl.length){
36379             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36380             // youtube support only?
36381             cfg.cn.push({
36382                 tag: 'iframe',
36383                 cls: 'masonry-brick-image-view',
36384                 src: vurl,
36385                 frameborder : 0,
36386                 allowfullscreen : true
36387             });
36388         }
36389         
36390         return cfg;
36391         
36392     },
36393     
36394     getSplitAutoCreate : function()
36395     {
36396         var cls = 'masonry-brick masonry-brick-split';
36397         
36398         if(this.href.length){
36399             cls += ' masonry-brick-link';
36400         }
36401         
36402         if(this.bgimage.length){
36403             cls += ' masonry-brick-image';
36404         }
36405         
36406         if(this.size){
36407             cls += ' masonry-' + this.size + '-brick';
36408         }
36409         
36410         switch (this.placetitle) {
36411             case 'center' :
36412                 cls += ' masonry-center-title';
36413                 break;
36414             case 'bottom' :
36415                 cls += ' masonry-bottom-title';
36416                 break;
36417             default:
36418                 if(!this.bgimage.length){
36419                     cls += ' masonry-center-title';
36420                 }
36421
36422                 if(this.bgimage.length){
36423                     cls += ' masonry-bottom-title';
36424                 }
36425                 break;
36426         }
36427         
36428         if(this.cls){
36429             cls += ' ' + this.cls;
36430         }
36431         
36432         var cfg = {
36433             tag: (this.href.length) ? 'a' : 'div',
36434             cls: cls,
36435             cn: [
36436                 {
36437                     tag: 'div',
36438                     cls: 'masonry-brick-split-head',
36439                     cn: [
36440                         {
36441                             tag: 'div',
36442                             cls: 'masonry-brick-paragraph',
36443                             cn: []
36444                         }
36445                     ]
36446                 },
36447                 {
36448                     tag: 'div',
36449                     cls: 'masonry-brick-split-body',
36450                     cn: []
36451                 }
36452             ]
36453         };
36454         
36455         if(this.href.length){
36456             cfg.href = this.href;
36457         }
36458         
36459         if(this.title.length){
36460             cfg.cn[0].cn[0].cn.push({
36461                 tag: 'h4',
36462                 cls: 'masonry-brick-title',
36463                 html: this.title
36464             });
36465         }
36466         
36467         if(this.html.length){
36468             cfg.cn[1].cn.push({
36469                 tag: 'p',
36470                 cls: 'masonry-brick-text',
36471                 html: this.html
36472             });
36473         }
36474
36475         if(this.bgimage.length){
36476             cfg.cn[0].cn.push({
36477                 tag: 'img',
36478                 cls: 'masonry-brick-image-view',
36479                 src: this.bgimage
36480             });
36481         }
36482         
36483         if(this.videourl.length){
36484             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36485             // youtube support only?
36486             cfg.cn[0].cn.cn.push({
36487                 tag: 'iframe',
36488                 cls: 'masonry-brick-image-view',
36489                 src: vurl,
36490                 frameborder : 0,
36491                 allowfullscreen : true
36492             });
36493         }
36494         
36495         return cfg;
36496     },
36497     
36498     initEvents: function() 
36499     {
36500         switch (this.size) {
36501             case 'xs' :
36502                 this.x = 1;
36503                 this.y = 1;
36504                 break;
36505             case 'sm' :
36506                 this.x = 2;
36507                 this.y = 2;
36508                 break;
36509             case 'md' :
36510             case 'md-left' :
36511             case 'md-right' :
36512                 this.x = 3;
36513                 this.y = 3;
36514                 break;
36515             case 'tall' :
36516                 this.x = 2;
36517                 this.y = 3;
36518                 break;
36519             case 'wide' :
36520                 this.x = 3;
36521                 this.y = 2;
36522                 break;
36523             case 'wide-thin' :
36524                 this.x = 3;
36525                 this.y = 1;
36526                 break;
36527                         
36528             default :
36529                 break;
36530         }
36531         
36532         if(Roo.isTouch){
36533             this.el.on('touchstart', this.onTouchStart, this);
36534             this.el.on('touchmove', this.onTouchMove, this);
36535             this.el.on('touchend', this.onTouchEnd, this);
36536             this.el.on('contextmenu', this.onContextMenu, this);
36537         } else {
36538             this.el.on('mouseenter'  ,this.enter, this);
36539             this.el.on('mouseleave', this.leave, this);
36540             this.el.on('click', this.onClick, this);
36541         }
36542         
36543         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36544             this.parent().bricks.push(this);   
36545         }
36546         
36547     },
36548     
36549     onClick: function(e, el)
36550     {
36551         var time = this.endTimer - this.startTimer;
36552         // Roo.log(e.preventDefault());
36553         if(Roo.isTouch){
36554             if(time > 1000){
36555                 e.preventDefault();
36556                 return;
36557             }
36558         }
36559         
36560         if(!this.preventDefault){
36561             return;
36562         }
36563         
36564         e.preventDefault();
36565         
36566         if (this.activeClass != '') {
36567             this.selectBrick();
36568         }
36569         
36570         this.fireEvent('click', this, e);
36571     },
36572     
36573     enter: function(e, el)
36574     {
36575         e.preventDefault();
36576         
36577         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36578             return;
36579         }
36580         
36581         if(this.bgimage.length && this.html.length){
36582             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36583         }
36584     },
36585     
36586     leave: function(e, el)
36587     {
36588         e.preventDefault();
36589         
36590         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36591             return;
36592         }
36593         
36594         if(this.bgimage.length && this.html.length){
36595             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36596         }
36597     },
36598     
36599     onTouchStart: function(e, el)
36600     {
36601 //        e.preventDefault();
36602         
36603         this.touchmoved = false;
36604         
36605         if(!this.isFitContainer){
36606             return;
36607         }
36608         
36609         if(!this.bgimage.length || !this.html.length){
36610             return;
36611         }
36612         
36613         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36614         
36615         this.timer = new Date().getTime();
36616         
36617     },
36618     
36619     onTouchMove: function(e, el)
36620     {
36621         this.touchmoved = true;
36622     },
36623     
36624     onContextMenu : function(e,el)
36625     {
36626         e.preventDefault();
36627         e.stopPropagation();
36628         return false;
36629     },
36630     
36631     onTouchEnd: function(e, el)
36632     {
36633 //        e.preventDefault();
36634         
36635         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36636         
36637             this.leave(e,el);
36638             
36639             return;
36640         }
36641         
36642         if(!this.bgimage.length || !this.html.length){
36643             
36644             if(this.href.length){
36645                 window.location.href = this.href;
36646             }
36647             
36648             return;
36649         }
36650         
36651         if(!this.isFitContainer){
36652             return;
36653         }
36654         
36655         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36656         
36657         window.location.href = this.href;
36658     },
36659     
36660     //selection on single brick only
36661     selectBrick : function() {
36662         
36663         if (!this.parentId) {
36664             return;
36665         }
36666         
36667         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36668         var index = m.selectedBrick.indexOf(this.id);
36669         
36670         if ( index > -1) {
36671             m.selectedBrick.splice(index,1);
36672             this.el.removeClass(this.activeClass);
36673             return;
36674         }
36675         
36676         for(var i = 0; i < m.selectedBrick.length; i++) {
36677             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36678             b.el.removeClass(b.activeClass);
36679         }
36680         
36681         m.selectedBrick = [];
36682         
36683         m.selectedBrick.push(this.id);
36684         this.el.addClass(this.activeClass);
36685         return;
36686     },
36687     
36688     isSelected : function(){
36689         return this.el.hasClass(this.activeClass);
36690         
36691     }
36692 });
36693
36694 Roo.apply(Roo.bootstrap.MasonryBrick, {
36695     
36696     //groups: {},
36697     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36698      /**
36699     * register a Masonry Brick
36700     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36701     */
36702     
36703     register : function(brick)
36704     {
36705         //this.groups[brick.id] = brick;
36706         this.groups.add(brick.id, brick);
36707     },
36708     /**
36709     * fetch a  masonry brick based on the masonry brick ID
36710     * @param {string} the masonry brick to add
36711     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36712     */
36713     
36714     get: function(brick_id) 
36715     {
36716         // if (typeof(this.groups[brick_id]) == 'undefined') {
36717         //     return false;
36718         // }
36719         // return this.groups[brick_id] ;
36720         
36721         if(this.groups.key(brick_id)) {
36722             return this.groups.key(brick_id);
36723         }
36724         
36725         return false;
36726     }
36727     
36728     
36729     
36730 });
36731
36732  /*
36733  * - LGPL
36734  *
36735  * element
36736  * 
36737  */
36738
36739 /**
36740  * @class Roo.bootstrap.Brick
36741  * @extends Roo.bootstrap.Component
36742  * Bootstrap Brick class
36743  * 
36744  * @constructor
36745  * Create a new Brick
36746  * @param {Object} config The config object
36747  */
36748
36749 Roo.bootstrap.Brick = function(config){
36750     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36751     
36752     this.addEvents({
36753         // raw events
36754         /**
36755          * @event click
36756          * When a Brick is click
36757          * @param {Roo.bootstrap.Brick} this
36758          * @param {Roo.EventObject} e
36759          */
36760         "click" : true
36761     });
36762 };
36763
36764 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36765     
36766     /**
36767      * @cfg {String} title
36768      */   
36769     title : '',
36770     /**
36771      * @cfg {String} html
36772      */   
36773     html : '',
36774     /**
36775      * @cfg {String} bgimage
36776      */   
36777     bgimage : '',
36778     /**
36779      * @cfg {String} cls
36780      */   
36781     cls : '',
36782     /**
36783      * @cfg {String} href
36784      */   
36785     href : '',
36786     /**
36787      * @cfg {String} video
36788      */   
36789     video : '',
36790     /**
36791      * @cfg {Boolean} square
36792      */   
36793     square : true,
36794     
36795     getAutoCreate : function()
36796     {
36797         var cls = 'roo-brick';
36798         
36799         if(this.href.length){
36800             cls += ' roo-brick-link';
36801         }
36802         
36803         if(this.bgimage.length){
36804             cls += ' roo-brick-image';
36805         }
36806         
36807         if(!this.html.length && !this.bgimage.length){
36808             cls += ' roo-brick-center-title';
36809         }
36810         
36811         if(!this.html.length && this.bgimage.length){
36812             cls += ' roo-brick-bottom-title';
36813         }
36814         
36815         if(this.cls){
36816             cls += ' ' + this.cls;
36817         }
36818         
36819         var cfg = {
36820             tag: (this.href.length) ? 'a' : 'div',
36821             cls: cls,
36822             cn: [
36823                 {
36824                     tag: 'div',
36825                     cls: 'roo-brick-paragraph',
36826                     cn: []
36827                 }
36828             ]
36829         };
36830         
36831         if(this.href.length){
36832             cfg.href = this.href;
36833         }
36834         
36835         var cn = cfg.cn[0].cn;
36836         
36837         if(this.title.length){
36838             cn.push({
36839                 tag: 'h4',
36840                 cls: 'roo-brick-title',
36841                 html: this.title
36842             });
36843         }
36844         
36845         if(this.html.length){
36846             cn.push({
36847                 tag: 'p',
36848                 cls: 'roo-brick-text',
36849                 html: this.html
36850             });
36851         } else {
36852             cn.cls += ' hide';
36853         }
36854         
36855         if(this.bgimage.length){
36856             cfg.cn.push({
36857                 tag: 'img',
36858                 cls: 'roo-brick-image-view',
36859                 src: this.bgimage
36860             });
36861         }
36862         
36863         return cfg;
36864     },
36865     
36866     initEvents: function() 
36867     {
36868         if(this.title.length || this.html.length){
36869             this.el.on('mouseenter'  ,this.enter, this);
36870             this.el.on('mouseleave', this.leave, this);
36871         }
36872         
36873         Roo.EventManager.onWindowResize(this.resize, this); 
36874         
36875         if(this.bgimage.length){
36876             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36877             this.imageEl.on('load', this.onImageLoad, this);
36878             return;
36879         }
36880         
36881         this.resize();
36882     },
36883     
36884     onImageLoad : function()
36885     {
36886         this.resize();
36887     },
36888     
36889     resize : function()
36890     {
36891         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36892         
36893         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36894         
36895         if(this.bgimage.length){
36896             var image = this.el.select('.roo-brick-image-view', true).first();
36897             
36898             image.setWidth(paragraph.getWidth());
36899             
36900             if(this.square){
36901                 image.setHeight(paragraph.getWidth());
36902             }
36903             
36904             this.el.setHeight(image.getHeight());
36905             paragraph.setHeight(image.getHeight());
36906             
36907         }
36908         
36909     },
36910     
36911     enter: function(e, el)
36912     {
36913         e.preventDefault();
36914         
36915         if(this.bgimage.length){
36916             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36917             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36918         }
36919     },
36920     
36921     leave: function(e, el)
36922     {
36923         e.preventDefault();
36924         
36925         if(this.bgimage.length){
36926             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36927             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36928         }
36929     }
36930     
36931 });
36932
36933  
36934
36935  /*
36936  * - LGPL
36937  *
36938  * Number field 
36939  */
36940
36941 /**
36942  * @class Roo.bootstrap.NumberField
36943  * @extends Roo.bootstrap.Input
36944  * Bootstrap NumberField class
36945  * 
36946  * 
36947  * 
36948  * 
36949  * @constructor
36950  * Create a new NumberField
36951  * @param {Object} config The config object
36952  */
36953
36954 Roo.bootstrap.NumberField = function(config){
36955     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36956 };
36957
36958 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36959     
36960     /**
36961      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36962      */
36963     allowDecimals : true,
36964     /**
36965      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36966      */
36967     decimalSeparator : ".",
36968     /**
36969      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36970      */
36971     decimalPrecision : 2,
36972     /**
36973      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36974      */
36975     allowNegative : true,
36976     
36977     /**
36978      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36979      */
36980     allowZero: true,
36981     /**
36982      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36983      */
36984     minValue : Number.NEGATIVE_INFINITY,
36985     /**
36986      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36987      */
36988     maxValue : Number.MAX_VALUE,
36989     /**
36990      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36991      */
36992     minText : "The minimum value for this field is {0}",
36993     /**
36994      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36995      */
36996     maxText : "The maximum value for this field is {0}",
36997     /**
36998      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36999      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37000      */
37001     nanText : "{0} is not a valid number",
37002     /**
37003      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37004      */
37005     thousandsDelimiter : false,
37006     /**
37007      * @cfg {String} valueAlign alignment of value
37008      */
37009     valueAlign : "left",
37010
37011     getAutoCreate : function()
37012     {
37013         var hiddenInput = {
37014             tag: 'input',
37015             type: 'hidden',
37016             id: Roo.id(),
37017             cls: 'hidden-number-input'
37018         };
37019         
37020         if (this.name) {
37021             hiddenInput.name = this.name;
37022         }
37023         
37024         this.name = '';
37025         
37026         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37027         
37028         this.name = hiddenInput.name;
37029         
37030         if(cfg.cn.length > 0) {
37031             cfg.cn.push(hiddenInput);
37032         }
37033         
37034         return cfg;
37035     },
37036
37037     // private
37038     initEvents : function()
37039     {   
37040         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37041         
37042         var allowed = "0123456789";
37043         
37044         if(this.allowDecimals){
37045             allowed += this.decimalSeparator;
37046         }
37047         
37048         if(this.allowNegative){
37049             allowed += "-";
37050         }
37051         
37052         if(this.thousandsDelimiter) {
37053             allowed += ",";
37054         }
37055         
37056         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37057         
37058         var keyPress = function(e){
37059             
37060             var k = e.getKey();
37061             
37062             var c = e.getCharCode();
37063             
37064             if(
37065                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37066                     allowed.indexOf(String.fromCharCode(c)) === -1
37067             ){
37068                 e.stopEvent();
37069                 return;
37070             }
37071             
37072             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37073                 return;
37074             }
37075             
37076             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37077                 e.stopEvent();
37078             }
37079         };
37080         
37081         this.el.on("keypress", keyPress, this);
37082     },
37083     
37084     validateValue : function(value)
37085     {
37086         
37087         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37088             return false;
37089         }
37090         
37091         var num = this.parseValue(value);
37092         
37093         if(isNaN(num)){
37094             this.markInvalid(String.format(this.nanText, value));
37095             return false;
37096         }
37097         
37098         if(num < this.minValue){
37099             this.markInvalid(String.format(this.minText, this.minValue));
37100             return false;
37101         }
37102         
37103         if(num > this.maxValue){
37104             this.markInvalid(String.format(this.maxText, this.maxValue));
37105             return false;
37106         }
37107         
37108         return true;
37109     },
37110
37111     getValue : function()
37112     {
37113         var v = this.hiddenEl().getValue();
37114         
37115         return this.fixPrecision(this.parseValue(v));
37116     },
37117
37118     parseValue : function(value)
37119     {
37120         if(this.thousandsDelimiter) {
37121             value += "";
37122             r = new RegExp(",", "g");
37123             value = value.replace(r, "");
37124         }
37125         
37126         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37127         return isNaN(value) ? '' : value;
37128     },
37129
37130     fixPrecision : function(value)
37131     {
37132         if(this.thousandsDelimiter) {
37133             value += "";
37134             r = new RegExp(",", "g");
37135             value = value.replace(r, "");
37136         }
37137         
37138         var nan = isNaN(value);
37139         
37140         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37141             return nan ? '' : value;
37142         }
37143         return parseFloat(value).toFixed(this.decimalPrecision);
37144     },
37145
37146     setValue : function(v)
37147     {
37148         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37149         
37150         this.value = v;
37151         
37152         if(this.rendered){
37153             
37154             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37155             
37156             this.inputEl().dom.value = (v == '') ? '' :
37157                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37158             
37159             if(!this.allowZero && v === '0') {
37160                 this.hiddenEl().dom.value = '';
37161                 this.inputEl().dom.value = '';
37162             }
37163             
37164             this.validate();
37165         }
37166     },
37167
37168     decimalPrecisionFcn : function(v)
37169     {
37170         return Math.floor(v);
37171     },
37172
37173     beforeBlur : function()
37174     {
37175         var v = this.parseValue(this.getRawValue());
37176         
37177         if(v || v === 0 || v === ''){
37178             this.setValue(v);
37179         }
37180     },
37181     
37182     hiddenEl : function()
37183     {
37184         return this.el.select('input.hidden-number-input',true).first();
37185     }
37186     
37187 });
37188
37189  
37190
37191 /*
37192 * Licence: LGPL
37193 */
37194
37195 /**
37196  * @class Roo.bootstrap.DocumentSlider
37197  * @extends Roo.bootstrap.Component
37198  * Bootstrap DocumentSlider class
37199  * 
37200  * @constructor
37201  * Create a new DocumentViewer
37202  * @param {Object} config The config object
37203  */
37204
37205 Roo.bootstrap.DocumentSlider = function(config){
37206     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37207     
37208     this.files = [];
37209     
37210     this.addEvents({
37211         /**
37212          * @event initial
37213          * Fire after initEvent
37214          * @param {Roo.bootstrap.DocumentSlider} this
37215          */
37216         "initial" : true,
37217         /**
37218          * @event update
37219          * Fire after update
37220          * @param {Roo.bootstrap.DocumentSlider} this
37221          */
37222         "update" : true,
37223         /**
37224          * @event click
37225          * Fire after click
37226          * @param {Roo.bootstrap.DocumentSlider} this
37227          */
37228         "click" : true
37229     });
37230 };
37231
37232 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37233     
37234     files : false,
37235     
37236     indicator : 0,
37237     
37238     getAutoCreate : function()
37239     {
37240         var cfg = {
37241             tag : 'div',
37242             cls : 'roo-document-slider',
37243             cn : [
37244                 {
37245                     tag : 'div',
37246                     cls : 'roo-document-slider-header',
37247                     cn : [
37248                         {
37249                             tag : 'div',
37250                             cls : 'roo-document-slider-header-title'
37251                         }
37252                     ]
37253                 },
37254                 {
37255                     tag : 'div',
37256                     cls : 'roo-document-slider-body',
37257                     cn : [
37258                         {
37259                             tag : 'div',
37260                             cls : 'roo-document-slider-prev',
37261                             cn : [
37262                                 {
37263                                     tag : 'i',
37264                                     cls : 'fa fa-chevron-left'
37265                                 }
37266                             ]
37267                         },
37268                         {
37269                             tag : 'div',
37270                             cls : 'roo-document-slider-thumb',
37271                             cn : [
37272                                 {
37273                                     tag : 'img',
37274                                     cls : 'roo-document-slider-image'
37275                                 }
37276                             ]
37277                         },
37278                         {
37279                             tag : 'div',
37280                             cls : 'roo-document-slider-next',
37281                             cn : [
37282                                 {
37283                                     tag : 'i',
37284                                     cls : 'fa fa-chevron-right'
37285                                 }
37286                             ]
37287                         }
37288                     ]
37289                 }
37290             ]
37291         };
37292         
37293         return cfg;
37294     },
37295     
37296     initEvents : function()
37297     {
37298         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37299         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37300         
37301         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37302         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37303         
37304         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37305         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37306         
37307         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37308         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37309         
37310         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37311         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37312         
37313         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37314         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37315         
37316         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37317         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37318         
37319         this.thumbEl.on('click', this.onClick, this);
37320         
37321         this.prevIndicator.on('click', this.prev, this);
37322         
37323         this.nextIndicator.on('click', this.next, this);
37324         
37325     },
37326     
37327     initial : function()
37328     {
37329         if(this.files.length){
37330             this.indicator = 1;
37331             this.update()
37332         }
37333         
37334         this.fireEvent('initial', this);
37335     },
37336     
37337     update : function()
37338     {
37339         this.imageEl.attr('src', this.files[this.indicator - 1]);
37340         
37341         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37342         
37343         this.prevIndicator.show();
37344         
37345         if(this.indicator == 1){
37346             this.prevIndicator.hide();
37347         }
37348         
37349         this.nextIndicator.show();
37350         
37351         if(this.indicator == this.files.length){
37352             this.nextIndicator.hide();
37353         }
37354         
37355         this.thumbEl.scrollTo('top');
37356         
37357         this.fireEvent('update', this);
37358     },
37359     
37360     onClick : function(e)
37361     {
37362         e.preventDefault();
37363         
37364         this.fireEvent('click', this);
37365     },
37366     
37367     prev : function(e)
37368     {
37369         e.preventDefault();
37370         
37371         this.indicator = Math.max(1, this.indicator - 1);
37372         
37373         this.update();
37374     },
37375     
37376     next : function(e)
37377     {
37378         e.preventDefault();
37379         
37380         this.indicator = Math.min(this.files.length, this.indicator + 1);
37381         
37382         this.update();
37383     }
37384 });
37385 /*
37386  * - LGPL
37387  *
37388  * RadioSet
37389  *
37390  *
37391  */
37392
37393 /**
37394  * @class Roo.bootstrap.RadioSet
37395  * @extends Roo.bootstrap.Input
37396  * Bootstrap RadioSet class
37397  * @cfg {String} indicatorpos (left|right) default left
37398  * @cfg {Boolean} inline (true|false) inline the element (default true)
37399  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37400  * @constructor
37401  * Create a new RadioSet
37402  * @param {Object} config The config object
37403  */
37404
37405 Roo.bootstrap.RadioSet = function(config){
37406     
37407     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37408     
37409     this.radioes = [];
37410     
37411     Roo.bootstrap.RadioSet.register(this);
37412     
37413     this.addEvents({
37414         /**
37415         * @event check
37416         * Fires when the element is checked or unchecked.
37417         * @param {Roo.bootstrap.RadioSet} this This radio
37418         * @param {Roo.bootstrap.Radio} item The checked item
37419         */
37420        check : true,
37421        /**
37422         * @event click
37423         * Fires when the element is click.
37424         * @param {Roo.bootstrap.RadioSet} this This radio set
37425         * @param {Roo.bootstrap.Radio} item The checked item
37426         * @param {Roo.EventObject} e The event object
37427         */
37428        click : true
37429     });
37430     
37431 };
37432
37433 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37434
37435     radioes : false,
37436     
37437     inline : true,
37438     
37439     weight : '',
37440     
37441     indicatorpos : 'left',
37442     
37443     getAutoCreate : function()
37444     {
37445         var label = {
37446             tag : 'label',
37447             cls : 'roo-radio-set-label',
37448             cn : [
37449                 {
37450                     tag : 'span',
37451                     html : this.fieldLabel
37452                 }
37453             ]
37454         };
37455         if (Roo.bootstrap.version == 3) {
37456             
37457             
37458             if(this.indicatorpos == 'left'){
37459                 label.cn.unshift({
37460                     tag : 'i',
37461                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37462                     tooltip : 'This field is required'
37463                 });
37464             } else {
37465                 label.cn.push({
37466                     tag : 'i',
37467                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37468                     tooltip : 'This field is required'
37469                 });
37470             }
37471         }
37472         var items = {
37473             tag : 'div',
37474             cls : 'roo-radio-set-items'
37475         };
37476         
37477         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37478         
37479         if (align === 'left' && this.fieldLabel.length) {
37480             
37481             items = {
37482                 cls : "roo-radio-set-right", 
37483                 cn: [
37484                     items
37485                 ]
37486             };
37487             
37488             if(this.labelWidth > 12){
37489                 label.style = "width: " + this.labelWidth + 'px';
37490             }
37491             
37492             if(this.labelWidth < 13 && this.labelmd == 0){
37493                 this.labelmd = this.labelWidth;
37494             }
37495             
37496             if(this.labellg > 0){
37497                 label.cls += ' col-lg-' + this.labellg;
37498                 items.cls += ' col-lg-' + (12 - this.labellg);
37499             }
37500             
37501             if(this.labelmd > 0){
37502                 label.cls += ' col-md-' + this.labelmd;
37503                 items.cls += ' col-md-' + (12 - this.labelmd);
37504             }
37505             
37506             if(this.labelsm > 0){
37507                 label.cls += ' col-sm-' + this.labelsm;
37508                 items.cls += ' col-sm-' + (12 - this.labelsm);
37509             }
37510             
37511             if(this.labelxs > 0){
37512                 label.cls += ' col-xs-' + this.labelxs;
37513                 items.cls += ' col-xs-' + (12 - this.labelxs);
37514             }
37515         }
37516         
37517         var cfg = {
37518             tag : 'div',
37519             cls : 'roo-radio-set',
37520             cn : [
37521                 {
37522                     tag : 'input',
37523                     cls : 'roo-radio-set-input',
37524                     type : 'hidden',
37525                     name : this.name,
37526                     value : this.value ? this.value :  ''
37527                 },
37528                 label,
37529                 items
37530             ]
37531         };
37532         
37533         if(this.weight.length){
37534             cfg.cls += ' roo-radio-' + this.weight;
37535         }
37536         
37537         if(this.inline) {
37538             cfg.cls += ' roo-radio-set-inline';
37539         }
37540         
37541         var settings=this;
37542         ['xs','sm','md','lg'].map(function(size){
37543             if (settings[size]) {
37544                 cfg.cls += ' col-' + size + '-' + settings[size];
37545             }
37546         });
37547         
37548         return cfg;
37549         
37550     },
37551
37552     initEvents : function()
37553     {
37554         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37555         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37556         
37557         if(!this.fieldLabel.length){
37558             this.labelEl.hide();
37559         }
37560         
37561         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37562         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37563         
37564         this.indicator = this.indicatorEl();
37565         
37566         if(this.indicator){
37567             this.indicator.addClass('invisible');
37568         }
37569         
37570         this.originalValue = this.getValue();
37571         
37572     },
37573     
37574     inputEl: function ()
37575     {
37576         return this.el.select('.roo-radio-set-input', true).first();
37577     },
37578     
37579     getChildContainer : function()
37580     {
37581         return this.itemsEl;
37582     },
37583     
37584     register : function(item)
37585     {
37586         this.radioes.push(item);
37587         
37588     },
37589     
37590     validate : function()
37591     {   
37592         if(this.getVisibilityEl().hasClass('hidden')){
37593             return true;
37594         }
37595         
37596         var valid = false;
37597         
37598         Roo.each(this.radioes, function(i){
37599             if(!i.checked){
37600                 return;
37601             }
37602             
37603             valid = true;
37604             return false;
37605         });
37606         
37607         if(this.allowBlank) {
37608             return true;
37609         }
37610         
37611         if(this.disabled || valid){
37612             this.markValid();
37613             return true;
37614         }
37615         
37616         this.markInvalid();
37617         return false;
37618         
37619     },
37620     
37621     markValid : function()
37622     {
37623         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37624             this.indicatorEl().removeClass('visible');
37625             this.indicatorEl().addClass('invisible');
37626         }
37627         
37628         
37629         if (Roo.bootstrap.version == 3) {
37630             this.el.removeClass([this.invalidClass, this.validClass]);
37631             this.el.addClass(this.validClass);
37632         } else {
37633             this.el.removeClass(['is-invalid','is-valid']);
37634             this.el.addClass(['is-valid']);
37635         }
37636         this.fireEvent('valid', this);
37637     },
37638     
37639     markInvalid : function(msg)
37640     {
37641         if(this.allowBlank || this.disabled){
37642             return;
37643         }
37644         
37645         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37646             this.indicatorEl().removeClass('invisible');
37647             this.indicatorEl().addClass('visible');
37648         }
37649         if (Roo.bootstrap.version == 3) {
37650             this.el.removeClass([this.invalidClass, this.validClass]);
37651             this.el.addClass(this.invalidClass);
37652         } else {
37653             this.el.removeClass(['is-invalid','is-valid']);
37654             this.el.addClass(['is-invalid']);
37655         }
37656         
37657         this.fireEvent('invalid', this, msg);
37658         
37659     },
37660     
37661     setValue : function(v, suppressEvent)
37662     {   
37663         if(this.value === v){
37664             return;
37665         }
37666         
37667         this.value = v;
37668         
37669         if(this.rendered){
37670             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37671         }
37672         
37673         Roo.each(this.radioes, function(i){
37674             i.checked = false;
37675             i.el.removeClass('checked');
37676         });
37677         
37678         Roo.each(this.radioes, function(i){
37679             
37680             if(i.value === v || i.value.toString() === v.toString()){
37681                 i.checked = true;
37682                 i.el.addClass('checked');
37683                 
37684                 if(suppressEvent !== true){
37685                     this.fireEvent('check', this, i);
37686                 }
37687                 
37688                 return false;
37689             }
37690             
37691         }, this);
37692         
37693         this.validate();
37694     },
37695     
37696     clearInvalid : function(){
37697         
37698         if(!this.el || this.preventMark){
37699             return;
37700         }
37701         
37702         this.el.removeClass([this.invalidClass]);
37703         
37704         this.fireEvent('valid', this);
37705     }
37706     
37707 });
37708
37709 Roo.apply(Roo.bootstrap.RadioSet, {
37710     
37711     groups: {},
37712     
37713     register : function(set)
37714     {
37715         this.groups[set.name] = set;
37716     },
37717     
37718     get: function(name) 
37719     {
37720         if (typeof(this.groups[name]) == 'undefined') {
37721             return false;
37722         }
37723         
37724         return this.groups[name] ;
37725     }
37726     
37727 });
37728 /*
37729  * Based on:
37730  * Ext JS Library 1.1.1
37731  * Copyright(c) 2006-2007, Ext JS, LLC.
37732  *
37733  * Originally Released Under LGPL - original licence link has changed is not relivant.
37734  *
37735  * Fork - LGPL
37736  * <script type="text/javascript">
37737  */
37738
37739
37740 /**
37741  * @class Roo.bootstrap.SplitBar
37742  * @extends Roo.util.Observable
37743  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37744  * <br><br>
37745  * Usage:
37746  * <pre><code>
37747 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37748                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37749 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37750 split.minSize = 100;
37751 split.maxSize = 600;
37752 split.animate = true;
37753 split.on('moved', splitterMoved);
37754 </code></pre>
37755  * @constructor
37756  * Create a new SplitBar
37757  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37758  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37759  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37760  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37761                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37762                         position of the SplitBar).
37763  */
37764 Roo.bootstrap.SplitBar = function(cfg){
37765     
37766     /** @private */
37767     
37768     //{
37769     //  dragElement : elm
37770     //  resizingElement: el,
37771         // optional..
37772     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37773     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37774         // existingProxy ???
37775     //}
37776     
37777     this.el = Roo.get(cfg.dragElement, true);
37778     this.el.dom.unselectable = "on";
37779     /** @private */
37780     this.resizingEl = Roo.get(cfg.resizingElement, true);
37781
37782     /**
37783      * @private
37784      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37785      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37786      * @type Number
37787      */
37788     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37789     
37790     /**
37791      * The minimum size of the resizing element. (Defaults to 0)
37792      * @type Number
37793      */
37794     this.minSize = 0;
37795     
37796     /**
37797      * The maximum size of the resizing element. (Defaults to 2000)
37798      * @type Number
37799      */
37800     this.maxSize = 2000;
37801     
37802     /**
37803      * Whether to animate the transition to the new size
37804      * @type Boolean
37805      */
37806     this.animate = false;
37807     
37808     /**
37809      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37810      * @type Boolean
37811      */
37812     this.useShim = false;
37813     
37814     /** @private */
37815     this.shim = null;
37816     
37817     if(!cfg.existingProxy){
37818         /** @private */
37819         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37820     }else{
37821         this.proxy = Roo.get(cfg.existingProxy).dom;
37822     }
37823     /** @private */
37824     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37825     
37826     /** @private */
37827     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37828     
37829     /** @private */
37830     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37831     
37832     /** @private */
37833     this.dragSpecs = {};
37834     
37835     /**
37836      * @private The adapter to use to positon and resize elements
37837      */
37838     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37839     this.adapter.init(this);
37840     
37841     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37842         /** @private */
37843         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37844         this.el.addClass("roo-splitbar-h");
37845     }else{
37846         /** @private */
37847         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37848         this.el.addClass("roo-splitbar-v");
37849     }
37850     
37851     this.addEvents({
37852         /**
37853          * @event resize
37854          * Fires when the splitter is moved (alias for {@link #event-moved})
37855          * @param {Roo.bootstrap.SplitBar} this
37856          * @param {Number} newSize the new width or height
37857          */
37858         "resize" : true,
37859         /**
37860          * @event moved
37861          * Fires when the splitter is moved
37862          * @param {Roo.bootstrap.SplitBar} this
37863          * @param {Number} newSize the new width or height
37864          */
37865         "moved" : true,
37866         /**
37867          * @event beforeresize
37868          * Fires before the splitter is dragged
37869          * @param {Roo.bootstrap.SplitBar} this
37870          */
37871         "beforeresize" : true,
37872
37873         "beforeapply" : true
37874     });
37875
37876     Roo.util.Observable.call(this);
37877 };
37878
37879 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37880     onStartProxyDrag : function(x, y){
37881         this.fireEvent("beforeresize", this);
37882         if(!this.overlay){
37883             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37884             o.unselectable();
37885             o.enableDisplayMode("block");
37886             // all splitbars share the same overlay
37887             Roo.bootstrap.SplitBar.prototype.overlay = o;
37888         }
37889         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37890         this.overlay.show();
37891         Roo.get(this.proxy).setDisplayed("block");
37892         var size = this.adapter.getElementSize(this);
37893         this.activeMinSize = this.getMinimumSize();;
37894         this.activeMaxSize = this.getMaximumSize();;
37895         var c1 = size - this.activeMinSize;
37896         var c2 = Math.max(this.activeMaxSize - size, 0);
37897         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37898             this.dd.resetConstraints();
37899             this.dd.setXConstraint(
37900                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37901                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37902             );
37903             this.dd.setYConstraint(0, 0);
37904         }else{
37905             this.dd.resetConstraints();
37906             this.dd.setXConstraint(0, 0);
37907             this.dd.setYConstraint(
37908                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37909                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37910             );
37911          }
37912         this.dragSpecs.startSize = size;
37913         this.dragSpecs.startPoint = [x, y];
37914         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37915     },
37916     
37917     /** 
37918      * @private Called after the drag operation by the DDProxy
37919      */
37920     onEndProxyDrag : function(e){
37921         Roo.get(this.proxy).setDisplayed(false);
37922         var endPoint = Roo.lib.Event.getXY(e);
37923         if(this.overlay){
37924             this.overlay.hide();
37925         }
37926         var newSize;
37927         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37928             newSize = this.dragSpecs.startSize + 
37929                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37930                     endPoint[0] - this.dragSpecs.startPoint[0] :
37931                     this.dragSpecs.startPoint[0] - endPoint[0]
37932                 );
37933         }else{
37934             newSize = this.dragSpecs.startSize + 
37935                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37936                     endPoint[1] - this.dragSpecs.startPoint[1] :
37937                     this.dragSpecs.startPoint[1] - endPoint[1]
37938                 );
37939         }
37940         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37941         if(newSize != this.dragSpecs.startSize){
37942             if(this.fireEvent('beforeapply', this, newSize) !== false){
37943                 this.adapter.setElementSize(this, newSize);
37944                 this.fireEvent("moved", this, newSize);
37945                 this.fireEvent("resize", this, newSize);
37946             }
37947         }
37948     },
37949     
37950     /**
37951      * Get the adapter this SplitBar uses
37952      * @return The adapter object
37953      */
37954     getAdapter : function(){
37955         return this.adapter;
37956     },
37957     
37958     /**
37959      * Set the adapter this SplitBar uses
37960      * @param {Object} adapter A SplitBar adapter object
37961      */
37962     setAdapter : function(adapter){
37963         this.adapter = adapter;
37964         this.adapter.init(this);
37965     },
37966     
37967     /**
37968      * Gets the minimum size for the resizing element
37969      * @return {Number} The minimum size
37970      */
37971     getMinimumSize : function(){
37972         return this.minSize;
37973     },
37974     
37975     /**
37976      * Sets the minimum size for the resizing element
37977      * @param {Number} minSize The minimum size
37978      */
37979     setMinimumSize : function(minSize){
37980         this.minSize = minSize;
37981     },
37982     
37983     /**
37984      * Gets the maximum size for the resizing element
37985      * @return {Number} The maximum size
37986      */
37987     getMaximumSize : function(){
37988         return this.maxSize;
37989     },
37990     
37991     /**
37992      * Sets the maximum size for the resizing element
37993      * @param {Number} maxSize The maximum size
37994      */
37995     setMaximumSize : function(maxSize){
37996         this.maxSize = maxSize;
37997     },
37998     
37999     /**
38000      * Sets the initialize size for the resizing element
38001      * @param {Number} size The initial size
38002      */
38003     setCurrentSize : function(size){
38004         var oldAnimate = this.animate;
38005         this.animate = false;
38006         this.adapter.setElementSize(this, size);
38007         this.animate = oldAnimate;
38008     },
38009     
38010     /**
38011      * Destroy this splitbar. 
38012      * @param {Boolean} removeEl True to remove the element
38013      */
38014     destroy : function(removeEl){
38015         if(this.shim){
38016             this.shim.remove();
38017         }
38018         this.dd.unreg();
38019         this.proxy.parentNode.removeChild(this.proxy);
38020         if(removeEl){
38021             this.el.remove();
38022         }
38023     }
38024 });
38025
38026 /**
38027  * @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.
38028  */
38029 Roo.bootstrap.SplitBar.createProxy = function(dir){
38030     var proxy = new Roo.Element(document.createElement("div"));
38031     proxy.unselectable();
38032     var cls = 'roo-splitbar-proxy';
38033     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38034     document.body.appendChild(proxy.dom);
38035     return proxy.dom;
38036 };
38037
38038 /** 
38039  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38040  * Default Adapter. It assumes the splitter and resizing element are not positioned
38041  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38042  */
38043 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38044 };
38045
38046 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38047     // do nothing for now
38048     init : function(s){
38049     
38050     },
38051     /**
38052      * Called before drag operations to get the current size of the resizing element. 
38053      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38054      */
38055      getElementSize : function(s){
38056         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38057             return s.resizingEl.getWidth();
38058         }else{
38059             return s.resizingEl.getHeight();
38060         }
38061     },
38062     
38063     /**
38064      * Called after drag operations to set the size of the resizing element.
38065      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38066      * @param {Number} newSize The new size to set
38067      * @param {Function} onComplete A function to be invoked when resizing is complete
38068      */
38069     setElementSize : function(s, newSize, onComplete){
38070         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38071             if(!s.animate){
38072                 s.resizingEl.setWidth(newSize);
38073                 if(onComplete){
38074                     onComplete(s, newSize);
38075                 }
38076             }else{
38077                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38078             }
38079         }else{
38080             
38081             if(!s.animate){
38082                 s.resizingEl.setHeight(newSize);
38083                 if(onComplete){
38084                     onComplete(s, newSize);
38085                 }
38086             }else{
38087                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38088             }
38089         }
38090     }
38091 };
38092
38093 /** 
38094  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38095  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38096  * Adapter that  moves the splitter element to align with the resized sizing element. 
38097  * Used with an absolute positioned SplitBar.
38098  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38099  * document.body, make sure you assign an id to the body element.
38100  */
38101 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38102     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38103     this.container = Roo.get(container);
38104 };
38105
38106 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38107     init : function(s){
38108         this.basic.init(s);
38109     },
38110     
38111     getElementSize : function(s){
38112         return this.basic.getElementSize(s);
38113     },
38114     
38115     setElementSize : function(s, newSize, onComplete){
38116         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38117     },
38118     
38119     moveSplitter : function(s){
38120         var yes = Roo.bootstrap.SplitBar;
38121         switch(s.placement){
38122             case yes.LEFT:
38123                 s.el.setX(s.resizingEl.getRight());
38124                 break;
38125             case yes.RIGHT:
38126                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38127                 break;
38128             case yes.TOP:
38129                 s.el.setY(s.resizingEl.getBottom());
38130                 break;
38131             case yes.BOTTOM:
38132                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38133                 break;
38134         }
38135     }
38136 };
38137
38138 /**
38139  * Orientation constant - Create a vertical SplitBar
38140  * @static
38141  * @type Number
38142  */
38143 Roo.bootstrap.SplitBar.VERTICAL = 1;
38144
38145 /**
38146  * Orientation constant - Create a horizontal SplitBar
38147  * @static
38148  * @type Number
38149  */
38150 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38151
38152 /**
38153  * Placement constant - The resizing element is to the left of the splitter element
38154  * @static
38155  * @type Number
38156  */
38157 Roo.bootstrap.SplitBar.LEFT = 1;
38158
38159 /**
38160  * Placement constant - The resizing element is to the right of the splitter element
38161  * @static
38162  * @type Number
38163  */
38164 Roo.bootstrap.SplitBar.RIGHT = 2;
38165
38166 /**
38167  * Placement constant - The resizing element is positioned above the splitter element
38168  * @static
38169  * @type Number
38170  */
38171 Roo.bootstrap.SplitBar.TOP = 3;
38172
38173 /**
38174  * Placement constant - The resizing element is positioned under splitter element
38175  * @static
38176  * @type Number
38177  */
38178 Roo.bootstrap.SplitBar.BOTTOM = 4;
38179 Roo.namespace("Roo.bootstrap.layout");/*
38180  * Based on:
38181  * Ext JS Library 1.1.1
38182  * Copyright(c) 2006-2007, Ext JS, LLC.
38183  *
38184  * Originally Released Under LGPL - original licence link has changed is not relivant.
38185  *
38186  * Fork - LGPL
38187  * <script type="text/javascript">
38188  */
38189
38190 /**
38191  * @class Roo.bootstrap.layout.Manager
38192  * @extends Roo.bootstrap.Component
38193  * Base class for layout managers.
38194  */
38195 Roo.bootstrap.layout.Manager = function(config)
38196 {
38197     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38198
38199
38200
38201
38202
38203     /** false to disable window resize monitoring @type Boolean */
38204     this.monitorWindowResize = true;
38205     this.regions = {};
38206     this.addEvents({
38207         /**
38208          * @event layout
38209          * Fires when a layout is performed.
38210          * @param {Roo.LayoutManager} this
38211          */
38212         "layout" : true,
38213         /**
38214          * @event regionresized
38215          * Fires when the user resizes a region.
38216          * @param {Roo.LayoutRegion} region The resized region
38217          * @param {Number} newSize The new size (width for east/west, height for north/south)
38218          */
38219         "regionresized" : true,
38220         /**
38221          * @event regioncollapsed
38222          * Fires when a region is collapsed.
38223          * @param {Roo.LayoutRegion} region The collapsed region
38224          */
38225         "regioncollapsed" : true,
38226         /**
38227          * @event regionexpanded
38228          * Fires when a region is expanded.
38229          * @param {Roo.LayoutRegion} region The expanded region
38230          */
38231         "regionexpanded" : true
38232     });
38233     this.updating = false;
38234
38235     if (config.el) {
38236         this.el = Roo.get(config.el);
38237         this.initEvents();
38238     }
38239
38240 };
38241
38242 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38243
38244
38245     regions : null,
38246
38247     monitorWindowResize : true,
38248
38249
38250     updating : false,
38251
38252
38253     onRender : function(ct, position)
38254     {
38255         if(!this.el){
38256             this.el = Roo.get(ct);
38257             this.initEvents();
38258         }
38259         //this.fireEvent('render',this);
38260     },
38261
38262
38263     initEvents: function()
38264     {
38265
38266
38267         // ie scrollbar fix
38268         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38269             document.body.scroll = "no";
38270         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38271             this.el.position('relative');
38272         }
38273         this.id = this.el.id;
38274         this.el.addClass("roo-layout-container");
38275         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38276         if(this.el.dom != document.body ) {
38277             this.el.on('resize', this.layout,this);
38278             this.el.on('show', this.layout,this);
38279         }
38280
38281     },
38282
38283     /**
38284      * Returns true if this layout is currently being updated
38285      * @return {Boolean}
38286      */
38287     isUpdating : function(){
38288         return this.updating;
38289     },
38290
38291     /**
38292      * Suspend the LayoutManager from doing auto-layouts while
38293      * making multiple add or remove calls
38294      */
38295     beginUpdate : function(){
38296         this.updating = true;
38297     },
38298
38299     /**
38300      * Restore auto-layouts and optionally disable the manager from performing a layout
38301      * @param {Boolean} noLayout true to disable a layout update
38302      */
38303     endUpdate : function(noLayout){
38304         this.updating = false;
38305         if(!noLayout){
38306             this.layout();
38307         }
38308     },
38309
38310     layout: function(){
38311         // abstract...
38312     },
38313
38314     onRegionResized : function(region, newSize){
38315         this.fireEvent("regionresized", region, newSize);
38316         this.layout();
38317     },
38318
38319     onRegionCollapsed : function(region){
38320         this.fireEvent("regioncollapsed", region);
38321     },
38322
38323     onRegionExpanded : function(region){
38324         this.fireEvent("regionexpanded", region);
38325     },
38326
38327     /**
38328      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38329      * performs box-model adjustments.
38330      * @return {Object} The size as an object {width: (the width), height: (the height)}
38331      */
38332     getViewSize : function()
38333     {
38334         var size;
38335         if(this.el.dom != document.body){
38336             size = this.el.getSize();
38337         }else{
38338             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38339         }
38340         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38341         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38342         return size;
38343     },
38344
38345     /**
38346      * Returns the Element this layout is bound to.
38347      * @return {Roo.Element}
38348      */
38349     getEl : function(){
38350         return this.el;
38351     },
38352
38353     /**
38354      * Returns the specified region.
38355      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38356      * @return {Roo.LayoutRegion}
38357      */
38358     getRegion : function(target){
38359         return this.regions[target.toLowerCase()];
38360     },
38361
38362     onWindowResize : function(){
38363         if(this.monitorWindowResize){
38364             this.layout();
38365         }
38366     }
38367 });
38368 /*
38369  * Based on:
38370  * Ext JS Library 1.1.1
38371  * Copyright(c) 2006-2007, Ext JS, LLC.
38372  *
38373  * Originally Released Under LGPL - original licence link has changed is not relivant.
38374  *
38375  * Fork - LGPL
38376  * <script type="text/javascript">
38377  */
38378 /**
38379  * @class Roo.bootstrap.layout.Border
38380  * @extends Roo.bootstrap.layout.Manager
38381  * @builder-top
38382  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38383  * please see: examples/bootstrap/nested.html<br><br>
38384  
38385 <b>The container the layout is rendered into can be either the body element or any other element.
38386 If it is not the body element, the container needs to either be an absolute positioned element,
38387 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38388 the container size if it is not the body element.</b>
38389
38390 * @constructor
38391 * Create a new Border
38392 * @param {Object} config Configuration options
38393  */
38394 Roo.bootstrap.layout.Border = function(config){
38395     config = config || {};
38396     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38397     
38398     
38399     
38400     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38401         if(config[region]){
38402             config[region].region = region;
38403             this.addRegion(config[region]);
38404         }
38405     },this);
38406     
38407 };
38408
38409 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38410
38411 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38412     
38413     parent : false, // this might point to a 'nest' or a ???
38414     
38415     /**
38416      * Creates and adds a new region if it doesn't already exist.
38417      * @param {String} target The target region key (north, south, east, west or center).
38418      * @param {Object} config The regions config object
38419      * @return {BorderLayoutRegion} The new region
38420      */
38421     addRegion : function(config)
38422     {
38423         if(!this.regions[config.region]){
38424             var r = this.factory(config);
38425             this.bindRegion(r);
38426         }
38427         return this.regions[config.region];
38428     },
38429
38430     // private (kinda)
38431     bindRegion : function(r){
38432         this.regions[r.config.region] = r;
38433         
38434         r.on("visibilitychange",    this.layout, this);
38435         r.on("paneladded",          this.layout, this);
38436         r.on("panelremoved",        this.layout, this);
38437         r.on("invalidated",         this.layout, this);
38438         r.on("resized",             this.onRegionResized, this);
38439         r.on("collapsed",           this.onRegionCollapsed, this);
38440         r.on("expanded",            this.onRegionExpanded, this);
38441     },
38442
38443     /**
38444      * Performs a layout update.
38445      */
38446     layout : function()
38447     {
38448         if(this.updating) {
38449             return;
38450         }
38451         
38452         // render all the rebions if they have not been done alreayd?
38453         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38454             if(this.regions[region] && !this.regions[region].bodyEl){
38455                 this.regions[region].onRender(this.el)
38456             }
38457         },this);
38458         
38459         var size = this.getViewSize();
38460         var w = size.width;
38461         var h = size.height;
38462         var centerW = w;
38463         var centerH = h;
38464         var centerY = 0;
38465         var centerX = 0;
38466         //var x = 0, y = 0;
38467
38468         var rs = this.regions;
38469         var north = rs["north"];
38470         var south = rs["south"]; 
38471         var west = rs["west"];
38472         var east = rs["east"];
38473         var center = rs["center"];
38474         //if(this.hideOnLayout){ // not supported anymore
38475             //c.el.setStyle("display", "none");
38476         //}
38477         if(north && north.isVisible()){
38478             var b = north.getBox();
38479             var m = north.getMargins();
38480             b.width = w - (m.left+m.right);
38481             b.x = m.left;
38482             b.y = m.top;
38483             centerY = b.height + b.y + m.bottom;
38484             centerH -= centerY;
38485             north.updateBox(this.safeBox(b));
38486         }
38487         if(south && south.isVisible()){
38488             var b = south.getBox();
38489             var m = south.getMargins();
38490             b.width = w - (m.left+m.right);
38491             b.x = m.left;
38492             var totalHeight = (b.height + m.top + m.bottom);
38493             b.y = h - totalHeight + m.top;
38494             centerH -= totalHeight;
38495             south.updateBox(this.safeBox(b));
38496         }
38497         if(west && west.isVisible()){
38498             var b = west.getBox();
38499             var m = west.getMargins();
38500             b.height = centerH - (m.top+m.bottom);
38501             b.x = m.left;
38502             b.y = centerY + m.top;
38503             var totalWidth = (b.width + m.left + m.right);
38504             centerX += totalWidth;
38505             centerW -= totalWidth;
38506             west.updateBox(this.safeBox(b));
38507         }
38508         if(east && east.isVisible()){
38509             var b = east.getBox();
38510             var m = east.getMargins();
38511             b.height = centerH - (m.top+m.bottom);
38512             var totalWidth = (b.width + m.left + m.right);
38513             b.x = w - totalWidth + m.left;
38514             b.y = centerY + m.top;
38515             centerW -= totalWidth;
38516             east.updateBox(this.safeBox(b));
38517         }
38518         if(center){
38519             var m = center.getMargins();
38520             var centerBox = {
38521                 x: centerX + m.left,
38522                 y: centerY + m.top,
38523                 width: centerW - (m.left+m.right),
38524                 height: centerH - (m.top+m.bottom)
38525             };
38526             //if(this.hideOnLayout){
38527                 //center.el.setStyle("display", "block");
38528             //}
38529             center.updateBox(this.safeBox(centerBox));
38530         }
38531         this.el.repaint();
38532         this.fireEvent("layout", this);
38533     },
38534
38535     // private
38536     safeBox : function(box){
38537         box.width = Math.max(0, box.width);
38538         box.height = Math.max(0, box.height);
38539         return box;
38540     },
38541
38542     /**
38543      * Adds a ContentPanel (or subclass) to this layout.
38544      * @param {String} target The target region key (north, south, east, west or center).
38545      * @param {Roo.ContentPanel} panel The panel to add
38546      * @return {Roo.ContentPanel} The added panel
38547      */
38548     add : function(target, panel){
38549          
38550         target = target.toLowerCase();
38551         return this.regions[target].add(panel);
38552     },
38553
38554     /**
38555      * Remove a ContentPanel (or subclass) to this layout.
38556      * @param {String} target The target region key (north, south, east, west or center).
38557      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38558      * @return {Roo.ContentPanel} The removed panel
38559      */
38560     remove : function(target, panel){
38561         target = target.toLowerCase();
38562         return this.regions[target].remove(panel);
38563     },
38564
38565     /**
38566      * Searches all regions for a panel with the specified id
38567      * @param {String} panelId
38568      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38569      */
38570     findPanel : function(panelId){
38571         var rs = this.regions;
38572         for(var target in rs){
38573             if(typeof rs[target] != "function"){
38574                 var p = rs[target].getPanel(panelId);
38575                 if(p){
38576                     return p;
38577                 }
38578             }
38579         }
38580         return null;
38581     },
38582
38583     /**
38584      * Searches all regions for a panel with the specified id and activates (shows) it.
38585      * @param {String/ContentPanel} panelId The panels id or the panel itself
38586      * @return {Roo.ContentPanel} The shown panel or null
38587      */
38588     showPanel : function(panelId) {
38589       var rs = this.regions;
38590       for(var target in rs){
38591          var r = rs[target];
38592          if(typeof r != "function"){
38593             if(r.hasPanel(panelId)){
38594                return r.showPanel(panelId);
38595             }
38596          }
38597       }
38598       return null;
38599    },
38600
38601    /**
38602      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38603      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38604      */
38605    /*
38606     restoreState : function(provider){
38607         if(!provider){
38608             provider = Roo.state.Manager;
38609         }
38610         var sm = new Roo.LayoutStateManager();
38611         sm.init(this, provider);
38612     },
38613 */
38614  
38615  
38616     /**
38617      * Adds a xtype elements to the layout.
38618      * <pre><code>
38619
38620 layout.addxtype({
38621        xtype : 'ContentPanel',
38622        region: 'west',
38623        items: [ .... ]
38624    }
38625 );
38626
38627 layout.addxtype({
38628         xtype : 'NestedLayoutPanel',
38629         region: 'west',
38630         layout: {
38631            center: { },
38632            west: { }   
38633         },
38634         items : [ ... list of content panels or nested layout panels.. ]
38635    }
38636 );
38637 </code></pre>
38638      * @param {Object} cfg Xtype definition of item to add.
38639      */
38640     addxtype : function(cfg)
38641     {
38642         // basically accepts a pannel...
38643         // can accept a layout region..!?!?
38644         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38645         
38646         
38647         // theory?  children can only be panels??
38648         
38649         //if (!cfg.xtype.match(/Panel$/)) {
38650         //    return false;
38651         //}
38652         var ret = false;
38653         
38654         if (typeof(cfg.region) == 'undefined') {
38655             Roo.log("Failed to add Panel, region was not set");
38656             Roo.log(cfg);
38657             return false;
38658         }
38659         var region = cfg.region;
38660         delete cfg.region;
38661         
38662           
38663         var xitems = [];
38664         if (cfg.items) {
38665             xitems = cfg.items;
38666             delete cfg.items;
38667         }
38668         var nb = false;
38669         
38670         if ( region == 'center') {
38671             Roo.log("Center: " + cfg.title);
38672         }
38673         
38674         
38675         switch(cfg.xtype) 
38676         {
38677             case 'Content':  // ContentPanel (el, cfg)
38678             case 'Scroll':  // ContentPanel (el, cfg)
38679             case 'View': 
38680                 cfg.autoCreate = cfg.autoCreate || true;
38681                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38682                 //} else {
38683                 //    var el = this.el.createChild();
38684                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38685                 //}
38686                 
38687                 this.add(region, ret);
38688                 break;
38689             
38690             /*
38691             case 'TreePanel': // our new panel!
38692                 cfg.el = this.el.createChild();
38693                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38694                 this.add(region, ret);
38695                 break;
38696             */
38697             
38698             case 'Nest': 
38699                 // create a new Layout (which is  a Border Layout...
38700                 
38701                 var clayout = cfg.layout;
38702                 clayout.el  = this.el.createChild();
38703                 clayout.items   = clayout.items  || [];
38704                 
38705                 delete cfg.layout;
38706                 
38707                 // replace this exitems with the clayout ones..
38708                 xitems = clayout.items;
38709                  
38710                 // force background off if it's in center...
38711                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38712                     cfg.background = false;
38713                 }
38714                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38715                 
38716                 
38717                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38718                 //console.log('adding nested layout panel '  + cfg.toSource());
38719                 this.add(region, ret);
38720                 nb = {}; /// find first...
38721                 break;
38722             
38723             case 'Grid':
38724                 
38725                 // needs grid and region
38726                 
38727                 //var el = this.getRegion(region).el.createChild();
38728                 /*
38729                  *var el = this.el.createChild();
38730                 // create the grid first...
38731                 cfg.grid.container = el;
38732                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38733                 */
38734                 
38735                 if (region == 'center' && this.active ) {
38736                     cfg.background = false;
38737                 }
38738                 
38739                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38740                 
38741                 this.add(region, ret);
38742                 /*
38743                 if (cfg.background) {
38744                     // render grid on panel activation (if panel background)
38745                     ret.on('activate', function(gp) {
38746                         if (!gp.grid.rendered) {
38747                     //        gp.grid.render(el);
38748                         }
38749                     });
38750                 } else {
38751                   //  cfg.grid.render(el);
38752                 }
38753                 */
38754                 break;
38755            
38756            
38757             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38758                 // it was the old xcomponent building that caused this before.
38759                 // espeically if border is the top element in the tree.
38760                 ret = this;
38761                 break; 
38762                 
38763                     
38764                 
38765                 
38766                 
38767             default:
38768                 /*
38769                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38770                     
38771                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38772                     this.add(region, ret);
38773                 } else {
38774                 */
38775                     Roo.log(cfg);
38776                     throw "Can not add '" + cfg.xtype + "' to Border";
38777                     return null;
38778              
38779                                 
38780              
38781         }
38782         this.beginUpdate();
38783         // add children..
38784         var region = '';
38785         var abn = {};
38786         Roo.each(xitems, function(i)  {
38787             region = nb && i.region ? i.region : false;
38788             
38789             var add = ret.addxtype(i);
38790            
38791             if (region) {
38792                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38793                 if (!i.background) {
38794                     abn[region] = nb[region] ;
38795                 }
38796             }
38797             
38798         });
38799         this.endUpdate();
38800
38801         // make the last non-background panel active..
38802         //if (nb) { Roo.log(abn); }
38803         if (nb) {
38804             
38805             for(var r in abn) {
38806                 region = this.getRegion(r);
38807                 if (region) {
38808                     // tried using nb[r], but it does not work..
38809                      
38810                     region.showPanel(abn[r]);
38811                    
38812                 }
38813             }
38814         }
38815         return ret;
38816         
38817     },
38818     
38819     
38820 // private
38821     factory : function(cfg)
38822     {
38823         
38824         var validRegions = Roo.bootstrap.layout.Border.regions;
38825
38826         var target = cfg.region;
38827         cfg.mgr = this;
38828         
38829         var r = Roo.bootstrap.layout;
38830         Roo.log(target);
38831         switch(target){
38832             case "north":
38833                 return new r.North(cfg);
38834             case "south":
38835                 return new r.South(cfg);
38836             case "east":
38837                 return new r.East(cfg);
38838             case "west":
38839                 return new r.West(cfg);
38840             case "center":
38841                 return new r.Center(cfg);
38842         }
38843         throw 'Layout region "'+target+'" not supported.';
38844     }
38845     
38846     
38847 });
38848  /*
38849  * Based on:
38850  * Ext JS Library 1.1.1
38851  * Copyright(c) 2006-2007, Ext JS, LLC.
38852  *
38853  * Originally Released Under LGPL - original licence link has changed is not relivant.
38854  *
38855  * Fork - LGPL
38856  * <script type="text/javascript">
38857  */
38858  
38859 /**
38860  * @class Roo.bootstrap.layout.Basic
38861  * @extends Roo.util.Observable
38862  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38863  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38864  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38865  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38866  * @cfg {string}   region  the region that it inhabits..
38867  * @cfg {bool}   skipConfig skip config?
38868  * 
38869
38870  */
38871 Roo.bootstrap.layout.Basic = function(config){
38872     
38873     this.mgr = config.mgr;
38874     
38875     this.position = config.region;
38876     
38877     var skipConfig = config.skipConfig;
38878     
38879     this.events = {
38880         /**
38881          * @scope Roo.BasicLayoutRegion
38882          */
38883         
38884         /**
38885          * @event beforeremove
38886          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38887          * @param {Roo.LayoutRegion} this
38888          * @param {Roo.ContentPanel} panel The panel
38889          * @param {Object} e The cancel event object
38890          */
38891         "beforeremove" : true,
38892         /**
38893          * @event invalidated
38894          * Fires when the layout for this region is changed.
38895          * @param {Roo.LayoutRegion} this
38896          */
38897         "invalidated" : true,
38898         /**
38899          * @event visibilitychange
38900          * Fires when this region is shown or hidden 
38901          * @param {Roo.LayoutRegion} this
38902          * @param {Boolean} visibility true or false
38903          */
38904         "visibilitychange" : true,
38905         /**
38906          * @event paneladded
38907          * Fires when a panel is added. 
38908          * @param {Roo.LayoutRegion} this
38909          * @param {Roo.ContentPanel} panel The panel
38910          */
38911         "paneladded" : true,
38912         /**
38913          * @event panelremoved
38914          * Fires when a panel is removed. 
38915          * @param {Roo.LayoutRegion} this
38916          * @param {Roo.ContentPanel} panel The panel
38917          */
38918         "panelremoved" : true,
38919         /**
38920          * @event beforecollapse
38921          * Fires when this region before collapse.
38922          * @param {Roo.LayoutRegion} this
38923          */
38924         "beforecollapse" : true,
38925         /**
38926          * @event collapsed
38927          * Fires when this region is collapsed.
38928          * @param {Roo.LayoutRegion} this
38929          */
38930         "collapsed" : true,
38931         /**
38932          * @event expanded
38933          * Fires when this region is expanded.
38934          * @param {Roo.LayoutRegion} this
38935          */
38936         "expanded" : true,
38937         /**
38938          * @event slideshow
38939          * Fires when this region is slid into view.
38940          * @param {Roo.LayoutRegion} this
38941          */
38942         "slideshow" : true,
38943         /**
38944          * @event slidehide
38945          * Fires when this region slides out of view. 
38946          * @param {Roo.LayoutRegion} this
38947          */
38948         "slidehide" : true,
38949         /**
38950          * @event panelactivated
38951          * Fires when a panel is activated. 
38952          * @param {Roo.LayoutRegion} this
38953          * @param {Roo.ContentPanel} panel The activated panel
38954          */
38955         "panelactivated" : true,
38956         /**
38957          * @event resized
38958          * Fires when the user resizes this region. 
38959          * @param {Roo.LayoutRegion} this
38960          * @param {Number} newSize The new size (width for east/west, height for north/south)
38961          */
38962         "resized" : true
38963     };
38964     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38965     this.panels = new Roo.util.MixedCollection();
38966     this.panels.getKey = this.getPanelId.createDelegate(this);
38967     this.box = null;
38968     this.activePanel = null;
38969     // ensure listeners are added...
38970     
38971     if (config.listeners || config.events) {
38972         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38973             listeners : config.listeners || {},
38974             events : config.events || {}
38975         });
38976     }
38977     
38978     if(skipConfig !== true){
38979         this.applyConfig(config);
38980     }
38981 };
38982
38983 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38984 {
38985     getPanelId : function(p){
38986         return p.getId();
38987     },
38988     
38989     applyConfig : function(config){
38990         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38991         this.config = config;
38992         
38993     },
38994     
38995     /**
38996      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38997      * the width, for horizontal (north, south) the height.
38998      * @param {Number} newSize The new width or height
38999      */
39000     resizeTo : function(newSize){
39001         var el = this.el ? this.el :
39002                  (this.activePanel ? this.activePanel.getEl() : null);
39003         if(el){
39004             switch(this.position){
39005                 case "east":
39006                 case "west":
39007                     el.setWidth(newSize);
39008                     this.fireEvent("resized", this, newSize);
39009                 break;
39010                 case "north":
39011                 case "south":
39012                     el.setHeight(newSize);
39013                     this.fireEvent("resized", this, newSize);
39014                 break;                
39015             }
39016         }
39017     },
39018     
39019     getBox : function(){
39020         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39021     },
39022     
39023     getMargins : function(){
39024         return this.margins;
39025     },
39026     
39027     updateBox : function(box){
39028         this.box = box;
39029         var el = this.activePanel.getEl();
39030         el.dom.style.left = box.x + "px";
39031         el.dom.style.top = box.y + "px";
39032         this.activePanel.setSize(box.width, box.height);
39033     },
39034     
39035     /**
39036      * Returns the container element for this region.
39037      * @return {Roo.Element}
39038      */
39039     getEl : function(){
39040         return this.activePanel;
39041     },
39042     
39043     /**
39044      * Returns true if this region is currently visible.
39045      * @return {Boolean}
39046      */
39047     isVisible : function(){
39048         return this.activePanel ? true : false;
39049     },
39050     
39051     setActivePanel : function(panel){
39052         panel = this.getPanel(panel);
39053         if(this.activePanel && this.activePanel != panel){
39054             this.activePanel.setActiveState(false);
39055             this.activePanel.getEl().setLeftTop(-10000,-10000);
39056         }
39057         this.activePanel = panel;
39058         panel.setActiveState(true);
39059         if(this.box){
39060             panel.setSize(this.box.width, this.box.height);
39061         }
39062         this.fireEvent("panelactivated", this, panel);
39063         this.fireEvent("invalidated");
39064     },
39065     
39066     /**
39067      * Show the specified panel.
39068      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39069      * @return {Roo.ContentPanel} The shown panel or null
39070      */
39071     showPanel : function(panel){
39072         panel = this.getPanel(panel);
39073         if(panel){
39074             this.setActivePanel(panel);
39075         }
39076         return panel;
39077     },
39078     
39079     /**
39080      * Get the active panel for this region.
39081      * @return {Roo.ContentPanel} The active panel or null
39082      */
39083     getActivePanel : function(){
39084         return this.activePanel;
39085     },
39086     
39087     /**
39088      * Add the passed ContentPanel(s)
39089      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39090      * @return {Roo.ContentPanel} The panel added (if only one was added)
39091      */
39092     add : function(panel){
39093         if(arguments.length > 1){
39094             for(var i = 0, len = arguments.length; i < len; i++) {
39095                 this.add(arguments[i]);
39096             }
39097             return null;
39098         }
39099         if(this.hasPanel(panel)){
39100             this.showPanel(panel);
39101             return panel;
39102         }
39103         var el = panel.getEl();
39104         if(el.dom.parentNode != this.mgr.el.dom){
39105             this.mgr.el.dom.appendChild(el.dom);
39106         }
39107         if(panel.setRegion){
39108             panel.setRegion(this);
39109         }
39110         this.panels.add(panel);
39111         el.setStyle("position", "absolute");
39112         if(!panel.background){
39113             this.setActivePanel(panel);
39114             if(this.config.initialSize && this.panels.getCount()==1){
39115                 this.resizeTo(this.config.initialSize);
39116             }
39117         }
39118         this.fireEvent("paneladded", this, panel);
39119         return panel;
39120     },
39121     
39122     /**
39123      * Returns true if the panel is in this region.
39124      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39125      * @return {Boolean}
39126      */
39127     hasPanel : function(panel){
39128         if(typeof panel == "object"){ // must be panel obj
39129             panel = panel.getId();
39130         }
39131         return this.getPanel(panel) ? true : false;
39132     },
39133     
39134     /**
39135      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39137      * @param {Boolean} preservePanel Overrides the config preservePanel option
39138      * @return {Roo.ContentPanel} The panel that was removed
39139      */
39140     remove : function(panel, preservePanel){
39141         panel = this.getPanel(panel);
39142         if(!panel){
39143             return null;
39144         }
39145         var e = {};
39146         this.fireEvent("beforeremove", this, panel, e);
39147         if(e.cancel === true){
39148             return null;
39149         }
39150         var panelId = panel.getId();
39151         this.panels.removeKey(panelId);
39152         return panel;
39153     },
39154     
39155     /**
39156      * Returns the panel specified or null if it's not in this region.
39157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39158      * @return {Roo.ContentPanel}
39159      */
39160     getPanel : function(id){
39161         if(typeof id == "object"){ // must be panel obj
39162             return id;
39163         }
39164         return this.panels.get(id);
39165     },
39166     
39167     /**
39168      * Returns this regions position (north/south/east/west/center).
39169      * @return {String} 
39170      */
39171     getPosition: function(){
39172         return this.position;    
39173     }
39174 });/*
39175  * Based on:
39176  * Ext JS Library 1.1.1
39177  * Copyright(c) 2006-2007, Ext JS, LLC.
39178  *
39179  * Originally Released Under LGPL - original licence link has changed is not relivant.
39180  *
39181  * Fork - LGPL
39182  * <script type="text/javascript">
39183  */
39184  
39185 /**
39186  * @class Roo.bootstrap.layout.Region
39187  * @extends Roo.bootstrap.layout.Basic
39188  * This class represents a region in a layout manager.
39189  
39190  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39191  * @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})
39192  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39193  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39194  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39195  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39196  * @cfg {String}    title           The title for the region (overrides panel titles)
39197  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39198  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39199  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39200  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39201  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39202  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39203  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39204  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39205  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39206  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39207
39208  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39209  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39210  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39211  * @cfg {Number}    width           For East/West panels
39212  * @cfg {Number}    height          For North/South panels
39213  * @cfg {Boolean}   split           To show the splitter
39214  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39215  * 
39216  * @cfg {string}   cls             Extra CSS classes to add to region
39217  * 
39218  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39219  * @cfg {string}   region  the region that it inhabits..
39220  *
39221
39222  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39223  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39224
39225  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39226  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39227  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39228  */
39229 Roo.bootstrap.layout.Region = function(config)
39230 {
39231     this.applyConfig(config);
39232
39233     var mgr = config.mgr;
39234     var pos = config.region;
39235     config.skipConfig = true;
39236     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39237     
39238     if (mgr.el) {
39239         this.onRender(mgr.el);   
39240     }
39241      
39242     this.visible = true;
39243     this.collapsed = false;
39244     this.unrendered_panels = [];
39245 };
39246
39247 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39248
39249     position: '', // set by wrapper (eg. north/south etc..)
39250     unrendered_panels : null,  // unrendered panels.
39251     
39252     tabPosition : false,
39253     
39254     mgr: false, // points to 'Border'
39255     
39256     
39257     createBody : function(){
39258         /** This region's body element 
39259         * @type Roo.Element */
39260         this.bodyEl = this.el.createChild({
39261                 tag: "div",
39262                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39263         });
39264     },
39265
39266     onRender: function(ctr, pos)
39267     {
39268         var dh = Roo.DomHelper;
39269         /** This region's container element 
39270         * @type Roo.Element */
39271         this.el = dh.append(ctr.dom, {
39272                 tag: "div",
39273                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39274             }, true);
39275         /** This region's title element 
39276         * @type Roo.Element */
39277     
39278         this.titleEl = dh.append(this.el.dom,  {
39279                 tag: "div",
39280                 unselectable: "on",
39281                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39282                 children:[
39283                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39284                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39285                 ]
39286             }, true);
39287         
39288         this.titleEl.enableDisplayMode();
39289         /** This region's title text element 
39290         * @type HTMLElement */
39291         this.titleTextEl = this.titleEl.dom.firstChild;
39292         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39293         /*
39294         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39295         this.closeBtn.enableDisplayMode();
39296         this.closeBtn.on("click", this.closeClicked, this);
39297         this.closeBtn.hide();
39298     */
39299         this.createBody(this.config);
39300         if(this.config.hideWhenEmpty){
39301             this.hide();
39302             this.on("paneladded", this.validateVisibility, this);
39303             this.on("panelremoved", this.validateVisibility, this);
39304         }
39305         if(this.autoScroll){
39306             this.bodyEl.setStyle("overflow", "auto");
39307         }else{
39308             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39309         }
39310         //if(c.titlebar !== false){
39311             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39312                 this.titleEl.hide();
39313             }else{
39314                 this.titleEl.show();
39315                 if(this.config.title){
39316                     this.titleTextEl.innerHTML = this.config.title;
39317                 }
39318             }
39319         //}
39320         if(this.config.collapsed){
39321             this.collapse(true);
39322         }
39323         if(this.config.hidden){
39324             this.hide();
39325         }
39326         
39327         if (this.unrendered_panels && this.unrendered_panels.length) {
39328             for (var i =0;i< this.unrendered_panels.length; i++) {
39329                 this.add(this.unrendered_panels[i]);
39330             }
39331             this.unrendered_panels = null;
39332             
39333         }
39334         
39335     },
39336     
39337     applyConfig : function(c)
39338     {
39339         /*
39340          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39341             var dh = Roo.DomHelper;
39342             if(c.titlebar !== false){
39343                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39344                 this.collapseBtn.on("click", this.collapse, this);
39345                 this.collapseBtn.enableDisplayMode();
39346                 /*
39347                 if(c.showPin === true || this.showPin){
39348                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39349                     this.stickBtn.enableDisplayMode();
39350                     this.stickBtn.on("click", this.expand, this);
39351                     this.stickBtn.hide();
39352                 }
39353                 
39354             }
39355             */
39356             /** This region's collapsed element
39357             * @type Roo.Element */
39358             /*
39359              *
39360             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39361                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39362             ]}, true);
39363             
39364             if(c.floatable !== false){
39365                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39366                this.collapsedEl.on("click", this.collapseClick, this);
39367             }
39368
39369             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39370                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39371                    id: "message", unselectable: "on", style:{"float":"left"}});
39372                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39373              }
39374             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39375             this.expandBtn.on("click", this.expand, this);
39376             
39377         }
39378         
39379         if(this.collapseBtn){
39380             this.collapseBtn.setVisible(c.collapsible == true);
39381         }
39382         
39383         this.cmargins = c.cmargins || this.cmargins ||
39384                          (this.position == "west" || this.position == "east" ?
39385                              {top: 0, left: 2, right:2, bottom: 0} :
39386                              {top: 2, left: 0, right:0, bottom: 2});
39387         */
39388         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39389         
39390         
39391         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39392         
39393         this.autoScroll = c.autoScroll || false;
39394         
39395         
39396        
39397         
39398         this.duration = c.duration || .30;
39399         this.slideDuration = c.slideDuration || .45;
39400         this.config = c;
39401        
39402     },
39403     /**
39404      * Returns true if this region is currently visible.
39405      * @return {Boolean}
39406      */
39407     isVisible : function(){
39408         return this.visible;
39409     },
39410
39411     /**
39412      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39413      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39414      */
39415     //setCollapsedTitle : function(title){
39416     //    title = title || "&#160;";
39417      //   if(this.collapsedTitleTextEl){
39418       //      this.collapsedTitleTextEl.innerHTML = title;
39419        // }
39420     //},
39421
39422     getBox : function(){
39423         var b;
39424       //  if(!this.collapsed){
39425             b = this.el.getBox(false, true);
39426        // }else{
39427           //  b = this.collapsedEl.getBox(false, true);
39428         //}
39429         return b;
39430     },
39431
39432     getMargins : function(){
39433         return this.margins;
39434         //return this.collapsed ? this.cmargins : this.margins;
39435     },
39436 /*
39437     highlight : function(){
39438         this.el.addClass("x-layout-panel-dragover");
39439     },
39440
39441     unhighlight : function(){
39442         this.el.removeClass("x-layout-panel-dragover");
39443     },
39444 */
39445     updateBox : function(box)
39446     {
39447         if (!this.bodyEl) {
39448             return; // not rendered yet..
39449         }
39450         
39451         this.box = box;
39452         if(!this.collapsed){
39453             this.el.dom.style.left = box.x + "px";
39454             this.el.dom.style.top = box.y + "px";
39455             this.updateBody(box.width, box.height);
39456         }else{
39457             this.collapsedEl.dom.style.left = box.x + "px";
39458             this.collapsedEl.dom.style.top = box.y + "px";
39459             this.collapsedEl.setSize(box.width, box.height);
39460         }
39461         if(this.tabs){
39462             this.tabs.autoSizeTabs();
39463         }
39464     },
39465
39466     updateBody : function(w, h)
39467     {
39468         if(w !== null){
39469             this.el.setWidth(w);
39470             w -= this.el.getBorderWidth("rl");
39471             if(this.config.adjustments){
39472                 w += this.config.adjustments[0];
39473             }
39474         }
39475         if(h !== null && h > 0){
39476             this.el.setHeight(h);
39477             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39478             h -= this.el.getBorderWidth("tb");
39479             if(this.config.adjustments){
39480                 h += this.config.adjustments[1];
39481             }
39482             this.bodyEl.setHeight(h);
39483             if(this.tabs){
39484                 h = this.tabs.syncHeight(h);
39485             }
39486         }
39487         if(this.panelSize){
39488             w = w !== null ? w : this.panelSize.width;
39489             h = h !== null ? h : this.panelSize.height;
39490         }
39491         if(this.activePanel){
39492             var el = this.activePanel.getEl();
39493             w = w !== null ? w : el.getWidth();
39494             h = h !== null ? h : el.getHeight();
39495             this.panelSize = {width: w, height: h};
39496             this.activePanel.setSize(w, h);
39497         }
39498         if(Roo.isIE && this.tabs){
39499             this.tabs.el.repaint();
39500         }
39501     },
39502
39503     /**
39504      * Returns the container element for this region.
39505      * @return {Roo.Element}
39506      */
39507     getEl : function(){
39508         return this.el;
39509     },
39510
39511     /**
39512      * Hides this region.
39513      */
39514     hide : function(){
39515         //if(!this.collapsed){
39516             this.el.dom.style.left = "-2000px";
39517             this.el.hide();
39518         //}else{
39519          //   this.collapsedEl.dom.style.left = "-2000px";
39520          //   this.collapsedEl.hide();
39521        // }
39522         this.visible = false;
39523         this.fireEvent("visibilitychange", this, false);
39524     },
39525
39526     /**
39527      * Shows this region if it was previously hidden.
39528      */
39529     show : function(){
39530         //if(!this.collapsed){
39531             this.el.show();
39532         //}else{
39533         //    this.collapsedEl.show();
39534        // }
39535         this.visible = true;
39536         this.fireEvent("visibilitychange", this, true);
39537     },
39538 /*
39539     closeClicked : function(){
39540         if(this.activePanel){
39541             this.remove(this.activePanel);
39542         }
39543     },
39544
39545     collapseClick : function(e){
39546         if(this.isSlid){
39547            e.stopPropagation();
39548            this.slideIn();
39549         }else{
39550            e.stopPropagation();
39551            this.slideOut();
39552         }
39553     },
39554 */
39555     /**
39556      * Collapses this region.
39557      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39558      */
39559     /*
39560     collapse : function(skipAnim, skipCheck = false){
39561         if(this.collapsed) {
39562             return;
39563         }
39564         
39565         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39566             
39567             this.collapsed = true;
39568             if(this.split){
39569                 this.split.el.hide();
39570             }
39571             if(this.config.animate && skipAnim !== true){
39572                 this.fireEvent("invalidated", this);
39573                 this.animateCollapse();
39574             }else{
39575                 this.el.setLocation(-20000,-20000);
39576                 this.el.hide();
39577                 this.collapsedEl.show();
39578                 this.fireEvent("collapsed", this);
39579                 this.fireEvent("invalidated", this);
39580             }
39581         }
39582         
39583     },
39584 */
39585     animateCollapse : function(){
39586         // overridden
39587     },
39588
39589     /**
39590      * Expands this region if it was previously collapsed.
39591      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39592      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39593      */
39594     /*
39595     expand : function(e, skipAnim){
39596         if(e) {
39597             e.stopPropagation();
39598         }
39599         if(!this.collapsed || this.el.hasActiveFx()) {
39600             return;
39601         }
39602         if(this.isSlid){
39603             this.afterSlideIn();
39604             skipAnim = true;
39605         }
39606         this.collapsed = false;
39607         if(this.config.animate && skipAnim !== true){
39608             this.animateExpand();
39609         }else{
39610             this.el.show();
39611             if(this.split){
39612                 this.split.el.show();
39613             }
39614             this.collapsedEl.setLocation(-2000,-2000);
39615             this.collapsedEl.hide();
39616             this.fireEvent("invalidated", this);
39617             this.fireEvent("expanded", this);
39618         }
39619     },
39620 */
39621     animateExpand : function(){
39622         // overridden
39623     },
39624
39625     initTabs : function()
39626     {
39627         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39628         
39629         var ts = new Roo.bootstrap.panel.Tabs({
39630             el: this.bodyEl.dom,
39631             region : this,
39632             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39633             disableTooltips: this.config.disableTabTips,
39634             toolbar : this.config.toolbar
39635         });
39636         
39637         if(this.config.hideTabs){
39638             ts.stripWrap.setDisplayed(false);
39639         }
39640         this.tabs = ts;
39641         ts.resizeTabs = this.config.resizeTabs === true;
39642         ts.minTabWidth = this.config.minTabWidth || 40;
39643         ts.maxTabWidth = this.config.maxTabWidth || 250;
39644         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39645         ts.monitorResize = false;
39646         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39647         ts.bodyEl.addClass('roo-layout-tabs-body');
39648         this.panels.each(this.initPanelAsTab, this);
39649     },
39650
39651     initPanelAsTab : function(panel){
39652         var ti = this.tabs.addTab(
39653             panel.getEl().id,
39654             panel.getTitle(),
39655             null,
39656             this.config.closeOnTab && panel.isClosable(),
39657             panel.tpl
39658         );
39659         if(panel.tabTip !== undefined){
39660             ti.setTooltip(panel.tabTip);
39661         }
39662         ti.on("activate", function(){
39663               this.setActivePanel(panel);
39664         }, this);
39665         
39666         if(this.config.closeOnTab){
39667             ti.on("beforeclose", function(t, e){
39668                 e.cancel = true;
39669                 this.remove(panel);
39670             }, this);
39671         }
39672         
39673         panel.tabItem = ti;
39674         
39675         return ti;
39676     },
39677
39678     updatePanelTitle : function(panel, title)
39679     {
39680         if(this.activePanel == panel){
39681             this.updateTitle(title);
39682         }
39683         if(this.tabs){
39684             var ti = this.tabs.getTab(panel.getEl().id);
39685             ti.setText(title);
39686             if(panel.tabTip !== undefined){
39687                 ti.setTooltip(panel.tabTip);
39688             }
39689         }
39690     },
39691
39692     updateTitle : function(title){
39693         if(this.titleTextEl && !this.config.title){
39694             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39695         }
39696     },
39697
39698     setActivePanel : function(panel)
39699     {
39700         panel = this.getPanel(panel);
39701         if(this.activePanel && this.activePanel != panel){
39702             if(this.activePanel.setActiveState(false) === false){
39703                 return;
39704             }
39705         }
39706         this.activePanel = panel;
39707         panel.setActiveState(true);
39708         if(this.panelSize){
39709             panel.setSize(this.panelSize.width, this.panelSize.height);
39710         }
39711         if(this.closeBtn){
39712             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39713         }
39714         this.updateTitle(panel.getTitle());
39715         if(this.tabs){
39716             this.fireEvent("invalidated", this);
39717         }
39718         this.fireEvent("panelactivated", this, panel);
39719     },
39720
39721     /**
39722      * Shows the specified panel.
39723      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39724      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39725      */
39726     showPanel : function(panel)
39727     {
39728         panel = this.getPanel(panel);
39729         if(panel){
39730             if(this.tabs){
39731                 var tab = this.tabs.getTab(panel.getEl().id);
39732                 if(tab.isHidden()){
39733                     this.tabs.unhideTab(tab.id);
39734                 }
39735                 tab.activate();
39736             }else{
39737                 this.setActivePanel(panel);
39738             }
39739         }
39740         return panel;
39741     },
39742
39743     /**
39744      * Get the active panel for this region.
39745      * @return {Roo.ContentPanel} The active panel or null
39746      */
39747     getActivePanel : function(){
39748         return this.activePanel;
39749     },
39750
39751     validateVisibility : function(){
39752         if(this.panels.getCount() < 1){
39753             this.updateTitle("&#160;");
39754             this.closeBtn.hide();
39755             this.hide();
39756         }else{
39757             if(!this.isVisible()){
39758                 this.show();
39759             }
39760         }
39761     },
39762
39763     /**
39764      * Adds the passed ContentPanel(s) to this region.
39765      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39766      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39767      */
39768     add : function(panel)
39769     {
39770         if(arguments.length > 1){
39771             for(var i = 0, len = arguments.length; i < len; i++) {
39772                 this.add(arguments[i]);
39773             }
39774             return null;
39775         }
39776         
39777         // if we have not been rendered yet, then we can not really do much of this..
39778         if (!this.bodyEl) {
39779             this.unrendered_panels.push(panel);
39780             return panel;
39781         }
39782         
39783         
39784         
39785         
39786         if(this.hasPanel(panel)){
39787             this.showPanel(panel);
39788             return panel;
39789         }
39790         panel.setRegion(this);
39791         this.panels.add(panel);
39792        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39793             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39794             // and hide them... ???
39795             this.bodyEl.dom.appendChild(panel.getEl().dom);
39796             if(panel.background !== true){
39797                 this.setActivePanel(panel);
39798             }
39799             this.fireEvent("paneladded", this, panel);
39800             return panel;
39801         }
39802         */
39803         if(!this.tabs){
39804             this.initTabs();
39805         }else{
39806             this.initPanelAsTab(panel);
39807         }
39808         
39809         
39810         if(panel.background !== true){
39811             this.tabs.activate(panel.getEl().id);
39812         }
39813         this.fireEvent("paneladded", this, panel);
39814         return panel;
39815     },
39816
39817     /**
39818      * Hides the tab for the specified panel.
39819      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39820      */
39821     hidePanel : function(panel){
39822         if(this.tabs && (panel = this.getPanel(panel))){
39823             this.tabs.hideTab(panel.getEl().id);
39824         }
39825     },
39826
39827     /**
39828      * Unhides the tab for a previously hidden panel.
39829      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39830      */
39831     unhidePanel : function(panel){
39832         if(this.tabs && (panel = this.getPanel(panel))){
39833             this.tabs.unhideTab(panel.getEl().id);
39834         }
39835     },
39836
39837     clearPanels : function(){
39838         while(this.panels.getCount() > 0){
39839              this.remove(this.panels.first());
39840         }
39841     },
39842
39843     /**
39844      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39845      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39846      * @param {Boolean} preservePanel Overrides the config preservePanel option
39847      * @return {Roo.ContentPanel} The panel that was removed
39848      */
39849     remove : function(panel, preservePanel)
39850     {
39851         panel = this.getPanel(panel);
39852         if(!panel){
39853             return null;
39854         }
39855         var e = {};
39856         this.fireEvent("beforeremove", this, panel, e);
39857         if(e.cancel === true){
39858             return null;
39859         }
39860         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39861         var panelId = panel.getId();
39862         this.panels.removeKey(panelId);
39863         if(preservePanel){
39864             document.body.appendChild(panel.getEl().dom);
39865         }
39866         if(this.tabs){
39867             this.tabs.removeTab(panel.getEl().id);
39868         }else if (!preservePanel){
39869             this.bodyEl.dom.removeChild(panel.getEl().dom);
39870         }
39871         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39872             var p = this.panels.first();
39873             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39874             tempEl.appendChild(p.getEl().dom);
39875             this.bodyEl.update("");
39876             this.bodyEl.dom.appendChild(p.getEl().dom);
39877             tempEl = null;
39878             this.updateTitle(p.getTitle());
39879             this.tabs = null;
39880             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39881             this.setActivePanel(p);
39882         }
39883         panel.setRegion(null);
39884         if(this.activePanel == panel){
39885             this.activePanel = null;
39886         }
39887         if(this.config.autoDestroy !== false && preservePanel !== true){
39888             try{panel.destroy();}catch(e){}
39889         }
39890         this.fireEvent("panelremoved", this, panel);
39891         return panel;
39892     },
39893
39894     /**
39895      * Returns the TabPanel component used by this region
39896      * @return {Roo.TabPanel}
39897      */
39898     getTabs : function(){
39899         return this.tabs;
39900     },
39901
39902     createTool : function(parentEl, className){
39903         var btn = Roo.DomHelper.append(parentEl, {
39904             tag: "div",
39905             cls: "x-layout-tools-button",
39906             children: [ {
39907                 tag: "div",
39908                 cls: "roo-layout-tools-button-inner " + className,
39909                 html: "&#160;"
39910             }]
39911         }, true);
39912         btn.addClassOnOver("roo-layout-tools-button-over");
39913         return btn;
39914     }
39915 });/*
39916  * Based on:
39917  * Ext JS Library 1.1.1
39918  * Copyright(c) 2006-2007, Ext JS, LLC.
39919  *
39920  * Originally Released Under LGPL - original licence link has changed is not relivant.
39921  *
39922  * Fork - LGPL
39923  * <script type="text/javascript">
39924  */
39925  
39926
39927
39928 /**
39929  * @class Roo.SplitLayoutRegion
39930  * @extends Roo.LayoutRegion
39931  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39932  */
39933 Roo.bootstrap.layout.Split = function(config){
39934     this.cursor = config.cursor;
39935     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39936 };
39937
39938 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39939 {
39940     splitTip : "Drag to resize.",
39941     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39942     useSplitTips : false,
39943
39944     applyConfig : function(config){
39945         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39946     },
39947     
39948     onRender : function(ctr,pos) {
39949         
39950         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39951         if(!this.config.split){
39952             return;
39953         }
39954         if(!this.split){
39955             
39956             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39957                             tag: "div",
39958                             id: this.el.id + "-split",
39959                             cls: "roo-layout-split roo-layout-split-"+this.position,
39960                             html: "&#160;"
39961             });
39962             /** The SplitBar for this region 
39963             * @type Roo.SplitBar */
39964             // does not exist yet...
39965             Roo.log([this.position, this.orientation]);
39966             
39967             this.split = new Roo.bootstrap.SplitBar({
39968                 dragElement : splitEl,
39969                 resizingElement: this.el,
39970                 orientation : this.orientation
39971             });
39972             
39973             this.split.on("moved", this.onSplitMove, this);
39974             this.split.useShim = this.config.useShim === true;
39975             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39976             if(this.useSplitTips){
39977                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39978             }
39979             //if(config.collapsible){
39980             //    this.split.el.on("dblclick", this.collapse,  this);
39981             //}
39982         }
39983         if(typeof this.config.minSize != "undefined"){
39984             this.split.minSize = this.config.minSize;
39985         }
39986         if(typeof this.config.maxSize != "undefined"){
39987             this.split.maxSize = this.config.maxSize;
39988         }
39989         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39990             this.hideSplitter();
39991         }
39992         
39993     },
39994
39995     getHMaxSize : function(){
39996          var cmax = this.config.maxSize || 10000;
39997          var center = this.mgr.getRegion("center");
39998          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39999     },
40000
40001     getVMaxSize : function(){
40002          var cmax = this.config.maxSize || 10000;
40003          var center = this.mgr.getRegion("center");
40004          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40005     },
40006
40007     onSplitMove : function(split, newSize){
40008         this.fireEvent("resized", this, newSize);
40009     },
40010     
40011     /** 
40012      * Returns the {@link Roo.SplitBar} for this region.
40013      * @return {Roo.SplitBar}
40014      */
40015     getSplitBar : function(){
40016         return this.split;
40017     },
40018     
40019     hide : function(){
40020         this.hideSplitter();
40021         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40022     },
40023
40024     hideSplitter : function(){
40025         if(this.split){
40026             this.split.el.setLocation(-2000,-2000);
40027             this.split.el.hide();
40028         }
40029     },
40030
40031     show : function(){
40032         if(this.split){
40033             this.split.el.show();
40034         }
40035         Roo.bootstrap.layout.Split.superclass.show.call(this);
40036     },
40037     
40038     beforeSlide: function(){
40039         if(Roo.isGecko){// firefox overflow auto bug workaround
40040             this.bodyEl.clip();
40041             if(this.tabs) {
40042                 this.tabs.bodyEl.clip();
40043             }
40044             if(this.activePanel){
40045                 this.activePanel.getEl().clip();
40046                 
40047                 if(this.activePanel.beforeSlide){
40048                     this.activePanel.beforeSlide();
40049                 }
40050             }
40051         }
40052     },
40053     
40054     afterSlide : function(){
40055         if(Roo.isGecko){// firefox overflow auto bug workaround
40056             this.bodyEl.unclip();
40057             if(this.tabs) {
40058                 this.tabs.bodyEl.unclip();
40059             }
40060             if(this.activePanel){
40061                 this.activePanel.getEl().unclip();
40062                 if(this.activePanel.afterSlide){
40063                     this.activePanel.afterSlide();
40064                 }
40065             }
40066         }
40067     },
40068
40069     initAutoHide : function(){
40070         if(this.autoHide !== false){
40071             if(!this.autoHideHd){
40072                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40073                 this.autoHideHd = {
40074                     "mouseout": function(e){
40075                         if(!e.within(this.el, true)){
40076                             st.delay(500);
40077                         }
40078                     },
40079                     "mouseover" : function(e){
40080                         st.cancel();
40081                     },
40082                     scope : this
40083                 };
40084             }
40085             this.el.on(this.autoHideHd);
40086         }
40087     },
40088
40089     clearAutoHide : function(){
40090         if(this.autoHide !== false){
40091             this.el.un("mouseout", this.autoHideHd.mouseout);
40092             this.el.un("mouseover", this.autoHideHd.mouseover);
40093         }
40094     },
40095
40096     clearMonitor : function(){
40097         Roo.get(document).un("click", this.slideInIf, this);
40098     },
40099
40100     // these names are backwards but not changed for compat
40101     slideOut : function(){
40102         if(this.isSlid || this.el.hasActiveFx()){
40103             return;
40104         }
40105         this.isSlid = true;
40106         if(this.collapseBtn){
40107             this.collapseBtn.hide();
40108         }
40109         this.closeBtnState = this.closeBtn.getStyle('display');
40110         this.closeBtn.hide();
40111         if(this.stickBtn){
40112             this.stickBtn.show();
40113         }
40114         this.el.show();
40115         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40116         this.beforeSlide();
40117         this.el.setStyle("z-index", 10001);
40118         this.el.slideIn(this.getSlideAnchor(), {
40119             callback: function(){
40120                 this.afterSlide();
40121                 this.initAutoHide();
40122                 Roo.get(document).on("click", this.slideInIf, this);
40123                 this.fireEvent("slideshow", this);
40124             },
40125             scope: this,
40126             block: true
40127         });
40128     },
40129
40130     afterSlideIn : function(){
40131         this.clearAutoHide();
40132         this.isSlid = false;
40133         this.clearMonitor();
40134         this.el.setStyle("z-index", "");
40135         if(this.collapseBtn){
40136             this.collapseBtn.show();
40137         }
40138         this.closeBtn.setStyle('display', this.closeBtnState);
40139         if(this.stickBtn){
40140             this.stickBtn.hide();
40141         }
40142         this.fireEvent("slidehide", this);
40143     },
40144
40145     slideIn : function(cb){
40146         if(!this.isSlid || this.el.hasActiveFx()){
40147             Roo.callback(cb);
40148             return;
40149         }
40150         this.isSlid = false;
40151         this.beforeSlide();
40152         this.el.slideOut(this.getSlideAnchor(), {
40153             callback: function(){
40154                 this.el.setLeftTop(-10000, -10000);
40155                 this.afterSlide();
40156                 this.afterSlideIn();
40157                 Roo.callback(cb);
40158             },
40159             scope: this,
40160             block: true
40161         });
40162     },
40163     
40164     slideInIf : function(e){
40165         if(!e.within(this.el)){
40166             this.slideIn();
40167         }
40168     },
40169
40170     animateCollapse : function(){
40171         this.beforeSlide();
40172         this.el.setStyle("z-index", 20000);
40173         var anchor = this.getSlideAnchor();
40174         this.el.slideOut(anchor, {
40175             callback : function(){
40176                 this.el.setStyle("z-index", "");
40177                 this.collapsedEl.slideIn(anchor, {duration:.3});
40178                 this.afterSlide();
40179                 this.el.setLocation(-10000,-10000);
40180                 this.el.hide();
40181                 this.fireEvent("collapsed", this);
40182             },
40183             scope: this,
40184             block: true
40185         });
40186     },
40187
40188     animateExpand : function(){
40189         this.beforeSlide();
40190         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40191         this.el.setStyle("z-index", 20000);
40192         this.collapsedEl.hide({
40193             duration:.1
40194         });
40195         this.el.slideIn(this.getSlideAnchor(), {
40196             callback : function(){
40197                 this.el.setStyle("z-index", "");
40198                 this.afterSlide();
40199                 if(this.split){
40200                     this.split.el.show();
40201                 }
40202                 this.fireEvent("invalidated", this);
40203                 this.fireEvent("expanded", this);
40204             },
40205             scope: this,
40206             block: true
40207         });
40208     },
40209
40210     anchors : {
40211         "west" : "left",
40212         "east" : "right",
40213         "north" : "top",
40214         "south" : "bottom"
40215     },
40216
40217     sanchors : {
40218         "west" : "l",
40219         "east" : "r",
40220         "north" : "t",
40221         "south" : "b"
40222     },
40223
40224     canchors : {
40225         "west" : "tl-tr",
40226         "east" : "tr-tl",
40227         "north" : "tl-bl",
40228         "south" : "bl-tl"
40229     },
40230
40231     getAnchor : function(){
40232         return this.anchors[this.position];
40233     },
40234
40235     getCollapseAnchor : function(){
40236         return this.canchors[this.position];
40237     },
40238
40239     getSlideAnchor : function(){
40240         return this.sanchors[this.position];
40241     },
40242
40243     getAlignAdj : function(){
40244         var cm = this.cmargins;
40245         switch(this.position){
40246             case "west":
40247                 return [0, 0];
40248             break;
40249             case "east":
40250                 return [0, 0];
40251             break;
40252             case "north":
40253                 return [0, 0];
40254             break;
40255             case "south":
40256                 return [0, 0];
40257             break;
40258         }
40259     },
40260
40261     getExpandAdj : function(){
40262         var c = this.collapsedEl, cm = this.cmargins;
40263         switch(this.position){
40264             case "west":
40265                 return [-(cm.right+c.getWidth()+cm.left), 0];
40266             break;
40267             case "east":
40268                 return [cm.right+c.getWidth()+cm.left, 0];
40269             break;
40270             case "north":
40271                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40272             break;
40273             case "south":
40274                 return [0, cm.top+cm.bottom+c.getHeight()];
40275             break;
40276         }
40277     }
40278 });/*
40279  * Based on:
40280  * Ext JS Library 1.1.1
40281  * Copyright(c) 2006-2007, Ext JS, LLC.
40282  *
40283  * Originally Released Under LGPL - original licence link has changed is not relivant.
40284  *
40285  * Fork - LGPL
40286  * <script type="text/javascript">
40287  */
40288 /*
40289  * These classes are private internal classes
40290  */
40291 Roo.bootstrap.layout.Center = function(config){
40292     config.region = "center";
40293     Roo.bootstrap.layout.Region.call(this, config);
40294     this.visible = true;
40295     this.minWidth = config.minWidth || 20;
40296     this.minHeight = config.minHeight || 20;
40297 };
40298
40299 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40300     hide : function(){
40301         // center panel can't be hidden
40302     },
40303     
40304     show : function(){
40305         // center panel can't be hidden
40306     },
40307     
40308     getMinWidth: function(){
40309         return this.minWidth;
40310     },
40311     
40312     getMinHeight: function(){
40313         return this.minHeight;
40314     }
40315 });
40316
40317
40318
40319
40320  
40321
40322
40323
40324
40325
40326
40327 Roo.bootstrap.layout.North = function(config)
40328 {
40329     config.region = 'north';
40330     config.cursor = 'n-resize';
40331     
40332     Roo.bootstrap.layout.Split.call(this, config);
40333     
40334     
40335     if(this.split){
40336         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40337         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40338         this.split.el.addClass("roo-layout-split-v");
40339     }
40340     //var size = config.initialSize || config.height;
40341     //if(this.el && typeof size != "undefined"){
40342     //    this.el.setHeight(size);
40343     //}
40344 };
40345 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40346 {
40347     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40348      
40349      
40350     onRender : function(ctr, pos)
40351     {
40352         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40353         var size = this.config.initialSize || this.config.height;
40354         if(this.el && typeof size != "undefined"){
40355             this.el.setHeight(size);
40356         }
40357     
40358     },
40359     
40360     getBox : function(){
40361         if(this.collapsed){
40362             return this.collapsedEl.getBox();
40363         }
40364         var box = this.el.getBox();
40365         if(this.split){
40366             box.height += this.split.el.getHeight();
40367         }
40368         return box;
40369     },
40370     
40371     updateBox : function(box){
40372         if(this.split && !this.collapsed){
40373             box.height -= this.split.el.getHeight();
40374             this.split.el.setLeft(box.x);
40375             this.split.el.setTop(box.y+box.height);
40376             this.split.el.setWidth(box.width);
40377         }
40378         if(this.collapsed){
40379             this.updateBody(box.width, null);
40380         }
40381         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40382     }
40383 });
40384
40385
40386
40387
40388
40389 Roo.bootstrap.layout.South = function(config){
40390     config.region = 'south';
40391     config.cursor = 's-resize';
40392     Roo.bootstrap.layout.Split.call(this, config);
40393     if(this.split){
40394         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40395         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40396         this.split.el.addClass("roo-layout-split-v");
40397     }
40398     
40399 };
40400
40401 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40402     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40403     
40404     onRender : function(ctr, pos)
40405     {
40406         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40407         var size = this.config.initialSize || this.config.height;
40408         if(this.el && typeof size != "undefined"){
40409             this.el.setHeight(size);
40410         }
40411     
40412     },
40413     
40414     getBox : function(){
40415         if(this.collapsed){
40416             return this.collapsedEl.getBox();
40417         }
40418         var box = this.el.getBox();
40419         if(this.split){
40420             var sh = this.split.el.getHeight();
40421             box.height += sh;
40422             box.y -= sh;
40423         }
40424         return box;
40425     },
40426     
40427     updateBox : function(box){
40428         if(this.split && !this.collapsed){
40429             var sh = this.split.el.getHeight();
40430             box.height -= sh;
40431             box.y += sh;
40432             this.split.el.setLeft(box.x);
40433             this.split.el.setTop(box.y-sh);
40434             this.split.el.setWidth(box.width);
40435         }
40436         if(this.collapsed){
40437             this.updateBody(box.width, null);
40438         }
40439         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40440     }
40441 });
40442
40443 Roo.bootstrap.layout.East = function(config){
40444     config.region = "east";
40445     config.cursor = "e-resize";
40446     Roo.bootstrap.layout.Split.call(this, config);
40447     if(this.split){
40448         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40449         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40450         this.split.el.addClass("roo-layout-split-h");
40451     }
40452     
40453 };
40454 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40455     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40456     
40457     onRender : function(ctr, pos)
40458     {
40459         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40460         var size = this.config.initialSize || this.config.width;
40461         if(this.el && typeof size != "undefined"){
40462             this.el.setWidth(size);
40463         }
40464     
40465     },
40466     
40467     getBox : function(){
40468         if(this.collapsed){
40469             return this.collapsedEl.getBox();
40470         }
40471         var box = this.el.getBox();
40472         if(this.split){
40473             var sw = this.split.el.getWidth();
40474             box.width += sw;
40475             box.x -= sw;
40476         }
40477         return box;
40478     },
40479
40480     updateBox : function(box){
40481         if(this.split && !this.collapsed){
40482             var sw = this.split.el.getWidth();
40483             box.width -= sw;
40484             this.split.el.setLeft(box.x);
40485             this.split.el.setTop(box.y);
40486             this.split.el.setHeight(box.height);
40487             box.x += sw;
40488         }
40489         if(this.collapsed){
40490             this.updateBody(null, box.height);
40491         }
40492         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40493     }
40494 });
40495
40496 Roo.bootstrap.layout.West = function(config){
40497     config.region = "west";
40498     config.cursor = "w-resize";
40499     
40500     Roo.bootstrap.layout.Split.call(this, config);
40501     if(this.split){
40502         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40503         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40504         this.split.el.addClass("roo-layout-split-h");
40505     }
40506     
40507 };
40508 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40509     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40510     
40511     onRender: function(ctr, pos)
40512     {
40513         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40514         var size = this.config.initialSize || this.config.width;
40515         if(typeof size != "undefined"){
40516             this.el.setWidth(size);
40517         }
40518     },
40519     
40520     getBox : function(){
40521         if(this.collapsed){
40522             return this.collapsedEl.getBox();
40523         }
40524         var box = this.el.getBox();
40525         if (box.width == 0) {
40526             box.width = this.config.width; // kludge?
40527         }
40528         if(this.split){
40529             box.width += this.split.el.getWidth();
40530         }
40531         return box;
40532     },
40533     
40534     updateBox : function(box){
40535         if(this.split && !this.collapsed){
40536             var sw = this.split.el.getWidth();
40537             box.width -= sw;
40538             this.split.el.setLeft(box.x+box.width);
40539             this.split.el.setTop(box.y);
40540             this.split.el.setHeight(box.height);
40541         }
40542         if(this.collapsed){
40543             this.updateBody(null, box.height);
40544         }
40545         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40546     }
40547 });Roo.namespace("Roo.bootstrap.panel");/*
40548  * Based on:
40549  * Ext JS Library 1.1.1
40550  * Copyright(c) 2006-2007, Ext JS, LLC.
40551  *
40552  * Originally Released Under LGPL - original licence link has changed is not relivant.
40553  *
40554  * Fork - LGPL
40555  * <script type="text/javascript">
40556  */
40557 /**
40558  * @class Roo.ContentPanel
40559  * @extends Roo.util.Observable
40560  * @builder-top
40561  * A basic ContentPanel element.
40562  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40563  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40564  * @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
40565  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40566  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40567  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40568  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40569  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40570  * @cfg {String} title          The title for this panel
40571  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40572  * @cfg {String} url            Calls {@link #setUrl} with this value
40573  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40574  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40575  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40576  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40577  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40578  * @cfg {Boolean} badges render the badges
40579  * @cfg {String} cls  extra classes to use  
40580  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40581
40582  * @constructor
40583  * Create a new ContentPanel.
40584  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40585  * @param {String/Object} config A string to set only the title or a config object
40586  * @param {String} content (optional) Set the HTML content for this panel
40587  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40588  */
40589 Roo.bootstrap.panel.Content = function( config){
40590     
40591     this.tpl = config.tpl || false;
40592     
40593     var el = config.el;
40594     var content = config.content;
40595
40596     if(config.autoCreate){ // xtype is available if this is called from factory
40597         el = Roo.id();
40598     }
40599     this.el = Roo.get(el);
40600     if(!this.el && config && config.autoCreate){
40601         if(typeof config.autoCreate == "object"){
40602             if(!config.autoCreate.id){
40603                 config.autoCreate.id = config.id||el;
40604             }
40605             this.el = Roo.DomHelper.append(document.body,
40606                         config.autoCreate, true);
40607         }else{
40608             var elcfg =  {
40609                 tag: "div",
40610                 cls: (config.cls || '') +
40611                     (config.background ? ' bg-' + config.background : '') +
40612                     " roo-layout-inactive-content",
40613                 id: config.id||el
40614             };
40615             if (config.iframe) {
40616                 elcfg.cn = [
40617                     {
40618                         tag : 'iframe',
40619                         style : 'border: 0px',
40620                         src : 'about:blank'
40621                     }
40622                 ];
40623             }
40624               
40625             if (config.html) {
40626                 elcfg.html = config.html;
40627                 
40628             }
40629                         
40630             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40631             if (config.iframe) {
40632                 this.iframeEl = this.el.select('iframe',true).first();
40633             }
40634             
40635         }
40636     } 
40637     this.closable = false;
40638     this.loaded = false;
40639     this.active = false;
40640    
40641       
40642     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40643         
40644         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40645         
40646         this.wrapEl = this.el; //this.el.wrap();
40647         var ti = [];
40648         if (config.toolbar.items) {
40649             ti = config.toolbar.items ;
40650             delete config.toolbar.items ;
40651         }
40652         
40653         var nitems = [];
40654         this.toolbar.render(this.wrapEl, 'before');
40655         for(var i =0;i < ti.length;i++) {
40656           //  Roo.log(['add child', items[i]]);
40657             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40658         }
40659         this.toolbar.items = nitems;
40660         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40661         delete config.toolbar;
40662         
40663     }
40664     /*
40665     // xtype created footer. - not sure if will work as we normally have to render first..
40666     if (this.footer && !this.footer.el && this.footer.xtype) {
40667         if (!this.wrapEl) {
40668             this.wrapEl = this.el.wrap();
40669         }
40670     
40671         this.footer.container = this.wrapEl.createChild();
40672          
40673         this.footer = Roo.factory(this.footer, Roo);
40674         
40675     }
40676     */
40677     
40678      if(typeof config == "string"){
40679         this.title = config;
40680     }else{
40681         Roo.apply(this, config);
40682     }
40683     
40684     if(this.resizeEl){
40685         this.resizeEl = Roo.get(this.resizeEl, true);
40686     }else{
40687         this.resizeEl = this.el;
40688     }
40689     // handle view.xtype
40690     
40691  
40692     
40693     
40694     this.addEvents({
40695         /**
40696          * @event activate
40697          * Fires when this panel is activated. 
40698          * @param {Roo.ContentPanel} this
40699          */
40700         "activate" : true,
40701         /**
40702          * @event deactivate
40703          * Fires when this panel is activated. 
40704          * @param {Roo.ContentPanel} this
40705          */
40706         "deactivate" : true,
40707
40708         /**
40709          * @event resize
40710          * Fires when this panel is resized if fitToFrame is true.
40711          * @param {Roo.ContentPanel} this
40712          * @param {Number} width The width after any component adjustments
40713          * @param {Number} height The height after any component adjustments
40714          */
40715         "resize" : true,
40716         
40717          /**
40718          * @event render
40719          * Fires when this tab is created
40720          * @param {Roo.ContentPanel} this
40721          */
40722         "render" : true,
40723         
40724           /**
40725          * @event scroll
40726          * Fires when this content is scrolled
40727          * @param {Roo.ContentPanel} this
40728          * @param {Event} scrollEvent
40729          */
40730         "scroll" : true
40731         
40732         
40733         
40734     });
40735     
40736
40737     
40738     
40739     if(this.autoScroll && !this.iframe){
40740         this.resizeEl.setStyle("overflow", "auto");
40741         this.resizeEl.on('scroll', this.onScroll, this);
40742     } else {
40743         // fix randome scrolling
40744         //this.el.on('scroll', function() {
40745         //    Roo.log('fix random scolling');
40746         //    this.scrollTo('top',0); 
40747         //});
40748     }
40749     content = content || this.content;
40750     if(content){
40751         this.setContent(content);
40752     }
40753     if(config && config.url){
40754         this.setUrl(this.url, this.params, this.loadOnce);
40755     }
40756     
40757     
40758     
40759     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40760     
40761     if (this.view && typeof(this.view.xtype) != 'undefined') {
40762         this.view.el = this.el.appendChild(document.createElement("div"));
40763         this.view = Roo.factory(this.view); 
40764         this.view.render  &&  this.view.render(false, '');  
40765     }
40766     
40767     
40768     this.fireEvent('render', this);
40769 };
40770
40771 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40772     
40773     cls : '',
40774     background : '',
40775     
40776     tabTip : '',
40777     
40778     iframe : false,
40779     iframeEl : false,
40780     
40781     /* Resize Element - use this to work out scroll etc. */
40782     resizeEl : false,
40783     
40784     setRegion : function(region){
40785         this.region = region;
40786         this.setActiveClass(region && !this.background);
40787     },
40788     
40789     
40790     setActiveClass: function(state)
40791     {
40792         if(state){
40793            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40794            this.el.setStyle('position','relative');
40795         }else{
40796            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40797            this.el.setStyle('position', 'absolute');
40798         } 
40799     },
40800     
40801     /**
40802      * Returns the toolbar for this Panel if one was configured. 
40803      * @return {Roo.Toolbar} 
40804      */
40805     getToolbar : function(){
40806         return this.toolbar;
40807     },
40808     
40809     setActiveState : function(active)
40810     {
40811         this.active = active;
40812         this.setActiveClass(active);
40813         if(!active){
40814             if(this.fireEvent("deactivate", this) === false){
40815                 return false;
40816             }
40817             return true;
40818         }
40819         this.fireEvent("activate", this);
40820         return true;
40821     },
40822     /**
40823      * Updates this panel's element (not for iframe)
40824      * @param {String} content The new content
40825      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40826     */
40827     setContent : function(content, loadScripts){
40828         if (this.iframe) {
40829             return;
40830         }
40831         
40832         this.el.update(content, loadScripts);
40833     },
40834
40835     ignoreResize : function(w, h){
40836         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40837             return true;
40838         }else{
40839             this.lastSize = {width: w, height: h};
40840             return false;
40841         }
40842     },
40843     /**
40844      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40845      * @return {Roo.UpdateManager} The UpdateManager
40846      */
40847     getUpdateManager : function(){
40848         if (this.iframe) {
40849             return false;
40850         }
40851         return this.el.getUpdateManager();
40852     },
40853      /**
40854      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40855      * Does not work with IFRAME contents
40856      * @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:
40857 <pre><code>
40858 panel.load({
40859     url: "your-url.php",
40860     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40861     callback: yourFunction,
40862     scope: yourObject, //(optional scope)
40863     discardUrl: false,
40864     nocache: false,
40865     text: "Loading...",
40866     timeout: 30,
40867     scripts: false
40868 });
40869 </code></pre>
40870      
40871      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40872      * 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.
40873      * @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}
40874      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40875      * @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.
40876      * @return {Roo.ContentPanel} this
40877      */
40878     load : function(){
40879         
40880         if (this.iframe) {
40881             return this;
40882         }
40883         
40884         var um = this.el.getUpdateManager();
40885         um.update.apply(um, arguments);
40886         return this;
40887     },
40888
40889
40890     /**
40891      * 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.
40892      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40893      * @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)
40894      * @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)
40895      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40896      */
40897     setUrl : function(url, params, loadOnce){
40898         if (this.iframe) {
40899             this.iframeEl.dom.src = url;
40900             return false;
40901         }
40902         
40903         if(this.refreshDelegate){
40904             this.removeListener("activate", this.refreshDelegate);
40905         }
40906         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40907         this.on("activate", this.refreshDelegate);
40908         return this.el.getUpdateManager();
40909     },
40910     
40911     _handleRefresh : function(url, params, loadOnce){
40912         if(!loadOnce || !this.loaded){
40913             var updater = this.el.getUpdateManager();
40914             updater.update(url, params, this._setLoaded.createDelegate(this));
40915         }
40916     },
40917     
40918     _setLoaded : function(){
40919         this.loaded = true;
40920     }, 
40921     
40922     /**
40923      * Returns this panel's id
40924      * @return {String} 
40925      */
40926     getId : function(){
40927         return this.el.id;
40928     },
40929     
40930     /** 
40931      * Returns this panel's element - used by regiosn to add.
40932      * @return {Roo.Element} 
40933      */
40934     getEl : function(){
40935         return this.wrapEl || this.el;
40936     },
40937     
40938    
40939     
40940     adjustForComponents : function(width, height)
40941     {
40942         //Roo.log('adjustForComponents ');
40943         if(this.resizeEl != this.el){
40944             width -= this.el.getFrameWidth('lr');
40945             height -= this.el.getFrameWidth('tb');
40946         }
40947         if(this.toolbar){
40948             var te = this.toolbar.getEl();
40949             te.setWidth(width);
40950             height -= te.getHeight();
40951         }
40952         if(this.footer){
40953             var te = this.footer.getEl();
40954             te.setWidth(width);
40955             height -= te.getHeight();
40956         }
40957         
40958         
40959         if(this.adjustments){
40960             width += this.adjustments[0];
40961             height += this.adjustments[1];
40962         }
40963         return {"width": width, "height": height};
40964     },
40965     
40966     setSize : function(width, height){
40967         if(this.fitToFrame && !this.ignoreResize(width, height)){
40968             if(this.fitContainer && this.resizeEl != this.el){
40969                 this.el.setSize(width, height);
40970             }
40971             var size = this.adjustForComponents(width, height);
40972             if (this.iframe) {
40973                 this.iframeEl.setSize(width,height);
40974             }
40975             
40976             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40977             this.fireEvent('resize', this, size.width, size.height);
40978             
40979             
40980         }
40981     },
40982     
40983     /**
40984      * Returns this panel's title
40985      * @return {String} 
40986      */
40987     getTitle : function(){
40988         
40989         if (typeof(this.title) != 'object') {
40990             return this.title;
40991         }
40992         
40993         var t = '';
40994         for (var k in this.title) {
40995             if (!this.title.hasOwnProperty(k)) {
40996                 continue;
40997             }
40998             
40999             if (k.indexOf('-') >= 0) {
41000                 var s = k.split('-');
41001                 for (var i = 0; i<s.length; i++) {
41002                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41003                 }
41004             } else {
41005                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41006             }
41007         }
41008         return t;
41009     },
41010     
41011     /**
41012      * Set this panel's title
41013      * @param {String} title
41014      */
41015     setTitle : function(title){
41016         this.title = title;
41017         if(this.region){
41018             this.region.updatePanelTitle(this, title);
41019         }
41020     },
41021     
41022     /**
41023      * Returns true is this panel was configured to be closable
41024      * @return {Boolean} 
41025      */
41026     isClosable : function(){
41027         return this.closable;
41028     },
41029     
41030     beforeSlide : function(){
41031         this.el.clip();
41032         this.resizeEl.clip();
41033     },
41034     
41035     afterSlide : function(){
41036         this.el.unclip();
41037         this.resizeEl.unclip();
41038     },
41039     
41040     /**
41041      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41042      *   Will fail silently if the {@link #setUrl} method has not been called.
41043      *   This does not activate the panel, just updates its content.
41044      */
41045     refresh : function(){
41046         if(this.refreshDelegate){
41047            this.loaded = false;
41048            this.refreshDelegate();
41049         }
41050     },
41051     
41052     /**
41053      * Destroys this panel
41054      */
41055     destroy : function(){
41056         this.el.removeAllListeners();
41057         var tempEl = document.createElement("span");
41058         tempEl.appendChild(this.el.dom);
41059         tempEl.innerHTML = "";
41060         this.el.remove();
41061         this.el = null;
41062     },
41063     
41064     /**
41065      * form - if the content panel contains a form - this is a reference to it.
41066      * @type {Roo.form.Form}
41067      */
41068     form : false,
41069     /**
41070      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41071      *    This contains a reference to it.
41072      * @type {Roo.View}
41073      */
41074     view : false,
41075     
41076       /**
41077      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41078      * <pre><code>
41079
41080 layout.addxtype({
41081        xtype : 'Form',
41082        items: [ .... ]
41083    }
41084 );
41085
41086 </code></pre>
41087      * @param {Object} cfg Xtype definition of item to add.
41088      */
41089     
41090     
41091     getChildContainer: function () {
41092         return this.getEl();
41093     },
41094     
41095     
41096     onScroll : function(e)
41097     {
41098         this.fireEvent('scroll', this, e);
41099     }
41100     
41101     
41102     /*
41103         var  ret = new Roo.factory(cfg);
41104         return ret;
41105         
41106         
41107         // add form..
41108         if (cfg.xtype.match(/^Form$/)) {
41109             
41110             var el;
41111             //if (this.footer) {
41112             //    el = this.footer.container.insertSibling(false, 'before');
41113             //} else {
41114                 el = this.el.createChild();
41115             //}
41116
41117             this.form = new  Roo.form.Form(cfg);
41118             
41119             
41120             if ( this.form.allItems.length) {
41121                 this.form.render(el.dom);
41122             }
41123             return this.form;
41124         }
41125         // should only have one of theses..
41126         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41127             // views.. should not be just added - used named prop 'view''
41128             
41129             cfg.el = this.el.appendChild(document.createElement("div"));
41130             // factory?
41131             
41132             var ret = new Roo.factory(cfg);
41133              
41134              ret.render && ret.render(false, ''); // render blank..
41135             this.view = ret;
41136             return ret;
41137         }
41138         return false;
41139     }
41140     \*/
41141 });
41142  
41143 /**
41144  * @class Roo.bootstrap.panel.Grid
41145  * @extends Roo.bootstrap.panel.Content
41146  * @constructor
41147  * Create a new GridPanel.
41148  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41149  * @param {Object} config A the config object
41150   
41151  */
41152
41153
41154
41155 Roo.bootstrap.panel.Grid = function(config)
41156 {
41157     
41158       
41159     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41160         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41161
41162     config.el = this.wrapper;
41163     //this.el = this.wrapper;
41164     
41165       if (config.container) {
41166         // ctor'ed from a Border/panel.grid
41167         
41168         
41169         this.wrapper.setStyle("overflow", "hidden");
41170         this.wrapper.addClass('roo-grid-container');
41171
41172     }
41173     
41174     
41175     if(config.toolbar){
41176         var tool_el = this.wrapper.createChild();    
41177         this.toolbar = Roo.factory(config.toolbar);
41178         var ti = [];
41179         if (config.toolbar.items) {
41180             ti = config.toolbar.items ;
41181             delete config.toolbar.items ;
41182         }
41183         
41184         var nitems = [];
41185         this.toolbar.render(tool_el);
41186         for(var i =0;i < ti.length;i++) {
41187           //  Roo.log(['add child', items[i]]);
41188             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41189         }
41190         this.toolbar.items = nitems;
41191         
41192         delete config.toolbar;
41193     }
41194     
41195     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41196     config.grid.scrollBody = true;;
41197     config.grid.monitorWindowResize = false; // turn off autosizing
41198     config.grid.autoHeight = false;
41199     config.grid.autoWidth = false;
41200     
41201     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41202     
41203     if (config.background) {
41204         // render grid on panel activation (if panel background)
41205         this.on('activate', function(gp) {
41206             if (!gp.grid.rendered) {
41207                 gp.grid.render(this.wrapper);
41208                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41209             }
41210         });
41211             
41212     } else {
41213         this.grid.render(this.wrapper);
41214         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41215
41216     }
41217     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41218     // ??? needed ??? config.el = this.wrapper;
41219     
41220     
41221     
41222   
41223     // xtype created footer. - not sure if will work as we normally have to render first..
41224     if (this.footer && !this.footer.el && this.footer.xtype) {
41225         
41226         var ctr = this.grid.getView().getFooterPanel(true);
41227         this.footer.dataSource = this.grid.dataSource;
41228         this.footer = Roo.factory(this.footer, Roo);
41229         this.footer.render(ctr);
41230         
41231     }
41232     
41233     
41234     
41235     
41236      
41237 };
41238
41239 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41240     getId : function(){
41241         return this.grid.id;
41242     },
41243     
41244     /**
41245      * Returns the grid for this panel
41246      * @return {Roo.bootstrap.Table} 
41247      */
41248     getGrid : function(){
41249         return this.grid;    
41250     },
41251     
41252     setSize : function(width, height){
41253         if(!this.ignoreResize(width, height)){
41254             var grid = this.grid;
41255             var size = this.adjustForComponents(width, height);
41256             // tfoot is not a footer?
41257           
41258             
41259             var gridel = grid.getGridEl();
41260             gridel.setSize(size.width, size.height);
41261             
41262             var tbd = grid.getGridEl().select('tbody', true).first();
41263             var thd = grid.getGridEl().select('thead',true).first();
41264             var tbf= grid.getGridEl().select('tfoot', true).first();
41265
41266             if (tbf) {
41267                 size.height -= tbf.getHeight();
41268             }
41269             if (thd) {
41270                 size.height -= thd.getHeight();
41271             }
41272             
41273             tbd.setSize(size.width, size.height );
41274             // this is for the account management tab -seems to work there.
41275             var thd = grid.getGridEl().select('thead',true).first();
41276             //if (tbd) {
41277             //    tbd.setSize(size.width, size.height - thd.getHeight());
41278             //}
41279              
41280             grid.autoSize();
41281         }
41282     },
41283      
41284     
41285     
41286     beforeSlide : function(){
41287         this.grid.getView().scroller.clip();
41288     },
41289     
41290     afterSlide : function(){
41291         this.grid.getView().scroller.unclip();
41292     },
41293     
41294     destroy : function(){
41295         this.grid.destroy();
41296         delete this.grid;
41297         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41298     }
41299 });
41300
41301 /**
41302  * @class Roo.bootstrap.panel.Nest
41303  * @extends Roo.bootstrap.panel.Content
41304  * @constructor
41305  * Create a new Panel, that can contain a layout.Border.
41306  * 
41307  * 
41308  * @param {Roo.BorderLayout} layout The layout for this panel
41309  * @param {String/Object} config A string to set only the title or a config object
41310  */
41311 Roo.bootstrap.panel.Nest = function(config)
41312 {
41313     // construct with only one argument..
41314     /* FIXME - implement nicer consturctors
41315     if (layout.layout) {
41316         config = layout;
41317         layout = config.layout;
41318         delete config.layout;
41319     }
41320     if (layout.xtype && !layout.getEl) {
41321         // then layout needs constructing..
41322         layout = Roo.factory(layout, Roo);
41323     }
41324     */
41325     
41326     config.el =  config.layout.getEl();
41327     
41328     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41329     
41330     config.layout.monitorWindowResize = false; // turn off autosizing
41331     this.layout = config.layout;
41332     this.layout.getEl().addClass("roo-layout-nested-layout");
41333     this.layout.parent = this;
41334     
41335     
41336     
41337     
41338 };
41339
41340 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41341
41342     setSize : function(width, height){
41343         if(!this.ignoreResize(width, height)){
41344             var size = this.adjustForComponents(width, height);
41345             var el = this.layout.getEl();
41346             if (size.height < 1) {
41347                 el.setWidth(size.width);   
41348             } else {
41349                 el.setSize(size.width, size.height);
41350             }
41351             var touch = el.dom.offsetWidth;
41352             this.layout.layout();
41353             // ie requires a double layout on the first pass
41354             if(Roo.isIE && !this.initialized){
41355                 this.initialized = true;
41356                 this.layout.layout();
41357             }
41358         }
41359     },
41360     
41361     // activate all subpanels if not currently active..
41362     
41363     setActiveState : function(active){
41364         this.active = active;
41365         this.setActiveClass(active);
41366         
41367         if(!active){
41368             this.fireEvent("deactivate", this);
41369             return;
41370         }
41371         
41372         this.fireEvent("activate", this);
41373         // not sure if this should happen before or after..
41374         if (!this.layout) {
41375             return; // should not happen..
41376         }
41377         var reg = false;
41378         for (var r in this.layout.regions) {
41379             reg = this.layout.getRegion(r);
41380             if (reg.getActivePanel()) {
41381                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41382                 reg.setActivePanel(reg.getActivePanel());
41383                 continue;
41384             }
41385             if (!reg.panels.length) {
41386                 continue;
41387             }
41388             reg.showPanel(reg.getPanel(0));
41389         }
41390         
41391         
41392         
41393         
41394     },
41395     
41396     /**
41397      * Returns the nested BorderLayout for this panel
41398      * @return {Roo.BorderLayout} 
41399      */
41400     getLayout : function(){
41401         return this.layout;
41402     },
41403     
41404      /**
41405      * Adds a xtype elements to the layout of the nested panel
41406      * <pre><code>
41407
41408 panel.addxtype({
41409        xtype : 'ContentPanel',
41410        region: 'west',
41411        items: [ .... ]
41412    }
41413 );
41414
41415 panel.addxtype({
41416         xtype : 'NestedLayoutPanel',
41417         region: 'west',
41418         layout: {
41419            center: { },
41420            west: { }   
41421         },
41422         items : [ ... list of content panels or nested layout panels.. ]
41423    }
41424 );
41425 </code></pre>
41426      * @param {Object} cfg Xtype definition of item to add.
41427      */
41428     addxtype : function(cfg) {
41429         return this.layout.addxtype(cfg);
41430     
41431     }
41432 });/*
41433  * Based on:
41434  * Ext JS Library 1.1.1
41435  * Copyright(c) 2006-2007, Ext JS, LLC.
41436  *
41437  * Originally Released Under LGPL - original licence link has changed is not relivant.
41438  *
41439  * Fork - LGPL
41440  * <script type="text/javascript">
41441  */
41442 /**
41443  * @class Roo.TabPanel
41444  * @extends Roo.util.Observable
41445  * A lightweight tab container.
41446  * <br><br>
41447  * Usage:
41448  * <pre><code>
41449 // basic tabs 1, built from existing content
41450 var tabs = new Roo.TabPanel("tabs1");
41451 tabs.addTab("script", "View Script");
41452 tabs.addTab("markup", "View Markup");
41453 tabs.activate("script");
41454
41455 // more advanced tabs, built from javascript
41456 var jtabs = new Roo.TabPanel("jtabs");
41457 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41458
41459 // set up the UpdateManager
41460 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41461 var updater = tab2.getUpdateManager();
41462 updater.setDefaultUrl("ajax1.htm");
41463 tab2.on('activate', updater.refresh, updater, true);
41464
41465 // Use setUrl for Ajax loading
41466 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41467 tab3.setUrl("ajax2.htm", null, true);
41468
41469 // Disabled tab
41470 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41471 tab4.disable();
41472
41473 jtabs.activate("jtabs-1");
41474  * </code></pre>
41475  * @constructor
41476  * Create a new TabPanel.
41477  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41478  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41479  */
41480 Roo.bootstrap.panel.Tabs = function(config){
41481     /**
41482     * The container element for this TabPanel.
41483     * @type Roo.Element
41484     */
41485     this.el = Roo.get(config.el);
41486     delete config.el;
41487     if(config){
41488         if(typeof config == "boolean"){
41489             this.tabPosition = config ? "bottom" : "top";
41490         }else{
41491             Roo.apply(this, config);
41492         }
41493     }
41494     
41495     if(this.tabPosition == "bottom"){
41496         // if tabs are at the bottom = create the body first.
41497         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41498         this.el.addClass("roo-tabs-bottom");
41499     }
41500     // next create the tabs holders
41501     
41502     if (this.tabPosition == "west"){
41503         
41504         var reg = this.region; // fake it..
41505         while (reg) {
41506             if (!reg.mgr.parent) {
41507                 break;
41508             }
41509             reg = reg.mgr.parent.region;
41510         }
41511         Roo.log("got nest?");
41512         Roo.log(reg);
41513         if (reg.mgr.getRegion('west')) {
41514             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41515             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41516             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41517             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41518             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41519         
41520             
41521         }
41522         
41523         
41524     } else {
41525      
41526         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41527         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41528         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41529         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41530     }
41531     
41532     
41533     if(Roo.isIE){
41534         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41535     }
41536     
41537     // finally - if tabs are at the top, then create the body last..
41538     if(this.tabPosition != "bottom"){
41539         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41540          * @type Roo.Element
41541          */
41542         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41543         this.el.addClass("roo-tabs-top");
41544     }
41545     this.items = [];
41546
41547     this.bodyEl.setStyle("position", "relative");
41548
41549     this.active = null;
41550     this.activateDelegate = this.activate.createDelegate(this);
41551
41552     this.addEvents({
41553         /**
41554          * @event tabchange
41555          * Fires when the active tab changes
41556          * @param {Roo.TabPanel} this
41557          * @param {Roo.TabPanelItem} activePanel The new active tab
41558          */
41559         "tabchange": true,
41560         /**
41561          * @event beforetabchange
41562          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41563          * @param {Roo.TabPanel} this
41564          * @param {Object} e Set cancel to true on this object to cancel the tab change
41565          * @param {Roo.TabPanelItem} tab The tab being changed to
41566          */
41567         "beforetabchange" : true
41568     });
41569
41570     Roo.EventManager.onWindowResize(this.onResize, this);
41571     this.cpad = this.el.getPadding("lr");
41572     this.hiddenCount = 0;
41573
41574
41575     // toolbar on the tabbar support...
41576     if (this.toolbar) {
41577         alert("no toolbar support yet");
41578         this.toolbar  = false;
41579         /*
41580         var tcfg = this.toolbar;
41581         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41582         this.toolbar = new Roo.Toolbar(tcfg);
41583         if (Roo.isSafari) {
41584             var tbl = tcfg.container.child('table', true);
41585             tbl.setAttribute('width', '100%');
41586         }
41587         */
41588         
41589     }
41590    
41591
41592
41593     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41594 };
41595
41596 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41597     /*
41598      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41599      */
41600     tabPosition : "top",
41601     /*
41602      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41603      */
41604     currentTabWidth : 0,
41605     /*
41606      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41607      */
41608     minTabWidth : 40,
41609     /*
41610      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41611      */
41612     maxTabWidth : 250,
41613     /*
41614      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41615      */
41616     preferredTabWidth : 175,
41617     /*
41618      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41619      */
41620     resizeTabs : false,
41621     /*
41622      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41623      */
41624     monitorResize : true,
41625     /*
41626      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41627      */
41628     toolbar : false,  // set by caller..
41629     
41630     region : false, /// set by caller
41631     
41632     disableTooltips : true, // not used yet...
41633
41634     /**
41635      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41636      * @param {String} id The id of the div to use <b>or create</b>
41637      * @param {String} text The text for the tab
41638      * @param {String} content (optional) Content to put in the TabPanelItem body
41639      * @param {Boolean} closable (optional) True to create a close icon on the tab
41640      * @return {Roo.TabPanelItem} The created TabPanelItem
41641      */
41642     addTab : function(id, text, content, closable, tpl)
41643     {
41644         var item = new Roo.bootstrap.panel.TabItem({
41645             panel: this,
41646             id : id,
41647             text : text,
41648             closable : closable,
41649             tpl : tpl
41650         });
41651         this.addTabItem(item);
41652         if(content){
41653             item.setContent(content);
41654         }
41655         return item;
41656     },
41657
41658     /**
41659      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41660      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41661      * @return {Roo.TabPanelItem}
41662      */
41663     getTab : function(id){
41664         return this.items[id];
41665     },
41666
41667     /**
41668      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41669      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41670      */
41671     hideTab : function(id){
41672         var t = this.items[id];
41673         if(!t.isHidden()){
41674            t.setHidden(true);
41675            this.hiddenCount++;
41676            this.autoSizeTabs();
41677         }
41678     },
41679
41680     /**
41681      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41682      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41683      */
41684     unhideTab : function(id){
41685         var t = this.items[id];
41686         if(t.isHidden()){
41687            t.setHidden(false);
41688            this.hiddenCount--;
41689            this.autoSizeTabs();
41690         }
41691     },
41692
41693     /**
41694      * Adds an existing {@link Roo.TabPanelItem}.
41695      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41696      */
41697     addTabItem : function(item)
41698     {
41699         this.items[item.id] = item;
41700         this.items.push(item);
41701         this.autoSizeTabs();
41702       //  if(this.resizeTabs){
41703     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41704   //         this.autoSizeTabs();
41705 //        }else{
41706 //            item.autoSize();
41707        // }
41708     },
41709
41710     /**
41711      * Removes a {@link Roo.TabPanelItem}.
41712      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41713      */
41714     removeTab : function(id){
41715         var items = this.items;
41716         var tab = items[id];
41717         if(!tab) { return; }
41718         var index = items.indexOf(tab);
41719         if(this.active == tab && items.length > 1){
41720             var newTab = this.getNextAvailable(index);
41721             if(newTab) {
41722                 newTab.activate();
41723             }
41724         }
41725         this.stripEl.dom.removeChild(tab.pnode.dom);
41726         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41727             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41728         }
41729         items.splice(index, 1);
41730         delete this.items[tab.id];
41731         tab.fireEvent("close", tab);
41732         tab.purgeListeners();
41733         this.autoSizeTabs();
41734     },
41735
41736     getNextAvailable : function(start){
41737         var items = this.items;
41738         var index = start;
41739         // look for a next tab that will slide over to
41740         // replace the one being removed
41741         while(index < items.length){
41742             var item = items[++index];
41743             if(item && !item.isHidden()){
41744                 return item;
41745             }
41746         }
41747         // if one isn't found select the previous tab (on the left)
41748         index = start;
41749         while(index >= 0){
41750             var item = items[--index];
41751             if(item && !item.isHidden()){
41752                 return item;
41753             }
41754         }
41755         return null;
41756     },
41757
41758     /**
41759      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41760      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41761      */
41762     disableTab : function(id){
41763         var tab = this.items[id];
41764         if(tab && this.active != tab){
41765             tab.disable();
41766         }
41767     },
41768
41769     /**
41770      * Enables a {@link Roo.TabPanelItem} that is disabled.
41771      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41772      */
41773     enableTab : function(id){
41774         var tab = this.items[id];
41775         tab.enable();
41776     },
41777
41778     /**
41779      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41780      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41781      * @return {Roo.TabPanelItem} The TabPanelItem.
41782      */
41783     activate : function(id)
41784     {
41785         //Roo.log('activite:'  + id);
41786         
41787         var tab = this.items[id];
41788         if(!tab){
41789             return null;
41790         }
41791         if(tab == this.active || tab.disabled){
41792             return tab;
41793         }
41794         var e = {};
41795         this.fireEvent("beforetabchange", this, e, tab);
41796         if(e.cancel !== true && !tab.disabled){
41797             if(this.active){
41798                 this.active.hide();
41799             }
41800             this.active = this.items[id];
41801             this.active.show();
41802             this.fireEvent("tabchange", this, this.active);
41803         }
41804         return tab;
41805     },
41806
41807     /**
41808      * Gets the active {@link Roo.TabPanelItem}.
41809      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41810      */
41811     getActiveTab : function(){
41812         return this.active;
41813     },
41814
41815     /**
41816      * Updates the tab body element to fit the height of the container element
41817      * for overflow scrolling
41818      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41819      */
41820     syncHeight : function(targetHeight){
41821         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41822         var bm = this.bodyEl.getMargins();
41823         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41824         this.bodyEl.setHeight(newHeight);
41825         return newHeight;
41826     },
41827
41828     onResize : function(){
41829         if(this.monitorResize){
41830             this.autoSizeTabs();
41831         }
41832     },
41833
41834     /**
41835      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41836      */
41837     beginUpdate : function(){
41838         this.updating = true;
41839     },
41840
41841     /**
41842      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41843      */
41844     endUpdate : function(){
41845         this.updating = false;
41846         this.autoSizeTabs();
41847     },
41848
41849     /**
41850      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41851      */
41852     autoSizeTabs : function()
41853     {
41854         var count = this.items.length;
41855         var vcount = count - this.hiddenCount;
41856         
41857         if (vcount < 2) {
41858             this.stripEl.hide();
41859         } else {
41860             this.stripEl.show();
41861         }
41862         
41863         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41864             return;
41865         }
41866         
41867         
41868         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41869         var availWidth = Math.floor(w / vcount);
41870         var b = this.stripBody;
41871         if(b.getWidth() > w){
41872             var tabs = this.items;
41873             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41874             if(availWidth < this.minTabWidth){
41875                 /*if(!this.sleft){    // incomplete scrolling code
41876                     this.createScrollButtons();
41877                 }
41878                 this.showScroll();
41879                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41880             }
41881         }else{
41882             if(this.currentTabWidth < this.preferredTabWidth){
41883                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41884             }
41885         }
41886     },
41887
41888     /**
41889      * Returns the number of tabs in this TabPanel.
41890      * @return {Number}
41891      */
41892      getCount : function(){
41893          return this.items.length;
41894      },
41895
41896     /**
41897      * Resizes all the tabs to the passed width
41898      * @param {Number} The new width
41899      */
41900     setTabWidth : function(width){
41901         this.currentTabWidth = width;
41902         for(var i = 0, len = this.items.length; i < len; i++) {
41903                 if(!this.items[i].isHidden()) {
41904                 this.items[i].setWidth(width);
41905             }
41906         }
41907     },
41908
41909     /**
41910      * Destroys this TabPanel
41911      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41912      */
41913     destroy : function(removeEl){
41914         Roo.EventManager.removeResizeListener(this.onResize, this);
41915         for(var i = 0, len = this.items.length; i < len; i++){
41916             this.items[i].purgeListeners();
41917         }
41918         if(removeEl === true){
41919             this.el.update("");
41920             this.el.remove();
41921         }
41922     },
41923     
41924     createStrip : function(container)
41925     {
41926         var strip = document.createElement("nav");
41927         strip.className = Roo.bootstrap.version == 4 ?
41928             "navbar-light bg-light" : 
41929             "navbar navbar-default"; //"x-tabs-wrap";
41930         container.appendChild(strip);
41931         return strip;
41932     },
41933     
41934     createStripList : function(strip)
41935     {
41936         // div wrapper for retard IE
41937         // returns the "tr" element.
41938         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41939         //'<div class="x-tabs-strip-wrap">'+
41940           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41941           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41942         return strip.firstChild; //.firstChild.firstChild.firstChild;
41943     },
41944     createBody : function(container)
41945     {
41946         var body = document.createElement("div");
41947         Roo.id(body, "tab-body");
41948         //Roo.fly(body).addClass("x-tabs-body");
41949         Roo.fly(body).addClass("tab-content");
41950         container.appendChild(body);
41951         return body;
41952     },
41953     createItemBody :function(bodyEl, id){
41954         var body = Roo.getDom(id);
41955         if(!body){
41956             body = document.createElement("div");
41957             body.id = id;
41958         }
41959         //Roo.fly(body).addClass("x-tabs-item-body");
41960         Roo.fly(body).addClass("tab-pane");
41961          bodyEl.insertBefore(body, bodyEl.firstChild);
41962         return body;
41963     },
41964     /** @private */
41965     createStripElements :  function(stripEl, text, closable, tpl)
41966     {
41967         var td = document.createElement("li"); // was td..
41968         td.className = 'nav-item';
41969         
41970         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41971         
41972         
41973         stripEl.appendChild(td);
41974         /*if(closable){
41975             td.className = "x-tabs-closable";
41976             if(!this.closeTpl){
41977                 this.closeTpl = new Roo.Template(
41978                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41979                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41980                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41981                 );
41982             }
41983             var el = this.closeTpl.overwrite(td, {"text": text});
41984             var close = el.getElementsByTagName("div")[0];
41985             var inner = el.getElementsByTagName("em")[0];
41986             return {"el": el, "close": close, "inner": inner};
41987         } else {
41988         */
41989         // not sure what this is..
41990 //            if(!this.tabTpl){
41991                 //this.tabTpl = new Roo.Template(
41992                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41993                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41994                 //);
41995 //                this.tabTpl = new Roo.Template(
41996 //                   '<a href="#">' +
41997 //                   '<span unselectable="on"' +
41998 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41999 //                            ' >{text}</span></a>'
42000 //                );
42001 //                
42002 //            }
42003
42004
42005             var template = tpl || this.tabTpl || false;
42006             
42007             if(!template){
42008                 template =  new Roo.Template(
42009                         Roo.bootstrap.version == 4 ? 
42010                             (
42011                                 '<a class="nav-link" href="#" unselectable="on"' +
42012                                      (this.disableTooltips ? '' : ' title="{text}"') +
42013                                      ' >{text}</a>'
42014                             ) : (
42015                                 '<a class="nav-link" href="#">' +
42016                                 '<span unselectable="on"' +
42017                                          (this.disableTooltips ? '' : ' title="{text}"') +
42018                                     ' >{text}</span></a>'
42019                             )
42020                 );
42021             }
42022             
42023             switch (typeof(template)) {
42024                 case 'object' :
42025                     break;
42026                 case 'string' :
42027                     template = new Roo.Template(template);
42028                     break;
42029                 default :
42030                     break;
42031             }
42032             
42033             var el = template.overwrite(td, {"text": text});
42034             
42035             var inner = el.getElementsByTagName("span")[0];
42036             
42037             return {"el": el, "inner": inner};
42038             
42039     }
42040         
42041     
42042 });
42043
42044 /**
42045  * @class Roo.TabPanelItem
42046  * @extends Roo.util.Observable
42047  * Represents an individual item (tab plus body) in a TabPanel.
42048  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42049  * @param {String} id The id of this TabPanelItem
42050  * @param {String} text The text for the tab of this TabPanelItem
42051  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42052  */
42053 Roo.bootstrap.panel.TabItem = function(config){
42054     /**
42055      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42056      * @type Roo.TabPanel
42057      */
42058     this.tabPanel = config.panel;
42059     /**
42060      * The id for this TabPanelItem
42061      * @type String
42062      */
42063     this.id = config.id;
42064     /** @private */
42065     this.disabled = false;
42066     /** @private */
42067     this.text = config.text;
42068     /** @private */
42069     this.loaded = false;
42070     this.closable = config.closable;
42071
42072     /**
42073      * The body element for this TabPanelItem.
42074      * @type Roo.Element
42075      */
42076     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42077     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42078     this.bodyEl.setStyle("display", "block");
42079     this.bodyEl.setStyle("zoom", "1");
42080     //this.hideAction();
42081
42082     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42083     /** @private */
42084     this.el = Roo.get(els.el);
42085     this.inner = Roo.get(els.inner, true);
42086      this.textEl = Roo.bootstrap.version == 4 ?
42087         this.el : Roo.get(this.el.dom.firstChild, true);
42088
42089     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42090     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42091
42092     
42093 //    this.el.on("mousedown", this.onTabMouseDown, this);
42094     this.el.on("click", this.onTabClick, this);
42095     /** @private */
42096     if(config.closable){
42097         var c = Roo.get(els.close, true);
42098         c.dom.title = this.closeText;
42099         c.addClassOnOver("close-over");
42100         c.on("click", this.closeClick, this);
42101      }
42102
42103     this.addEvents({
42104          /**
42105          * @event activate
42106          * Fires when this tab becomes the active tab.
42107          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42108          * @param {Roo.TabPanelItem} this
42109          */
42110         "activate": true,
42111         /**
42112          * @event beforeclose
42113          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42114          * @param {Roo.TabPanelItem} this
42115          * @param {Object} e Set cancel to true on this object to cancel the close.
42116          */
42117         "beforeclose": true,
42118         /**
42119          * @event close
42120          * Fires when this tab is closed.
42121          * @param {Roo.TabPanelItem} this
42122          */
42123          "close": true,
42124         /**
42125          * @event deactivate
42126          * Fires when this tab is no longer the active tab.
42127          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42128          * @param {Roo.TabPanelItem} this
42129          */
42130          "deactivate" : true
42131     });
42132     this.hidden = false;
42133
42134     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42135 };
42136
42137 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42138            {
42139     purgeListeners : function(){
42140        Roo.util.Observable.prototype.purgeListeners.call(this);
42141        this.el.removeAllListeners();
42142     },
42143     /**
42144      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42145      */
42146     show : function(){
42147         this.status_node.addClass("active");
42148         this.showAction();
42149         if(Roo.isOpera){
42150             this.tabPanel.stripWrap.repaint();
42151         }
42152         this.fireEvent("activate", this.tabPanel, this);
42153     },
42154
42155     /**
42156      * Returns true if this tab is the active tab.
42157      * @return {Boolean}
42158      */
42159     isActive : function(){
42160         return this.tabPanel.getActiveTab() == this;
42161     },
42162
42163     /**
42164      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42165      */
42166     hide : function(){
42167         this.status_node.removeClass("active");
42168         this.hideAction();
42169         this.fireEvent("deactivate", this.tabPanel, this);
42170     },
42171
42172     hideAction : function(){
42173         this.bodyEl.hide();
42174         this.bodyEl.setStyle("position", "absolute");
42175         this.bodyEl.setLeft("-20000px");
42176         this.bodyEl.setTop("-20000px");
42177     },
42178
42179     showAction : function(){
42180         this.bodyEl.setStyle("position", "relative");
42181         this.bodyEl.setTop("");
42182         this.bodyEl.setLeft("");
42183         this.bodyEl.show();
42184     },
42185
42186     /**
42187      * Set the tooltip for the tab.
42188      * @param {String} tooltip The tab's tooltip
42189      */
42190     setTooltip : function(text){
42191         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42192             this.textEl.dom.qtip = text;
42193             this.textEl.dom.removeAttribute('title');
42194         }else{
42195             this.textEl.dom.title = text;
42196         }
42197     },
42198
42199     onTabClick : function(e){
42200         e.preventDefault();
42201         this.tabPanel.activate(this.id);
42202     },
42203
42204     onTabMouseDown : function(e){
42205         e.preventDefault();
42206         this.tabPanel.activate(this.id);
42207     },
42208 /*
42209     getWidth : function(){
42210         return this.inner.getWidth();
42211     },
42212
42213     setWidth : function(width){
42214         var iwidth = width - this.linode.getPadding("lr");
42215         this.inner.setWidth(iwidth);
42216         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42217         this.linode.setWidth(width);
42218     },
42219 */
42220     /**
42221      * Show or hide the tab
42222      * @param {Boolean} hidden True to hide or false to show.
42223      */
42224     setHidden : function(hidden){
42225         this.hidden = hidden;
42226         this.linode.setStyle("display", hidden ? "none" : "");
42227     },
42228
42229     /**
42230      * Returns true if this tab is "hidden"
42231      * @return {Boolean}
42232      */
42233     isHidden : function(){
42234         return this.hidden;
42235     },
42236
42237     /**
42238      * Returns the text for this tab
42239      * @return {String}
42240      */
42241     getText : function(){
42242         return this.text;
42243     },
42244     /*
42245     autoSize : function(){
42246         //this.el.beginMeasure();
42247         this.textEl.setWidth(1);
42248         /*
42249          *  #2804 [new] Tabs in Roojs
42250          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42251          */
42252         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42253         //this.el.endMeasure();
42254     //},
42255
42256     /**
42257      * Sets the text for the tab (Note: this also sets the tooltip text)
42258      * @param {String} text The tab's text and tooltip
42259      */
42260     setText : function(text){
42261         this.text = text;
42262         this.textEl.update(text);
42263         this.setTooltip(text);
42264         //if(!this.tabPanel.resizeTabs){
42265         //    this.autoSize();
42266         //}
42267     },
42268     /**
42269      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42270      */
42271     activate : function(){
42272         this.tabPanel.activate(this.id);
42273     },
42274
42275     /**
42276      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42277      */
42278     disable : function(){
42279         if(this.tabPanel.active != this){
42280             this.disabled = true;
42281             this.status_node.addClass("disabled");
42282         }
42283     },
42284
42285     /**
42286      * Enables this TabPanelItem if it was previously disabled.
42287      */
42288     enable : function(){
42289         this.disabled = false;
42290         this.status_node.removeClass("disabled");
42291     },
42292
42293     /**
42294      * Sets the content for this TabPanelItem.
42295      * @param {String} content The content
42296      * @param {Boolean} loadScripts true to look for and load scripts
42297      */
42298     setContent : function(content, loadScripts){
42299         this.bodyEl.update(content, loadScripts);
42300     },
42301
42302     /**
42303      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42304      * @return {Roo.UpdateManager} The UpdateManager
42305      */
42306     getUpdateManager : function(){
42307         return this.bodyEl.getUpdateManager();
42308     },
42309
42310     /**
42311      * Set a URL to be used to load the content for this TabPanelItem.
42312      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42313      * @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)
42314      * @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)
42315      * @return {Roo.UpdateManager} The UpdateManager
42316      */
42317     setUrl : function(url, params, loadOnce){
42318         if(this.refreshDelegate){
42319             this.un('activate', this.refreshDelegate);
42320         }
42321         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42322         this.on("activate", this.refreshDelegate);
42323         return this.bodyEl.getUpdateManager();
42324     },
42325
42326     /** @private */
42327     _handleRefresh : function(url, params, loadOnce){
42328         if(!loadOnce || !this.loaded){
42329             var updater = this.bodyEl.getUpdateManager();
42330             updater.update(url, params, this._setLoaded.createDelegate(this));
42331         }
42332     },
42333
42334     /**
42335      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42336      *   Will fail silently if the setUrl method has not been called.
42337      *   This does not activate the panel, just updates its content.
42338      */
42339     refresh : function(){
42340         if(this.refreshDelegate){
42341            this.loaded = false;
42342            this.refreshDelegate();
42343         }
42344     },
42345
42346     /** @private */
42347     _setLoaded : function(){
42348         this.loaded = true;
42349     },
42350
42351     /** @private */
42352     closeClick : function(e){
42353         var o = {};
42354         e.stopEvent();
42355         this.fireEvent("beforeclose", this, o);
42356         if(o.cancel !== true){
42357             this.tabPanel.removeTab(this.id);
42358         }
42359     },
42360     /**
42361      * The text displayed in the tooltip for the close icon.
42362      * @type String
42363      */
42364     closeText : "Close this tab"
42365 });
42366 /**
42367 *    This script refer to:
42368 *    Title: International Telephone Input
42369 *    Author: Jack O'Connor
42370 *    Code version:  v12.1.12
42371 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42372 **/
42373
42374 Roo.bootstrap.PhoneInputData = function() {
42375     var d = [
42376       [
42377         "Afghanistan (‫افغانستان‬‎)",
42378         "af",
42379         "93"
42380       ],
42381       [
42382         "Albania (Shqipëri)",
42383         "al",
42384         "355"
42385       ],
42386       [
42387         "Algeria (‫الجزائر‬‎)",
42388         "dz",
42389         "213"
42390       ],
42391       [
42392         "American Samoa",
42393         "as",
42394         "1684"
42395       ],
42396       [
42397         "Andorra",
42398         "ad",
42399         "376"
42400       ],
42401       [
42402         "Angola",
42403         "ao",
42404         "244"
42405       ],
42406       [
42407         "Anguilla",
42408         "ai",
42409         "1264"
42410       ],
42411       [
42412         "Antigua and Barbuda",
42413         "ag",
42414         "1268"
42415       ],
42416       [
42417         "Argentina",
42418         "ar",
42419         "54"
42420       ],
42421       [
42422         "Armenia (Հայաստան)",
42423         "am",
42424         "374"
42425       ],
42426       [
42427         "Aruba",
42428         "aw",
42429         "297"
42430       ],
42431       [
42432         "Australia",
42433         "au",
42434         "61",
42435         0
42436       ],
42437       [
42438         "Austria (Österreich)",
42439         "at",
42440         "43"
42441       ],
42442       [
42443         "Azerbaijan (Azərbaycan)",
42444         "az",
42445         "994"
42446       ],
42447       [
42448         "Bahamas",
42449         "bs",
42450         "1242"
42451       ],
42452       [
42453         "Bahrain (‫البحرين‬‎)",
42454         "bh",
42455         "973"
42456       ],
42457       [
42458         "Bangladesh (বাংলাদেশ)",
42459         "bd",
42460         "880"
42461       ],
42462       [
42463         "Barbados",
42464         "bb",
42465         "1246"
42466       ],
42467       [
42468         "Belarus (Беларусь)",
42469         "by",
42470         "375"
42471       ],
42472       [
42473         "Belgium (België)",
42474         "be",
42475         "32"
42476       ],
42477       [
42478         "Belize",
42479         "bz",
42480         "501"
42481       ],
42482       [
42483         "Benin (Bénin)",
42484         "bj",
42485         "229"
42486       ],
42487       [
42488         "Bermuda",
42489         "bm",
42490         "1441"
42491       ],
42492       [
42493         "Bhutan (འབྲུག)",
42494         "bt",
42495         "975"
42496       ],
42497       [
42498         "Bolivia",
42499         "bo",
42500         "591"
42501       ],
42502       [
42503         "Bosnia and Herzegovina (Босна и Херцеговина)",
42504         "ba",
42505         "387"
42506       ],
42507       [
42508         "Botswana",
42509         "bw",
42510         "267"
42511       ],
42512       [
42513         "Brazil (Brasil)",
42514         "br",
42515         "55"
42516       ],
42517       [
42518         "British Indian Ocean Territory",
42519         "io",
42520         "246"
42521       ],
42522       [
42523         "British Virgin Islands",
42524         "vg",
42525         "1284"
42526       ],
42527       [
42528         "Brunei",
42529         "bn",
42530         "673"
42531       ],
42532       [
42533         "Bulgaria (България)",
42534         "bg",
42535         "359"
42536       ],
42537       [
42538         "Burkina Faso",
42539         "bf",
42540         "226"
42541       ],
42542       [
42543         "Burundi (Uburundi)",
42544         "bi",
42545         "257"
42546       ],
42547       [
42548         "Cambodia (កម្ពុជា)",
42549         "kh",
42550         "855"
42551       ],
42552       [
42553         "Cameroon (Cameroun)",
42554         "cm",
42555         "237"
42556       ],
42557       [
42558         "Canada",
42559         "ca",
42560         "1",
42561         1,
42562         ["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"]
42563       ],
42564       [
42565         "Cape Verde (Kabu Verdi)",
42566         "cv",
42567         "238"
42568       ],
42569       [
42570         "Caribbean Netherlands",
42571         "bq",
42572         "599",
42573         1
42574       ],
42575       [
42576         "Cayman Islands",
42577         "ky",
42578         "1345"
42579       ],
42580       [
42581         "Central African Republic (République centrafricaine)",
42582         "cf",
42583         "236"
42584       ],
42585       [
42586         "Chad (Tchad)",
42587         "td",
42588         "235"
42589       ],
42590       [
42591         "Chile",
42592         "cl",
42593         "56"
42594       ],
42595       [
42596         "China (中国)",
42597         "cn",
42598         "86"
42599       ],
42600       [
42601         "Christmas Island",
42602         "cx",
42603         "61",
42604         2
42605       ],
42606       [
42607         "Cocos (Keeling) Islands",
42608         "cc",
42609         "61",
42610         1
42611       ],
42612       [
42613         "Colombia",
42614         "co",
42615         "57"
42616       ],
42617       [
42618         "Comoros (‫جزر القمر‬‎)",
42619         "km",
42620         "269"
42621       ],
42622       [
42623         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42624         "cd",
42625         "243"
42626       ],
42627       [
42628         "Congo (Republic) (Congo-Brazzaville)",
42629         "cg",
42630         "242"
42631       ],
42632       [
42633         "Cook Islands",
42634         "ck",
42635         "682"
42636       ],
42637       [
42638         "Costa Rica",
42639         "cr",
42640         "506"
42641       ],
42642       [
42643         "Côte d’Ivoire",
42644         "ci",
42645         "225"
42646       ],
42647       [
42648         "Croatia (Hrvatska)",
42649         "hr",
42650         "385"
42651       ],
42652       [
42653         "Cuba",
42654         "cu",
42655         "53"
42656       ],
42657       [
42658         "Curaçao",
42659         "cw",
42660         "599",
42661         0
42662       ],
42663       [
42664         "Cyprus (Κύπρος)",
42665         "cy",
42666         "357"
42667       ],
42668       [
42669         "Czech Republic (Česká republika)",
42670         "cz",
42671         "420"
42672       ],
42673       [
42674         "Denmark (Danmark)",
42675         "dk",
42676         "45"
42677       ],
42678       [
42679         "Djibouti",
42680         "dj",
42681         "253"
42682       ],
42683       [
42684         "Dominica",
42685         "dm",
42686         "1767"
42687       ],
42688       [
42689         "Dominican Republic (República Dominicana)",
42690         "do",
42691         "1",
42692         2,
42693         ["809", "829", "849"]
42694       ],
42695       [
42696         "Ecuador",
42697         "ec",
42698         "593"
42699       ],
42700       [
42701         "Egypt (‫مصر‬‎)",
42702         "eg",
42703         "20"
42704       ],
42705       [
42706         "El Salvador",
42707         "sv",
42708         "503"
42709       ],
42710       [
42711         "Equatorial Guinea (Guinea Ecuatorial)",
42712         "gq",
42713         "240"
42714       ],
42715       [
42716         "Eritrea",
42717         "er",
42718         "291"
42719       ],
42720       [
42721         "Estonia (Eesti)",
42722         "ee",
42723         "372"
42724       ],
42725       [
42726         "Ethiopia",
42727         "et",
42728         "251"
42729       ],
42730       [
42731         "Falkland Islands (Islas Malvinas)",
42732         "fk",
42733         "500"
42734       ],
42735       [
42736         "Faroe Islands (Føroyar)",
42737         "fo",
42738         "298"
42739       ],
42740       [
42741         "Fiji",
42742         "fj",
42743         "679"
42744       ],
42745       [
42746         "Finland (Suomi)",
42747         "fi",
42748         "358",
42749         0
42750       ],
42751       [
42752         "France",
42753         "fr",
42754         "33"
42755       ],
42756       [
42757         "French Guiana (Guyane française)",
42758         "gf",
42759         "594"
42760       ],
42761       [
42762         "French Polynesia (Polynésie française)",
42763         "pf",
42764         "689"
42765       ],
42766       [
42767         "Gabon",
42768         "ga",
42769         "241"
42770       ],
42771       [
42772         "Gambia",
42773         "gm",
42774         "220"
42775       ],
42776       [
42777         "Georgia (საქართველო)",
42778         "ge",
42779         "995"
42780       ],
42781       [
42782         "Germany (Deutschland)",
42783         "de",
42784         "49"
42785       ],
42786       [
42787         "Ghana (Gaana)",
42788         "gh",
42789         "233"
42790       ],
42791       [
42792         "Gibraltar",
42793         "gi",
42794         "350"
42795       ],
42796       [
42797         "Greece (Ελλάδα)",
42798         "gr",
42799         "30"
42800       ],
42801       [
42802         "Greenland (Kalaallit Nunaat)",
42803         "gl",
42804         "299"
42805       ],
42806       [
42807         "Grenada",
42808         "gd",
42809         "1473"
42810       ],
42811       [
42812         "Guadeloupe",
42813         "gp",
42814         "590",
42815         0
42816       ],
42817       [
42818         "Guam",
42819         "gu",
42820         "1671"
42821       ],
42822       [
42823         "Guatemala",
42824         "gt",
42825         "502"
42826       ],
42827       [
42828         "Guernsey",
42829         "gg",
42830         "44",
42831         1
42832       ],
42833       [
42834         "Guinea (Guinée)",
42835         "gn",
42836         "224"
42837       ],
42838       [
42839         "Guinea-Bissau (Guiné Bissau)",
42840         "gw",
42841         "245"
42842       ],
42843       [
42844         "Guyana",
42845         "gy",
42846         "592"
42847       ],
42848       [
42849         "Haiti",
42850         "ht",
42851         "509"
42852       ],
42853       [
42854         "Honduras",
42855         "hn",
42856         "504"
42857       ],
42858       [
42859         "Hong Kong (香港)",
42860         "hk",
42861         "852"
42862       ],
42863       [
42864         "Hungary (Magyarország)",
42865         "hu",
42866         "36"
42867       ],
42868       [
42869         "Iceland (Ísland)",
42870         "is",
42871         "354"
42872       ],
42873       [
42874         "India (भारत)",
42875         "in",
42876         "91"
42877       ],
42878       [
42879         "Indonesia",
42880         "id",
42881         "62"
42882       ],
42883       [
42884         "Iran (‫ایران‬‎)",
42885         "ir",
42886         "98"
42887       ],
42888       [
42889         "Iraq (‫العراق‬‎)",
42890         "iq",
42891         "964"
42892       ],
42893       [
42894         "Ireland",
42895         "ie",
42896         "353"
42897       ],
42898       [
42899         "Isle of Man",
42900         "im",
42901         "44",
42902         2
42903       ],
42904       [
42905         "Israel (‫ישראל‬‎)",
42906         "il",
42907         "972"
42908       ],
42909       [
42910         "Italy (Italia)",
42911         "it",
42912         "39",
42913         0
42914       ],
42915       [
42916         "Jamaica",
42917         "jm",
42918         "1876"
42919       ],
42920       [
42921         "Japan (日本)",
42922         "jp",
42923         "81"
42924       ],
42925       [
42926         "Jersey",
42927         "je",
42928         "44",
42929         3
42930       ],
42931       [
42932         "Jordan (‫الأردن‬‎)",
42933         "jo",
42934         "962"
42935       ],
42936       [
42937         "Kazakhstan (Казахстан)",
42938         "kz",
42939         "7",
42940         1
42941       ],
42942       [
42943         "Kenya",
42944         "ke",
42945         "254"
42946       ],
42947       [
42948         "Kiribati",
42949         "ki",
42950         "686"
42951       ],
42952       [
42953         "Kosovo",
42954         "xk",
42955         "383"
42956       ],
42957       [
42958         "Kuwait (‫الكويت‬‎)",
42959         "kw",
42960         "965"
42961       ],
42962       [
42963         "Kyrgyzstan (Кыргызстан)",
42964         "kg",
42965         "996"
42966       ],
42967       [
42968         "Laos (ລາວ)",
42969         "la",
42970         "856"
42971       ],
42972       [
42973         "Latvia (Latvija)",
42974         "lv",
42975         "371"
42976       ],
42977       [
42978         "Lebanon (‫لبنان‬‎)",
42979         "lb",
42980         "961"
42981       ],
42982       [
42983         "Lesotho",
42984         "ls",
42985         "266"
42986       ],
42987       [
42988         "Liberia",
42989         "lr",
42990         "231"
42991       ],
42992       [
42993         "Libya (‫ليبيا‬‎)",
42994         "ly",
42995         "218"
42996       ],
42997       [
42998         "Liechtenstein",
42999         "li",
43000         "423"
43001       ],
43002       [
43003         "Lithuania (Lietuva)",
43004         "lt",
43005         "370"
43006       ],
43007       [
43008         "Luxembourg",
43009         "lu",
43010         "352"
43011       ],
43012       [
43013         "Macau (澳門)",
43014         "mo",
43015         "853"
43016       ],
43017       [
43018         "Macedonia (FYROM) (Македонија)",
43019         "mk",
43020         "389"
43021       ],
43022       [
43023         "Madagascar (Madagasikara)",
43024         "mg",
43025         "261"
43026       ],
43027       [
43028         "Malawi",
43029         "mw",
43030         "265"
43031       ],
43032       [
43033         "Malaysia",
43034         "my",
43035         "60"
43036       ],
43037       [
43038         "Maldives",
43039         "mv",
43040         "960"
43041       ],
43042       [
43043         "Mali",
43044         "ml",
43045         "223"
43046       ],
43047       [
43048         "Malta",
43049         "mt",
43050         "356"
43051       ],
43052       [
43053         "Marshall Islands",
43054         "mh",
43055         "692"
43056       ],
43057       [
43058         "Martinique",
43059         "mq",
43060         "596"
43061       ],
43062       [
43063         "Mauritania (‫موريتانيا‬‎)",
43064         "mr",
43065         "222"
43066       ],
43067       [
43068         "Mauritius (Moris)",
43069         "mu",
43070         "230"
43071       ],
43072       [
43073         "Mayotte",
43074         "yt",
43075         "262",
43076         1
43077       ],
43078       [
43079         "Mexico (México)",
43080         "mx",
43081         "52"
43082       ],
43083       [
43084         "Micronesia",
43085         "fm",
43086         "691"
43087       ],
43088       [
43089         "Moldova (Republica Moldova)",
43090         "md",
43091         "373"
43092       ],
43093       [
43094         "Monaco",
43095         "mc",
43096         "377"
43097       ],
43098       [
43099         "Mongolia (Монгол)",
43100         "mn",
43101         "976"
43102       ],
43103       [
43104         "Montenegro (Crna Gora)",
43105         "me",
43106         "382"
43107       ],
43108       [
43109         "Montserrat",
43110         "ms",
43111         "1664"
43112       ],
43113       [
43114         "Morocco (‫المغرب‬‎)",
43115         "ma",
43116         "212",
43117         0
43118       ],
43119       [
43120         "Mozambique (Moçambique)",
43121         "mz",
43122         "258"
43123       ],
43124       [
43125         "Myanmar (Burma) (မြန်မာ)",
43126         "mm",
43127         "95"
43128       ],
43129       [
43130         "Namibia (Namibië)",
43131         "na",
43132         "264"
43133       ],
43134       [
43135         "Nauru",
43136         "nr",
43137         "674"
43138       ],
43139       [
43140         "Nepal (नेपाल)",
43141         "np",
43142         "977"
43143       ],
43144       [
43145         "Netherlands (Nederland)",
43146         "nl",
43147         "31"
43148       ],
43149       [
43150         "New Caledonia (Nouvelle-Calédonie)",
43151         "nc",
43152         "687"
43153       ],
43154       [
43155         "New Zealand",
43156         "nz",
43157         "64"
43158       ],
43159       [
43160         "Nicaragua",
43161         "ni",
43162         "505"
43163       ],
43164       [
43165         "Niger (Nijar)",
43166         "ne",
43167         "227"
43168       ],
43169       [
43170         "Nigeria",
43171         "ng",
43172         "234"
43173       ],
43174       [
43175         "Niue",
43176         "nu",
43177         "683"
43178       ],
43179       [
43180         "Norfolk Island",
43181         "nf",
43182         "672"
43183       ],
43184       [
43185         "North Korea (조선 민주주의 인민 공화국)",
43186         "kp",
43187         "850"
43188       ],
43189       [
43190         "Northern Mariana Islands",
43191         "mp",
43192         "1670"
43193       ],
43194       [
43195         "Norway (Norge)",
43196         "no",
43197         "47",
43198         0
43199       ],
43200       [
43201         "Oman (‫عُمان‬‎)",
43202         "om",
43203         "968"
43204       ],
43205       [
43206         "Pakistan (‫پاکستان‬‎)",
43207         "pk",
43208         "92"
43209       ],
43210       [
43211         "Palau",
43212         "pw",
43213         "680"
43214       ],
43215       [
43216         "Palestine (‫فلسطين‬‎)",
43217         "ps",
43218         "970"
43219       ],
43220       [
43221         "Panama (Panamá)",
43222         "pa",
43223         "507"
43224       ],
43225       [
43226         "Papua New Guinea",
43227         "pg",
43228         "675"
43229       ],
43230       [
43231         "Paraguay",
43232         "py",
43233         "595"
43234       ],
43235       [
43236         "Peru (Perú)",
43237         "pe",
43238         "51"
43239       ],
43240       [
43241         "Philippines",
43242         "ph",
43243         "63"
43244       ],
43245       [
43246         "Poland (Polska)",
43247         "pl",
43248         "48"
43249       ],
43250       [
43251         "Portugal",
43252         "pt",
43253         "351"
43254       ],
43255       [
43256         "Puerto Rico",
43257         "pr",
43258         "1",
43259         3,
43260         ["787", "939"]
43261       ],
43262       [
43263         "Qatar (‫قطر‬‎)",
43264         "qa",
43265         "974"
43266       ],
43267       [
43268         "Réunion (La Réunion)",
43269         "re",
43270         "262",
43271         0
43272       ],
43273       [
43274         "Romania (România)",
43275         "ro",
43276         "40"
43277       ],
43278       [
43279         "Russia (Россия)",
43280         "ru",
43281         "7",
43282         0
43283       ],
43284       [
43285         "Rwanda",
43286         "rw",
43287         "250"
43288       ],
43289       [
43290         "Saint Barthélemy",
43291         "bl",
43292         "590",
43293         1
43294       ],
43295       [
43296         "Saint Helena",
43297         "sh",
43298         "290"
43299       ],
43300       [
43301         "Saint Kitts and Nevis",
43302         "kn",
43303         "1869"
43304       ],
43305       [
43306         "Saint Lucia",
43307         "lc",
43308         "1758"
43309       ],
43310       [
43311         "Saint Martin (Saint-Martin (partie française))",
43312         "mf",
43313         "590",
43314         2
43315       ],
43316       [
43317         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43318         "pm",
43319         "508"
43320       ],
43321       [
43322         "Saint Vincent and the Grenadines",
43323         "vc",
43324         "1784"
43325       ],
43326       [
43327         "Samoa",
43328         "ws",
43329         "685"
43330       ],
43331       [
43332         "San Marino",
43333         "sm",
43334         "378"
43335       ],
43336       [
43337         "São Tomé and Príncipe (São Tomé e Príncipe)",
43338         "st",
43339         "239"
43340       ],
43341       [
43342         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43343         "sa",
43344         "966"
43345       ],
43346       [
43347         "Senegal (Sénégal)",
43348         "sn",
43349         "221"
43350       ],
43351       [
43352         "Serbia (Србија)",
43353         "rs",
43354         "381"
43355       ],
43356       [
43357         "Seychelles",
43358         "sc",
43359         "248"
43360       ],
43361       [
43362         "Sierra Leone",
43363         "sl",
43364         "232"
43365       ],
43366       [
43367         "Singapore",
43368         "sg",
43369         "65"
43370       ],
43371       [
43372         "Sint Maarten",
43373         "sx",
43374         "1721"
43375       ],
43376       [
43377         "Slovakia (Slovensko)",
43378         "sk",
43379         "421"
43380       ],
43381       [
43382         "Slovenia (Slovenija)",
43383         "si",
43384         "386"
43385       ],
43386       [
43387         "Solomon Islands",
43388         "sb",
43389         "677"
43390       ],
43391       [
43392         "Somalia (Soomaaliya)",
43393         "so",
43394         "252"
43395       ],
43396       [
43397         "South Africa",
43398         "za",
43399         "27"
43400       ],
43401       [
43402         "South Korea (대한민국)",
43403         "kr",
43404         "82"
43405       ],
43406       [
43407         "South Sudan (‫جنوب السودان‬‎)",
43408         "ss",
43409         "211"
43410       ],
43411       [
43412         "Spain (España)",
43413         "es",
43414         "34"
43415       ],
43416       [
43417         "Sri Lanka (ශ්‍රී ලංකාව)",
43418         "lk",
43419         "94"
43420       ],
43421       [
43422         "Sudan (‫السودان‬‎)",
43423         "sd",
43424         "249"
43425       ],
43426       [
43427         "Suriname",
43428         "sr",
43429         "597"
43430       ],
43431       [
43432         "Svalbard and Jan Mayen",
43433         "sj",
43434         "47",
43435         1
43436       ],
43437       [
43438         "Swaziland",
43439         "sz",
43440         "268"
43441       ],
43442       [
43443         "Sweden (Sverige)",
43444         "se",
43445         "46"
43446       ],
43447       [
43448         "Switzerland (Schweiz)",
43449         "ch",
43450         "41"
43451       ],
43452       [
43453         "Syria (‫سوريا‬‎)",
43454         "sy",
43455         "963"
43456       ],
43457       [
43458         "Taiwan (台灣)",
43459         "tw",
43460         "886"
43461       ],
43462       [
43463         "Tajikistan",
43464         "tj",
43465         "992"
43466       ],
43467       [
43468         "Tanzania",
43469         "tz",
43470         "255"
43471       ],
43472       [
43473         "Thailand (ไทย)",
43474         "th",
43475         "66"
43476       ],
43477       [
43478         "Timor-Leste",
43479         "tl",
43480         "670"
43481       ],
43482       [
43483         "Togo",
43484         "tg",
43485         "228"
43486       ],
43487       [
43488         "Tokelau",
43489         "tk",
43490         "690"
43491       ],
43492       [
43493         "Tonga",
43494         "to",
43495         "676"
43496       ],
43497       [
43498         "Trinidad and Tobago",
43499         "tt",
43500         "1868"
43501       ],
43502       [
43503         "Tunisia (‫تونس‬‎)",
43504         "tn",
43505         "216"
43506       ],
43507       [
43508         "Turkey (Türkiye)",
43509         "tr",
43510         "90"
43511       ],
43512       [
43513         "Turkmenistan",
43514         "tm",
43515         "993"
43516       ],
43517       [
43518         "Turks and Caicos Islands",
43519         "tc",
43520         "1649"
43521       ],
43522       [
43523         "Tuvalu",
43524         "tv",
43525         "688"
43526       ],
43527       [
43528         "U.S. Virgin Islands",
43529         "vi",
43530         "1340"
43531       ],
43532       [
43533         "Uganda",
43534         "ug",
43535         "256"
43536       ],
43537       [
43538         "Ukraine (Україна)",
43539         "ua",
43540         "380"
43541       ],
43542       [
43543         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43544         "ae",
43545         "971"
43546       ],
43547       [
43548         "United Kingdom",
43549         "gb",
43550         "44",
43551         0
43552       ],
43553       [
43554         "United States",
43555         "us",
43556         "1",
43557         0
43558       ],
43559       [
43560         "Uruguay",
43561         "uy",
43562         "598"
43563       ],
43564       [
43565         "Uzbekistan (Oʻzbekiston)",
43566         "uz",
43567         "998"
43568       ],
43569       [
43570         "Vanuatu",
43571         "vu",
43572         "678"
43573       ],
43574       [
43575         "Vatican City (Città del Vaticano)",
43576         "va",
43577         "39",
43578         1
43579       ],
43580       [
43581         "Venezuela",
43582         "ve",
43583         "58"
43584       ],
43585       [
43586         "Vietnam (Việt Nam)",
43587         "vn",
43588         "84"
43589       ],
43590       [
43591         "Wallis and Futuna (Wallis-et-Futuna)",
43592         "wf",
43593         "681"
43594       ],
43595       [
43596         "Western Sahara (‫الصحراء الغربية‬‎)",
43597         "eh",
43598         "212",
43599         1
43600       ],
43601       [
43602         "Yemen (‫اليمن‬‎)",
43603         "ye",
43604         "967"
43605       ],
43606       [
43607         "Zambia",
43608         "zm",
43609         "260"
43610       ],
43611       [
43612         "Zimbabwe",
43613         "zw",
43614         "263"
43615       ],
43616       [
43617         "Åland Islands",
43618         "ax",
43619         "358",
43620         1
43621       ]
43622   ];
43623   
43624   return d;
43625 }/**
43626 *    This script refer to:
43627 *    Title: International Telephone Input
43628 *    Author: Jack O'Connor
43629 *    Code version:  v12.1.12
43630 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43631 **/
43632
43633 /**
43634  * @class Roo.bootstrap.PhoneInput
43635  * @extends Roo.bootstrap.TriggerField
43636  * An input with International dial-code selection
43637  
43638  * @cfg {String} defaultDialCode default '+852'
43639  * @cfg {Array} preferedCountries default []
43640   
43641  * @constructor
43642  * Create a new PhoneInput.
43643  * @param {Object} config Configuration options
43644  */
43645
43646 Roo.bootstrap.PhoneInput = function(config) {
43647     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43648 };
43649
43650 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43651         /**
43652         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43653         */
43654         listWidth: undefined,
43655         
43656         selectedClass: 'active',
43657         
43658         invalidClass : "has-warning",
43659         
43660         validClass: 'has-success',
43661         
43662         allowed: '0123456789',
43663         
43664         max_length: 15,
43665         
43666         /**
43667          * @cfg {String} defaultDialCode The default dial code when initializing the input
43668          */
43669         defaultDialCode: '+852',
43670         
43671         /**
43672          * @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
43673          */
43674         preferedCountries: false,
43675         
43676         getAutoCreate : function()
43677         {
43678             var data = Roo.bootstrap.PhoneInputData();
43679             var align = this.labelAlign || this.parentLabelAlign();
43680             var id = Roo.id();
43681             
43682             this.allCountries = [];
43683             this.dialCodeMapping = [];
43684             
43685             for (var i = 0; i < data.length; i++) {
43686               var c = data[i];
43687               this.allCountries[i] = {
43688                 name: c[0],
43689                 iso2: c[1],
43690                 dialCode: c[2],
43691                 priority: c[3] || 0,
43692                 areaCodes: c[4] || null
43693               };
43694               this.dialCodeMapping[c[2]] = {
43695                   name: c[0],
43696                   iso2: c[1],
43697                   priority: c[3] || 0,
43698                   areaCodes: c[4] || null
43699               };
43700             }
43701             
43702             var cfg = {
43703                 cls: 'form-group',
43704                 cn: []
43705             };
43706             
43707             var input =  {
43708                 tag: 'input',
43709                 id : id,
43710                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43711                 maxlength: this.max_length,
43712                 cls : 'form-control tel-input',
43713                 autocomplete: 'new-password'
43714             };
43715             
43716             var hiddenInput = {
43717                 tag: 'input',
43718                 type: 'hidden',
43719                 cls: 'hidden-tel-input'
43720             };
43721             
43722             if (this.name) {
43723                 hiddenInput.name = this.name;
43724             }
43725             
43726             if (this.disabled) {
43727                 input.disabled = true;
43728             }
43729             
43730             var flag_container = {
43731                 tag: 'div',
43732                 cls: 'flag-box',
43733                 cn: [
43734                     {
43735                         tag: 'div',
43736                         cls: 'flag'
43737                     },
43738                     {
43739                         tag: 'div',
43740                         cls: 'caret'
43741                     }
43742                 ]
43743             };
43744             
43745             var box = {
43746                 tag: 'div',
43747                 cls: this.hasFeedback ? 'has-feedback' : '',
43748                 cn: [
43749                     hiddenInput,
43750                     input,
43751                     {
43752                         tag: 'input',
43753                         cls: 'dial-code-holder',
43754                         disabled: true
43755                     }
43756                 ]
43757             };
43758             
43759             var container = {
43760                 cls: 'roo-select2-container input-group',
43761                 cn: [
43762                     flag_container,
43763                     box
43764                 ]
43765             };
43766             
43767             if (this.fieldLabel.length) {
43768                 var indicator = {
43769                     tag: 'i',
43770                     tooltip: 'This field is required'
43771                 };
43772                 
43773                 var label = {
43774                     tag: 'label',
43775                     'for':  id,
43776                     cls: 'control-label',
43777                     cn: []
43778                 };
43779                 
43780                 var label_text = {
43781                     tag: 'span',
43782                     html: this.fieldLabel
43783                 };
43784                 
43785                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43786                 label.cn = [
43787                     indicator,
43788                     label_text
43789                 ];
43790                 
43791                 if(this.indicatorpos == 'right') {
43792                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43793                     label.cn = [
43794                         label_text,
43795                         indicator
43796                     ];
43797                 }
43798                 
43799                 if(align == 'left') {
43800                     container = {
43801                         tag: 'div',
43802                         cn: [
43803                             container
43804                         ]
43805                     };
43806                     
43807                     if(this.labelWidth > 12){
43808                         label.style = "width: " + this.labelWidth + 'px';
43809                     }
43810                     if(this.labelWidth < 13 && this.labelmd == 0){
43811                         this.labelmd = this.labelWidth;
43812                     }
43813                     if(this.labellg > 0){
43814                         label.cls += ' col-lg-' + this.labellg;
43815                         input.cls += ' col-lg-' + (12 - this.labellg);
43816                     }
43817                     if(this.labelmd > 0){
43818                         label.cls += ' col-md-' + this.labelmd;
43819                         container.cls += ' col-md-' + (12 - this.labelmd);
43820                     }
43821                     if(this.labelsm > 0){
43822                         label.cls += ' col-sm-' + this.labelsm;
43823                         container.cls += ' col-sm-' + (12 - this.labelsm);
43824                     }
43825                     if(this.labelxs > 0){
43826                         label.cls += ' col-xs-' + this.labelxs;
43827                         container.cls += ' col-xs-' + (12 - this.labelxs);
43828                     }
43829                 }
43830             }
43831             
43832             cfg.cn = [
43833                 label,
43834                 container
43835             ];
43836             
43837             var settings = this;
43838             
43839             ['xs','sm','md','lg'].map(function(size){
43840                 if (settings[size]) {
43841                     cfg.cls += ' col-' + size + '-' + settings[size];
43842                 }
43843             });
43844             
43845             this.store = new Roo.data.Store({
43846                 proxy : new Roo.data.MemoryProxy({}),
43847                 reader : new Roo.data.JsonReader({
43848                     fields : [
43849                         {
43850                             'name' : 'name',
43851                             'type' : 'string'
43852                         },
43853                         {
43854                             'name' : 'iso2',
43855                             'type' : 'string'
43856                         },
43857                         {
43858                             'name' : 'dialCode',
43859                             'type' : 'string'
43860                         },
43861                         {
43862                             'name' : 'priority',
43863                             'type' : 'string'
43864                         },
43865                         {
43866                             'name' : 'areaCodes',
43867                             'type' : 'string'
43868                         }
43869                     ]
43870                 })
43871             });
43872             
43873             if(!this.preferedCountries) {
43874                 this.preferedCountries = [
43875                     'hk',
43876                     'gb',
43877                     'us'
43878                 ];
43879             }
43880             
43881             var p = this.preferedCountries.reverse();
43882             
43883             if(p) {
43884                 for (var i = 0; i < p.length; i++) {
43885                     for (var j = 0; j < this.allCountries.length; j++) {
43886                         if(this.allCountries[j].iso2 == p[i]) {
43887                             var t = this.allCountries[j];
43888                             this.allCountries.splice(j,1);
43889                             this.allCountries.unshift(t);
43890                         }
43891                     } 
43892                 }
43893             }
43894             
43895             this.store.proxy.data = {
43896                 success: true,
43897                 data: this.allCountries
43898             };
43899             
43900             return cfg;
43901         },
43902         
43903         initEvents : function()
43904         {
43905             this.createList();
43906             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43907             
43908             this.indicator = this.indicatorEl();
43909             this.flag = this.flagEl();
43910             this.dialCodeHolder = this.dialCodeHolderEl();
43911             
43912             this.trigger = this.el.select('div.flag-box',true).first();
43913             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43914             
43915             var _this = this;
43916             
43917             (function(){
43918                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43919                 _this.list.setWidth(lw);
43920             }).defer(100);
43921             
43922             this.list.on('mouseover', this.onViewOver, this);
43923             this.list.on('mousemove', this.onViewMove, this);
43924             this.inputEl().on("keyup", this.onKeyUp, this);
43925             this.inputEl().on("keypress", this.onKeyPress, this);
43926             
43927             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43928
43929             this.view = new Roo.View(this.list, this.tpl, {
43930                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43931             });
43932             
43933             this.view.on('click', this.onViewClick, this);
43934             this.setValue(this.defaultDialCode);
43935         },
43936         
43937         onTriggerClick : function(e)
43938         {
43939             Roo.log('trigger click');
43940             if(this.disabled){
43941                 return;
43942             }
43943             
43944             if(this.isExpanded()){
43945                 this.collapse();
43946                 this.hasFocus = false;
43947             }else {
43948                 this.store.load({});
43949                 this.hasFocus = true;
43950                 this.expand();
43951             }
43952         },
43953         
43954         isExpanded : function()
43955         {
43956             return this.list.isVisible();
43957         },
43958         
43959         collapse : function()
43960         {
43961             if(!this.isExpanded()){
43962                 return;
43963             }
43964             this.list.hide();
43965             Roo.get(document).un('mousedown', this.collapseIf, this);
43966             Roo.get(document).un('mousewheel', this.collapseIf, this);
43967             this.fireEvent('collapse', this);
43968             this.validate();
43969         },
43970         
43971         expand : function()
43972         {
43973             Roo.log('expand');
43974
43975             if(this.isExpanded() || !this.hasFocus){
43976                 return;
43977             }
43978             
43979             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43980             this.list.setWidth(lw);
43981             
43982             this.list.show();
43983             this.restrictHeight();
43984             
43985             Roo.get(document).on('mousedown', this.collapseIf, this);
43986             Roo.get(document).on('mousewheel', this.collapseIf, this);
43987             
43988             this.fireEvent('expand', this);
43989         },
43990         
43991         restrictHeight : function()
43992         {
43993             this.list.alignTo(this.inputEl(), this.listAlign);
43994             this.list.alignTo(this.inputEl(), this.listAlign);
43995         },
43996         
43997         onViewOver : function(e, t)
43998         {
43999             if(this.inKeyMode){
44000                 return;
44001             }
44002             var item = this.view.findItemFromChild(t);
44003             
44004             if(item){
44005                 var index = this.view.indexOf(item);
44006                 this.select(index, false);
44007             }
44008         },
44009
44010         // private
44011         onViewClick : function(view, doFocus, el, e)
44012         {
44013             var index = this.view.getSelectedIndexes()[0];
44014             
44015             var r = this.store.getAt(index);
44016             
44017             if(r){
44018                 this.onSelect(r, index);
44019             }
44020             if(doFocus !== false && !this.blockFocus){
44021                 this.inputEl().focus();
44022             }
44023         },
44024         
44025         onViewMove : function(e, t)
44026         {
44027             this.inKeyMode = false;
44028         },
44029         
44030         select : function(index, scrollIntoView)
44031         {
44032             this.selectedIndex = index;
44033             this.view.select(index);
44034             if(scrollIntoView !== false){
44035                 var el = this.view.getNode(index);
44036                 if(el){
44037                     this.list.scrollChildIntoView(el, false);
44038                 }
44039             }
44040         },
44041         
44042         createList : function()
44043         {
44044             this.list = Roo.get(document.body).createChild({
44045                 tag: 'ul',
44046                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44047                 style: 'display:none'
44048             });
44049             
44050             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44051         },
44052         
44053         collapseIf : function(e)
44054         {
44055             var in_combo  = e.within(this.el);
44056             var in_list =  e.within(this.list);
44057             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44058             
44059             if (in_combo || in_list || is_list) {
44060                 return;
44061             }
44062             this.collapse();
44063         },
44064         
44065         onSelect : function(record, index)
44066         {
44067             if(this.fireEvent('beforeselect', this, record, index) !== false){
44068                 
44069                 this.setFlagClass(record.data.iso2);
44070                 this.setDialCode(record.data.dialCode);
44071                 this.hasFocus = false;
44072                 this.collapse();
44073                 this.fireEvent('select', this, record, index);
44074             }
44075         },
44076         
44077         flagEl : function()
44078         {
44079             var flag = this.el.select('div.flag',true).first();
44080             if(!flag){
44081                 return false;
44082             }
44083             return flag;
44084         },
44085         
44086         dialCodeHolderEl : function()
44087         {
44088             var d = this.el.select('input.dial-code-holder',true).first();
44089             if(!d){
44090                 return false;
44091             }
44092             return d;
44093         },
44094         
44095         setDialCode : function(v)
44096         {
44097             this.dialCodeHolder.dom.value = '+'+v;
44098         },
44099         
44100         setFlagClass : function(n)
44101         {
44102             this.flag.dom.className = 'flag '+n;
44103         },
44104         
44105         getValue : function()
44106         {
44107             var v = this.inputEl().getValue();
44108             if(this.dialCodeHolder) {
44109                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44110             }
44111             return v;
44112         },
44113         
44114         setValue : function(v)
44115         {
44116             var d = this.getDialCode(v);
44117             
44118             //invalid dial code
44119             if(v.length == 0 || !d || d.length == 0) {
44120                 if(this.rendered){
44121                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44122                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44123                 }
44124                 return;
44125             }
44126             
44127             //valid dial code
44128             this.setFlagClass(this.dialCodeMapping[d].iso2);
44129             this.setDialCode(d);
44130             this.inputEl().dom.value = v.replace('+'+d,'');
44131             this.hiddenEl().dom.value = this.getValue();
44132             
44133             this.validate();
44134         },
44135         
44136         getDialCode : function(v)
44137         {
44138             v = v ||  '';
44139             
44140             if (v.length == 0) {
44141                 return this.dialCodeHolder.dom.value;
44142             }
44143             
44144             var dialCode = "";
44145             if (v.charAt(0) != "+") {
44146                 return false;
44147             }
44148             var numericChars = "";
44149             for (var i = 1; i < v.length; i++) {
44150               var c = v.charAt(i);
44151               if (!isNaN(c)) {
44152                 numericChars += c;
44153                 if (this.dialCodeMapping[numericChars]) {
44154                   dialCode = v.substr(1, i);
44155                 }
44156                 if (numericChars.length == 4) {
44157                   break;
44158                 }
44159               }
44160             }
44161             return dialCode;
44162         },
44163         
44164         reset : function()
44165         {
44166             this.setValue(this.defaultDialCode);
44167             this.validate();
44168         },
44169         
44170         hiddenEl : function()
44171         {
44172             return this.el.select('input.hidden-tel-input',true).first();
44173         },
44174         
44175         // after setting val
44176         onKeyUp : function(e){
44177             this.setValue(this.getValue());
44178         },
44179         
44180         onKeyPress : function(e){
44181             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44182                 e.stopEvent();
44183             }
44184         }
44185         
44186 });
44187 /**
44188  * @class Roo.bootstrap.MoneyField
44189  * @extends Roo.bootstrap.ComboBox
44190  * Bootstrap MoneyField class
44191  * 
44192  * @constructor
44193  * Create a new MoneyField.
44194  * @param {Object} config Configuration options
44195  */
44196
44197 Roo.bootstrap.MoneyField = function(config) {
44198     
44199     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44200     
44201 };
44202
44203 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44204     
44205     /**
44206      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44207      */
44208     allowDecimals : true,
44209     /**
44210      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44211      */
44212     decimalSeparator : ".",
44213     /**
44214      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44215      */
44216     decimalPrecision : 0,
44217     /**
44218      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44219      */
44220     allowNegative : true,
44221     /**
44222      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44223      */
44224     allowZero: true,
44225     /**
44226      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44227      */
44228     minValue : Number.NEGATIVE_INFINITY,
44229     /**
44230      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44231      */
44232     maxValue : Number.MAX_VALUE,
44233     /**
44234      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44235      */
44236     minText : "The minimum value for this field is {0}",
44237     /**
44238      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44239      */
44240     maxText : "The maximum value for this field is {0}",
44241     /**
44242      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44243      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44244      */
44245     nanText : "{0} is not a valid number",
44246     /**
44247      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44248      */
44249     castInt : true,
44250     /**
44251      * @cfg {String} defaults currency of the MoneyField
44252      * value should be in lkey
44253      */
44254     defaultCurrency : false,
44255     /**
44256      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44257      */
44258     thousandsDelimiter : false,
44259     /**
44260      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44261      */
44262     max_length: false,
44263     
44264     inputlg : 9,
44265     inputmd : 9,
44266     inputsm : 9,
44267     inputxs : 6,
44268     
44269     store : false,
44270     
44271     getAutoCreate : function()
44272     {
44273         var align = this.labelAlign || this.parentLabelAlign();
44274         
44275         var id = Roo.id();
44276
44277         var cfg = {
44278             cls: 'form-group',
44279             cn: []
44280         };
44281
44282         var input =  {
44283             tag: 'input',
44284             id : id,
44285             cls : 'form-control roo-money-amount-input',
44286             autocomplete: 'new-password'
44287         };
44288         
44289         var hiddenInput = {
44290             tag: 'input',
44291             type: 'hidden',
44292             id: Roo.id(),
44293             cls: 'hidden-number-input'
44294         };
44295         
44296         if(this.max_length) {
44297             input.maxlength = this.max_length; 
44298         }
44299         
44300         if (this.name) {
44301             hiddenInput.name = this.name;
44302         }
44303
44304         if (this.disabled) {
44305             input.disabled = true;
44306         }
44307
44308         var clg = 12 - this.inputlg;
44309         var cmd = 12 - this.inputmd;
44310         var csm = 12 - this.inputsm;
44311         var cxs = 12 - this.inputxs;
44312         
44313         var container = {
44314             tag : 'div',
44315             cls : 'row roo-money-field',
44316             cn : [
44317                 {
44318                     tag : 'div',
44319                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44320                     cn : [
44321                         {
44322                             tag : 'div',
44323                             cls: 'roo-select2-container input-group',
44324                             cn: [
44325                                 {
44326                                     tag : 'input',
44327                                     cls : 'form-control roo-money-currency-input',
44328                                     autocomplete: 'new-password',
44329                                     readOnly : 1,
44330                                     name : this.currencyName
44331                                 },
44332                                 {
44333                                     tag :'span',
44334                                     cls : 'input-group-addon',
44335                                     cn : [
44336                                         {
44337                                             tag: 'span',
44338                                             cls: 'caret'
44339                                         }
44340                                     ]
44341                                 }
44342                             ]
44343                         }
44344                     ]
44345                 },
44346                 {
44347                     tag : 'div',
44348                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44349                     cn : [
44350                         {
44351                             tag: 'div',
44352                             cls: this.hasFeedback ? 'has-feedback' : '',
44353                             cn: [
44354                                 input
44355                             ]
44356                         }
44357                     ]
44358                 }
44359             ]
44360             
44361         };
44362         
44363         if (this.fieldLabel.length) {
44364             var indicator = {
44365                 tag: 'i',
44366                 tooltip: 'This field is required'
44367             };
44368
44369             var label = {
44370                 tag: 'label',
44371                 'for':  id,
44372                 cls: 'control-label',
44373                 cn: []
44374             };
44375
44376             var label_text = {
44377                 tag: 'span',
44378                 html: this.fieldLabel
44379             };
44380
44381             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44382             label.cn = [
44383                 indicator,
44384                 label_text
44385             ];
44386
44387             if(this.indicatorpos == 'right') {
44388                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44389                 label.cn = [
44390                     label_text,
44391                     indicator
44392                 ];
44393             }
44394
44395             if(align == 'left') {
44396                 container = {
44397                     tag: 'div',
44398                     cn: [
44399                         container
44400                     ]
44401                 };
44402
44403                 if(this.labelWidth > 12){
44404                     label.style = "width: " + this.labelWidth + 'px';
44405                 }
44406                 if(this.labelWidth < 13 && this.labelmd == 0){
44407                     this.labelmd = this.labelWidth;
44408                 }
44409                 if(this.labellg > 0){
44410                     label.cls += ' col-lg-' + this.labellg;
44411                     input.cls += ' col-lg-' + (12 - this.labellg);
44412                 }
44413                 if(this.labelmd > 0){
44414                     label.cls += ' col-md-' + this.labelmd;
44415                     container.cls += ' col-md-' + (12 - this.labelmd);
44416                 }
44417                 if(this.labelsm > 0){
44418                     label.cls += ' col-sm-' + this.labelsm;
44419                     container.cls += ' col-sm-' + (12 - this.labelsm);
44420                 }
44421                 if(this.labelxs > 0){
44422                     label.cls += ' col-xs-' + this.labelxs;
44423                     container.cls += ' col-xs-' + (12 - this.labelxs);
44424                 }
44425             }
44426         }
44427
44428         cfg.cn = [
44429             label,
44430             container,
44431             hiddenInput
44432         ];
44433         
44434         var settings = this;
44435
44436         ['xs','sm','md','lg'].map(function(size){
44437             if (settings[size]) {
44438                 cfg.cls += ' col-' + size + '-' + settings[size];
44439             }
44440         });
44441         
44442         return cfg;
44443     },
44444     
44445     initEvents : function()
44446     {
44447         this.indicator = this.indicatorEl();
44448         
44449         this.initCurrencyEvent();
44450         
44451         this.initNumberEvent();
44452     },
44453     
44454     initCurrencyEvent : function()
44455     {
44456         if (!this.store) {
44457             throw "can not find store for combo";
44458         }
44459         
44460         this.store = Roo.factory(this.store, Roo.data);
44461         this.store.parent = this;
44462         
44463         this.createList();
44464         
44465         this.triggerEl = this.el.select('.input-group-addon', true).first();
44466         
44467         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44468         
44469         var _this = this;
44470         
44471         (function(){
44472             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44473             _this.list.setWidth(lw);
44474         }).defer(100);
44475         
44476         this.list.on('mouseover', this.onViewOver, this);
44477         this.list.on('mousemove', this.onViewMove, this);
44478         this.list.on('scroll', this.onViewScroll, this);
44479         
44480         if(!this.tpl){
44481             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44482         }
44483         
44484         this.view = new Roo.View(this.list, this.tpl, {
44485             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44486         });
44487         
44488         this.view.on('click', this.onViewClick, this);
44489         
44490         this.store.on('beforeload', this.onBeforeLoad, this);
44491         this.store.on('load', this.onLoad, this);
44492         this.store.on('loadexception', this.onLoadException, this);
44493         
44494         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44495             "up" : function(e){
44496                 this.inKeyMode = true;
44497                 this.selectPrev();
44498             },
44499
44500             "down" : function(e){
44501                 if(!this.isExpanded()){
44502                     this.onTriggerClick();
44503                 }else{
44504                     this.inKeyMode = true;
44505                     this.selectNext();
44506                 }
44507             },
44508
44509             "enter" : function(e){
44510                 this.collapse();
44511                 
44512                 if(this.fireEvent("specialkey", this, e)){
44513                     this.onViewClick(false);
44514                 }
44515                 
44516                 return true;
44517             },
44518
44519             "esc" : function(e){
44520                 this.collapse();
44521             },
44522
44523             "tab" : function(e){
44524                 this.collapse();
44525                 
44526                 if(this.fireEvent("specialkey", this, e)){
44527                     this.onViewClick(false);
44528                 }
44529                 
44530                 return true;
44531             },
44532
44533             scope : this,
44534
44535             doRelay : function(foo, bar, hname){
44536                 if(hname == 'down' || this.scope.isExpanded()){
44537                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44538                 }
44539                 return true;
44540             },
44541
44542             forceKeyDown: true
44543         });
44544         
44545         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44546         
44547     },
44548     
44549     initNumberEvent : function(e)
44550     {
44551         this.inputEl().on("keydown" , this.fireKey,  this);
44552         this.inputEl().on("focus", this.onFocus,  this);
44553         this.inputEl().on("blur", this.onBlur,  this);
44554         
44555         this.inputEl().relayEvent('keyup', this);
44556         
44557         if(this.indicator){
44558             this.indicator.addClass('invisible');
44559         }
44560  
44561         this.originalValue = this.getValue();
44562         
44563         if(this.validationEvent == 'keyup'){
44564             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44565             this.inputEl().on('keyup', this.filterValidation, this);
44566         }
44567         else if(this.validationEvent !== false){
44568             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44569         }
44570         
44571         if(this.selectOnFocus){
44572             this.on("focus", this.preFocus, this);
44573             
44574         }
44575         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44576             this.inputEl().on("keypress", this.filterKeys, this);
44577         } else {
44578             this.inputEl().relayEvent('keypress', this);
44579         }
44580         
44581         var allowed = "0123456789";
44582         
44583         if(this.allowDecimals){
44584             allowed += this.decimalSeparator;
44585         }
44586         
44587         if(this.allowNegative){
44588             allowed += "-";
44589         }
44590         
44591         if(this.thousandsDelimiter) {
44592             allowed += ",";
44593         }
44594         
44595         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44596         
44597         var keyPress = function(e){
44598             
44599             var k = e.getKey();
44600             
44601             var c = e.getCharCode();
44602             
44603             if(
44604                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44605                     allowed.indexOf(String.fromCharCode(c)) === -1
44606             ){
44607                 e.stopEvent();
44608                 return;
44609             }
44610             
44611             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44612                 return;
44613             }
44614             
44615             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44616                 e.stopEvent();
44617             }
44618         };
44619         
44620         this.inputEl().on("keypress", keyPress, this);
44621         
44622     },
44623     
44624     onTriggerClick : function(e)
44625     {   
44626         if(this.disabled){
44627             return;
44628         }
44629         
44630         this.page = 0;
44631         this.loadNext = false;
44632         
44633         if(this.isExpanded()){
44634             this.collapse();
44635             return;
44636         }
44637         
44638         this.hasFocus = true;
44639         
44640         if(this.triggerAction == 'all') {
44641             this.doQuery(this.allQuery, true);
44642             return;
44643         }
44644         
44645         this.doQuery(this.getRawValue());
44646     },
44647     
44648     getCurrency : function()
44649     {   
44650         var v = this.currencyEl().getValue();
44651         
44652         return v;
44653     },
44654     
44655     restrictHeight : function()
44656     {
44657         this.list.alignTo(this.currencyEl(), this.listAlign);
44658         this.list.alignTo(this.currencyEl(), this.listAlign);
44659     },
44660     
44661     onViewClick : function(view, doFocus, el, e)
44662     {
44663         var index = this.view.getSelectedIndexes()[0];
44664         
44665         var r = this.store.getAt(index);
44666         
44667         if(r){
44668             this.onSelect(r, index);
44669         }
44670     },
44671     
44672     onSelect : function(record, index){
44673         
44674         if(this.fireEvent('beforeselect', this, record, index) !== false){
44675         
44676             this.setFromCurrencyData(index > -1 ? record.data : false);
44677             
44678             this.collapse();
44679             
44680             this.fireEvent('select', this, record, index);
44681         }
44682     },
44683     
44684     setFromCurrencyData : function(o)
44685     {
44686         var currency = '';
44687         
44688         this.lastCurrency = o;
44689         
44690         if (this.currencyField) {
44691             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44692         } else {
44693             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44694         }
44695         
44696         this.lastSelectionText = currency;
44697         
44698         //setting default currency
44699         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44700             this.setCurrency(this.defaultCurrency);
44701             return;
44702         }
44703         
44704         this.setCurrency(currency);
44705     },
44706     
44707     setFromData : function(o)
44708     {
44709         var c = {};
44710         
44711         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44712         
44713         this.setFromCurrencyData(c);
44714         
44715         var value = '';
44716         
44717         if (this.name) {
44718             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44719         } else {
44720             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44721         }
44722         
44723         this.setValue(value);
44724         
44725     },
44726     
44727     setCurrency : function(v)
44728     {   
44729         this.currencyValue = v;
44730         
44731         if(this.rendered){
44732             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44733             this.validate();
44734         }
44735     },
44736     
44737     setValue : function(v)
44738     {
44739         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44740         
44741         this.value = v;
44742         
44743         if(this.rendered){
44744             
44745             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44746             
44747             this.inputEl().dom.value = (v == '') ? '' :
44748                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44749             
44750             if(!this.allowZero && v === '0') {
44751                 this.hiddenEl().dom.value = '';
44752                 this.inputEl().dom.value = '';
44753             }
44754             
44755             this.validate();
44756         }
44757     },
44758     
44759     getRawValue : function()
44760     {
44761         var v = this.inputEl().getValue();
44762         
44763         return v;
44764     },
44765     
44766     getValue : function()
44767     {
44768         return this.fixPrecision(this.parseValue(this.getRawValue()));
44769     },
44770     
44771     parseValue : function(value)
44772     {
44773         if(this.thousandsDelimiter) {
44774             value += "";
44775             r = new RegExp(",", "g");
44776             value = value.replace(r, "");
44777         }
44778         
44779         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44780         return isNaN(value) ? '' : value;
44781         
44782     },
44783     
44784     fixPrecision : function(value)
44785     {
44786         if(this.thousandsDelimiter) {
44787             value += "";
44788             r = new RegExp(",", "g");
44789             value = value.replace(r, "");
44790         }
44791         
44792         var nan = isNaN(value);
44793         
44794         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44795             return nan ? '' : value;
44796         }
44797         return parseFloat(value).toFixed(this.decimalPrecision);
44798     },
44799     
44800     decimalPrecisionFcn : function(v)
44801     {
44802         return Math.floor(v);
44803     },
44804     
44805     validateValue : function(value)
44806     {
44807         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44808             return false;
44809         }
44810         
44811         var num = this.parseValue(value);
44812         
44813         if(isNaN(num)){
44814             this.markInvalid(String.format(this.nanText, value));
44815             return false;
44816         }
44817         
44818         if(num < this.minValue){
44819             this.markInvalid(String.format(this.minText, this.minValue));
44820             return false;
44821         }
44822         
44823         if(num > this.maxValue){
44824             this.markInvalid(String.format(this.maxText, this.maxValue));
44825             return false;
44826         }
44827         
44828         return true;
44829     },
44830     
44831     validate : function()
44832     {
44833         if(this.disabled || this.allowBlank){
44834             this.markValid();
44835             return true;
44836         }
44837         
44838         var currency = this.getCurrency();
44839         
44840         if(this.validateValue(this.getRawValue()) && currency.length){
44841             this.markValid();
44842             return true;
44843         }
44844         
44845         this.markInvalid();
44846         return false;
44847     },
44848     
44849     getName: function()
44850     {
44851         return this.name;
44852     },
44853     
44854     beforeBlur : function()
44855     {
44856         if(!this.castInt){
44857             return;
44858         }
44859         
44860         var v = this.parseValue(this.getRawValue());
44861         
44862         if(v || v == 0){
44863             this.setValue(v);
44864         }
44865     },
44866     
44867     onBlur : function()
44868     {
44869         this.beforeBlur();
44870         
44871         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44872             //this.el.removeClass(this.focusClass);
44873         }
44874         
44875         this.hasFocus = false;
44876         
44877         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44878             this.validate();
44879         }
44880         
44881         var v = this.getValue();
44882         
44883         if(String(v) !== String(this.startValue)){
44884             this.fireEvent('change', this, v, this.startValue);
44885         }
44886         
44887         this.fireEvent("blur", this);
44888     },
44889     
44890     inputEl : function()
44891     {
44892         return this.el.select('.roo-money-amount-input', true).first();
44893     },
44894     
44895     currencyEl : function()
44896     {
44897         return this.el.select('.roo-money-currency-input', true).first();
44898     },
44899     
44900     hiddenEl : function()
44901     {
44902         return this.el.select('input.hidden-number-input',true).first();
44903     }
44904     
44905 });/**
44906  * @class Roo.bootstrap.BezierSignature
44907  * @extends Roo.bootstrap.Component
44908  * Bootstrap BezierSignature class
44909  * This script refer to:
44910  *    Title: Signature Pad
44911  *    Author: szimek
44912  *    Availability: https://github.com/szimek/signature_pad
44913  *
44914  * @constructor
44915  * Create a new BezierSignature
44916  * @param {Object} config The config object
44917  */
44918
44919 Roo.bootstrap.BezierSignature = function(config){
44920     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44921     this.addEvents({
44922         "resize" : true
44923     });
44924 };
44925
44926 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44927 {
44928      
44929     curve_data: [],
44930     
44931     is_empty: true,
44932     
44933     mouse_btn_down: true,
44934     
44935     /**
44936      * @cfg {int} canvas height
44937      */
44938     canvas_height: '200px',
44939     
44940     /**
44941      * @cfg {float|function} Radius of a single dot.
44942      */ 
44943     dot_size: false,
44944     
44945     /**
44946      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44947      */
44948     min_width: 0.5,
44949     
44950     /**
44951      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44952      */
44953     max_width: 2.5,
44954     
44955     /**
44956      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44957      */
44958     throttle: 16,
44959     
44960     /**
44961      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44962      */
44963     min_distance: 5,
44964     
44965     /**
44966      * @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.
44967      */
44968     bg_color: 'rgba(0, 0, 0, 0)',
44969     
44970     /**
44971      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44972      */
44973     dot_color: 'black',
44974     
44975     /**
44976      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44977      */ 
44978     velocity_filter_weight: 0.7,
44979     
44980     /**
44981      * @cfg {function} Callback when stroke begin. 
44982      */
44983     onBegin: false,
44984     
44985     /**
44986      * @cfg {function} Callback when stroke end.
44987      */
44988     onEnd: false,
44989     
44990     getAutoCreate : function()
44991     {
44992         var cls = 'roo-signature column';
44993         
44994         if(this.cls){
44995             cls += ' ' + this.cls;
44996         }
44997         
44998         var col_sizes = [
44999             'lg',
45000             'md',
45001             'sm',
45002             'xs'
45003         ];
45004         
45005         for(var i = 0; i < col_sizes.length; i++) {
45006             if(this[col_sizes[i]]) {
45007                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45008             }
45009         }
45010         
45011         var cfg = {
45012             tag: 'div',
45013             cls: cls,
45014             cn: [
45015                 {
45016                     tag: 'div',
45017                     cls: 'roo-signature-body',
45018                     cn: [
45019                         {
45020                             tag: 'canvas',
45021                             cls: 'roo-signature-body-canvas',
45022                             height: this.canvas_height,
45023                             width: this.canvas_width
45024                         }
45025                     ]
45026                 },
45027                 {
45028                     tag: 'input',
45029                     type: 'file',
45030                     style: 'display: none'
45031                 }
45032             ]
45033         };
45034         
45035         return cfg;
45036     },
45037     
45038     initEvents: function() 
45039     {
45040         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45041         
45042         var canvas = this.canvasEl();
45043         
45044         // mouse && touch event swapping...
45045         canvas.dom.style.touchAction = 'none';
45046         canvas.dom.style.msTouchAction = 'none';
45047         
45048         this.mouse_btn_down = false;
45049         canvas.on('mousedown', this._handleMouseDown, this);
45050         canvas.on('mousemove', this._handleMouseMove, this);
45051         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45052         
45053         if (window.PointerEvent) {
45054             canvas.on('pointerdown', this._handleMouseDown, this);
45055             canvas.on('pointermove', this._handleMouseMove, this);
45056             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45057         }
45058         
45059         if ('ontouchstart' in window) {
45060             canvas.on('touchstart', this._handleTouchStart, this);
45061             canvas.on('touchmove', this._handleTouchMove, this);
45062             canvas.on('touchend', this._handleTouchEnd, this);
45063         }
45064         
45065         Roo.EventManager.onWindowResize(this.resize, this, true);
45066         
45067         // file input event
45068         this.fileEl().on('change', this.uploadImage, this);
45069         
45070         this.clear();
45071         
45072         this.resize();
45073     },
45074     
45075     resize: function(){
45076         
45077         var canvas = this.canvasEl().dom;
45078         var ctx = this.canvasElCtx();
45079         var img_data = false;
45080         
45081         if(canvas.width > 0) {
45082             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45083         }
45084         // setting canvas width will clean img data
45085         canvas.width = 0;
45086         
45087         var style = window.getComputedStyle ? 
45088             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45089             
45090         var padding_left = parseInt(style.paddingLeft) || 0;
45091         var padding_right = parseInt(style.paddingRight) || 0;
45092         
45093         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45094         
45095         if(img_data) {
45096             ctx.putImageData(img_data, 0, 0);
45097         }
45098     },
45099     
45100     _handleMouseDown: function(e)
45101     {
45102         if (e.browserEvent.which === 1) {
45103             this.mouse_btn_down = true;
45104             this.strokeBegin(e);
45105         }
45106     },
45107     
45108     _handleMouseMove: function (e)
45109     {
45110         if (this.mouse_btn_down) {
45111             this.strokeMoveUpdate(e);
45112         }
45113     },
45114     
45115     _handleMouseUp: function (e)
45116     {
45117         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45118             this.mouse_btn_down = false;
45119             this.strokeEnd(e);
45120         }
45121     },
45122     
45123     _handleTouchStart: function (e) {
45124         
45125         e.preventDefault();
45126         if (e.browserEvent.targetTouches.length === 1) {
45127             // var touch = e.browserEvent.changedTouches[0];
45128             // this.strokeBegin(touch);
45129             
45130              this.strokeBegin(e); // assume e catching the correct xy...
45131         }
45132     },
45133     
45134     _handleTouchMove: function (e) {
45135         e.preventDefault();
45136         // var touch = event.targetTouches[0];
45137         // _this._strokeMoveUpdate(touch);
45138         this.strokeMoveUpdate(e);
45139     },
45140     
45141     _handleTouchEnd: function (e) {
45142         var wasCanvasTouched = e.target === this.canvasEl().dom;
45143         if (wasCanvasTouched) {
45144             e.preventDefault();
45145             // var touch = event.changedTouches[0];
45146             // _this._strokeEnd(touch);
45147             this.strokeEnd(e);
45148         }
45149     },
45150     
45151     reset: function () {
45152         this._lastPoints = [];
45153         this._lastVelocity = 0;
45154         this._lastWidth = (this.min_width + this.max_width) / 2;
45155         this.canvasElCtx().fillStyle = this.dot_color;
45156     },
45157     
45158     strokeMoveUpdate: function(e)
45159     {
45160         this.strokeUpdate(e);
45161         
45162         if (this.throttle) {
45163             this.throttleStroke(this.strokeUpdate, this.throttle);
45164         }
45165         else {
45166             this.strokeUpdate(e);
45167         }
45168     },
45169     
45170     strokeBegin: function(e)
45171     {
45172         var newPointGroup = {
45173             color: this.dot_color,
45174             points: []
45175         };
45176         
45177         if (typeof this.onBegin === 'function') {
45178             this.onBegin(e);
45179         }
45180         
45181         this.curve_data.push(newPointGroup);
45182         this.reset();
45183         this.strokeUpdate(e);
45184     },
45185     
45186     strokeUpdate: function(e)
45187     {
45188         var rect = this.canvasEl().dom.getBoundingClientRect();
45189         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45190         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45191         var lastPoints = lastPointGroup.points;
45192         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45193         var isLastPointTooClose = lastPoint
45194             ? point.distanceTo(lastPoint) <= this.min_distance
45195             : false;
45196         var color = lastPointGroup.color;
45197         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45198             var curve = this.addPoint(point);
45199             if (!lastPoint) {
45200                 this.drawDot({color: color, point: point});
45201             }
45202             else if (curve) {
45203                 this.drawCurve({color: color, curve: curve});
45204             }
45205             lastPoints.push({
45206                 time: point.time,
45207                 x: point.x,
45208                 y: point.y
45209             });
45210         }
45211     },
45212     
45213     strokeEnd: function(e)
45214     {
45215         this.strokeUpdate(e);
45216         if (typeof this.onEnd === 'function') {
45217             this.onEnd(e);
45218         }
45219     },
45220     
45221     addPoint:  function (point) {
45222         var _lastPoints = this._lastPoints;
45223         _lastPoints.push(point);
45224         if (_lastPoints.length > 2) {
45225             if (_lastPoints.length === 3) {
45226                 _lastPoints.unshift(_lastPoints[0]);
45227             }
45228             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45229             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45230             _lastPoints.shift();
45231             return curve;
45232         }
45233         return null;
45234     },
45235     
45236     calculateCurveWidths: function (startPoint, endPoint) {
45237         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45238             (1 - this.velocity_filter_weight) * this._lastVelocity;
45239
45240         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45241         var widths = {
45242             end: newWidth,
45243             start: this._lastWidth
45244         };
45245         
45246         this._lastVelocity = velocity;
45247         this._lastWidth = newWidth;
45248         return widths;
45249     },
45250     
45251     drawDot: function (_a) {
45252         var color = _a.color, point = _a.point;
45253         var ctx = this.canvasElCtx();
45254         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45255         ctx.beginPath();
45256         this.drawCurveSegment(point.x, point.y, width);
45257         ctx.closePath();
45258         ctx.fillStyle = color;
45259         ctx.fill();
45260     },
45261     
45262     drawCurve: function (_a) {
45263         var color = _a.color, curve = _a.curve;
45264         var ctx = this.canvasElCtx();
45265         var widthDelta = curve.endWidth - curve.startWidth;
45266         var drawSteps = Math.floor(curve.length()) * 2;
45267         ctx.beginPath();
45268         ctx.fillStyle = color;
45269         for (var i = 0; i < drawSteps; i += 1) {
45270         var t = i / drawSteps;
45271         var tt = t * t;
45272         var ttt = tt * t;
45273         var u = 1 - t;
45274         var uu = u * u;
45275         var uuu = uu * u;
45276         var x = uuu * curve.startPoint.x;
45277         x += 3 * uu * t * curve.control1.x;
45278         x += 3 * u * tt * curve.control2.x;
45279         x += ttt * curve.endPoint.x;
45280         var y = uuu * curve.startPoint.y;
45281         y += 3 * uu * t * curve.control1.y;
45282         y += 3 * u * tt * curve.control2.y;
45283         y += ttt * curve.endPoint.y;
45284         var width = curve.startWidth + ttt * widthDelta;
45285         this.drawCurveSegment(x, y, width);
45286         }
45287         ctx.closePath();
45288         ctx.fill();
45289     },
45290     
45291     drawCurveSegment: function (x, y, width) {
45292         var ctx = this.canvasElCtx();
45293         ctx.moveTo(x, y);
45294         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45295         this.is_empty = false;
45296     },
45297     
45298     clear: function()
45299     {
45300         var ctx = this.canvasElCtx();
45301         var canvas = this.canvasEl().dom;
45302         ctx.fillStyle = this.bg_color;
45303         ctx.clearRect(0, 0, canvas.width, canvas.height);
45304         ctx.fillRect(0, 0, canvas.width, canvas.height);
45305         this.curve_data = [];
45306         this.reset();
45307         this.is_empty = true;
45308     },
45309     
45310     fileEl: function()
45311     {
45312         return  this.el.select('input',true).first();
45313     },
45314     
45315     canvasEl: function()
45316     {
45317         return this.el.select('canvas',true).first();
45318     },
45319     
45320     canvasElCtx: function()
45321     {
45322         return this.el.select('canvas',true).first().dom.getContext('2d');
45323     },
45324     
45325     getImage: function(type)
45326     {
45327         if(this.is_empty) {
45328             return false;
45329         }
45330         
45331         // encryption ?
45332         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45333     },
45334     
45335     drawFromImage: function(img_src)
45336     {
45337         var img = new Image();
45338         
45339         img.onload = function(){
45340             this.canvasElCtx().drawImage(img, 0, 0);
45341         }.bind(this);
45342         
45343         img.src = img_src;
45344         
45345         this.is_empty = false;
45346     },
45347     
45348     selectImage: function()
45349     {
45350         this.fileEl().dom.click();
45351     },
45352     
45353     uploadImage: function(e)
45354     {
45355         var reader = new FileReader();
45356         
45357         reader.onload = function(e){
45358             var img = new Image();
45359             img.onload = function(){
45360                 this.reset();
45361                 this.canvasElCtx().drawImage(img, 0, 0);
45362             }.bind(this);
45363             img.src = e.target.result;
45364         }.bind(this);
45365         
45366         reader.readAsDataURL(e.target.files[0]);
45367     },
45368     
45369     // Bezier Point Constructor
45370     Point: (function () {
45371         function Point(x, y, time) {
45372             this.x = x;
45373             this.y = y;
45374             this.time = time || Date.now();
45375         }
45376         Point.prototype.distanceTo = function (start) {
45377             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45378         };
45379         Point.prototype.equals = function (other) {
45380             return this.x === other.x && this.y === other.y && this.time === other.time;
45381         };
45382         Point.prototype.velocityFrom = function (start) {
45383             return this.time !== start.time
45384             ? this.distanceTo(start) / (this.time - start.time)
45385             : 0;
45386         };
45387         return Point;
45388     }()),
45389     
45390     
45391     // Bezier Constructor
45392     Bezier: (function () {
45393         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45394             this.startPoint = startPoint;
45395             this.control2 = control2;
45396             this.control1 = control1;
45397             this.endPoint = endPoint;
45398             this.startWidth = startWidth;
45399             this.endWidth = endWidth;
45400         }
45401         Bezier.fromPoints = function (points, widths, scope) {
45402             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45403             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45404             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45405         };
45406         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45407             var dx1 = s1.x - s2.x;
45408             var dy1 = s1.y - s2.y;
45409             var dx2 = s2.x - s3.x;
45410             var dy2 = s2.y - s3.y;
45411             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45412             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45413             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45414             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45415             var dxm = m1.x - m2.x;
45416             var dym = m1.y - m2.y;
45417             var k = l2 / (l1 + l2);
45418             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45419             var tx = s2.x - cm.x;
45420             var ty = s2.y - cm.y;
45421             return {
45422                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45423                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45424             };
45425         };
45426         Bezier.prototype.length = function () {
45427             var steps = 10;
45428             var length = 0;
45429             var px;
45430             var py;
45431             for (var i = 0; i <= steps; i += 1) {
45432                 var t = i / steps;
45433                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45434                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45435                 if (i > 0) {
45436                     var xdiff = cx - px;
45437                     var ydiff = cy - py;
45438                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45439                 }
45440                 px = cx;
45441                 py = cy;
45442             }
45443             return length;
45444         };
45445         Bezier.prototype.point = function (t, start, c1, c2, end) {
45446             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45447             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45448             + (3.0 * c2 * (1.0 - t) * t * t)
45449             + (end * t * t * t);
45450         };
45451         return Bezier;
45452     }()),
45453     
45454     throttleStroke: function(fn, wait) {
45455       if (wait === void 0) { wait = 250; }
45456       var previous = 0;
45457       var timeout = null;
45458       var result;
45459       var storedContext;
45460       var storedArgs;
45461       var later = function () {
45462           previous = Date.now();
45463           timeout = null;
45464           result = fn.apply(storedContext, storedArgs);
45465           if (!timeout) {
45466               storedContext = null;
45467               storedArgs = [];
45468           }
45469       };
45470       return function wrapper() {
45471           var args = [];
45472           for (var _i = 0; _i < arguments.length; _i++) {
45473               args[_i] = arguments[_i];
45474           }
45475           var now = Date.now();
45476           var remaining = wait - (now - previous);
45477           storedContext = this;
45478           storedArgs = args;
45479           if (remaining <= 0 || remaining > wait) {
45480               if (timeout) {
45481                   clearTimeout(timeout);
45482                   timeout = null;
45483               }
45484               previous = now;
45485               result = fn.apply(storedContext, storedArgs);
45486               if (!timeout) {
45487                   storedContext = null;
45488                   storedArgs = [];
45489               }
45490           }
45491           else if (!timeout) {
45492               timeout = window.setTimeout(later, remaining);
45493           }
45494           return result;
45495       };
45496   }
45497   
45498 });
45499
45500  
45501
45502