roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * @builder-top
852  * @children Roo.bootstrap.Component
853  * @parent none
854  * Bootstrap Body class
855  *
856  * @constructor
857  * Create a new body
858  * @param {Object} config The config object
859  */
860
861 Roo.bootstrap.Body = function(config){
862
863     config = config || {};
864
865     Roo.bootstrap.Body.superclass.constructor.call(this, config);
866     this.el = Roo.get(config.el ? config.el : document.body );
867     if (this.cls && this.cls.length) {
868         Roo.get(document.body).addClass(this.cls);
869     }
870 };
871
872 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
873
874     is_body : true,// just to make sure it's constructed?
875
876         autoCreate : {
877         cls: 'container'
878     },
879     onRender : function(ct, position)
880     {
881        /* Roo.log("Roo.bootstrap.Body - onRender");
882         if (this.cls && this.cls.length) {
883             Roo.get(document.body).addClass(this.cls);
884         }
885         // style??? xttr???
886         */
887     }
888
889
890
891
892 });
893 /*
894  * - LGPL
895  *
896  * button group
897  * 
898  */
899
900
901 /**
902  * @class Roo.bootstrap.ButtonGroup
903  * @extends Roo.bootstrap.Component
904  * Bootstrap ButtonGroup class
905  * @cfg {String} size lg | sm | xs (default empty normal)
906  * @cfg {String} align vertical | justified  (default none)
907  * @cfg {String} direction up | down (default down)
908  * @cfg {Boolean} toolbar false | true
909  * @cfg {Boolean} btn true | false
910  * 
911  * 
912  * @constructor
913  * Create a new Input
914  * @param {Object} config The config object
915  */
916
917 Roo.bootstrap.ButtonGroup = function(config){
918     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
919 };
920
921 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
922     
923     size: '',
924     align: '',
925     direction: '',
926     toolbar: false,
927     btn: true,
928
929     getAutoCreate : function(){
930         var cfg = {
931             cls: 'btn-group',
932             html : null
933         };
934         
935         cfg.html = this.html || cfg.html;
936         
937         if (this.toolbar) {
938             cfg = {
939                 cls: 'btn-toolbar',
940                 html: null
941             };
942             
943             return cfg;
944         }
945         
946         if (['vertical','justified'].indexOf(this.align)!==-1) {
947             cfg.cls = 'btn-group-' + this.align;
948             
949             if (this.align == 'justified') {
950                 console.log(this.items);
951             }
952         }
953         
954         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
955             cfg.cls += ' btn-group-' + this.size;
956         }
957         
958         if (this.direction == 'up') {
959             cfg.cls += ' dropup' ;
960         }
961         
962         return cfg;
963     },
964     /**
965      * Add a button to the group (similar to NavItem API.)
966      */
967     addItem : function(cfg)
968     {
969         var cn = new Roo.bootstrap.Button(cfg);
970         //this.register(cn);
971         cn.parentId = this.id;
972         cn.onRender(this.el, null);
973         return cn;
974     }
975    
976 });
977
978  /*
979  * - LGPL
980  *
981  * button
982  * 
983  */
984
985 /**
986  * @class Roo.bootstrap.Button
987  * @extends Roo.bootstrap.Component
988  * Bootstrap Button class
989  * @cfg {String} html The button content
990  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
991  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
992  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
993  * @cfg {String} size (lg|sm|xs)
994  * @cfg {String} tag (a|input|submit)
995  * @cfg {String} href empty or href
996  * @cfg {Boolean} disabled default false;
997  * @cfg {Boolean} isClose default false;
998  * @cfg {String} glyphicon depricated - use fa
999  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1000  * @cfg {String} badge text for badge
1001  * @cfg {String} theme (default|glow)  
1002  * @cfg {Boolean} inverse dark themed version
1003  * @cfg {Boolean} toggle is it a slidy toggle button
1004  * @cfg {Boolean} pressed   default null - if the button ahs active state
1005  * @cfg {String} ontext text for on slidy toggle state
1006  * @cfg {String} offtext text for off slidy toggle state
1007  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1008  * @cfg {Boolean} removeClass remove the standard class..
1009  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1010  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1011  * 
1012  * @constructor
1013  * Create a new button
1014  * @param {Object} config The config object
1015  */
1016
1017
1018 Roo.bootstrap.Button = function(config){
1019     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1020     
1021     this.addEvents({
1022         // raw events
1023         /**
1024          * @event click
1025          * When a button is pressed
1026          * @param {Roo.bootstrap.Button} btn
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true,
1030         /**
1031          * @event dblclick
1032          * When a button is double clicked
1033          * @param {Roo.bootstrap.Button} btn
1034          * @param {Roo.EventObject} e
1035          */
1036         "dblclick" : true,
1037          /**
1038          * @event toggle
1039          * After the button has been toggles
1040          * @param {Roo.bootstrap.Button} btn
1041          * @param {Roo.EventObject} e
1042          * @param {boolean} pressed (also available as button.pressed)
1043          */
1044         "toggle" : true
1045     });
1046 };
1047
1048 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1049     html: false,
1050     active: false,
1051     weight: '',
1052     badge_weight: '',
1053     outline : false,
1054     size: '',
1055     tag: 'button',
1056     href: '',
1057     disabled: false,
1058     isClose: false,
1059     glyphicon: '',
1060     fa: '',
1061     badge: '',
1062     theme: 'default',
1063     inverse: false,
1064     
1065     toggle: false,
1066     ontext: 'ON',
1067     offtext: 'OFF',
1068     defaulton: true,
1069     preventDefault: true,
1070     removeClass: false,
1071     name: false,
1072     target: false,
1073     group : false,
1074      
1075     pressed : null,
1076      
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : 'button',
1082             cls : 'roo-button',
1083             html: ''
1084         };
1085         
1086         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1087             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1088             this.tag = 'button';
1089         } else {
1090             cfg.tag = this.tag;
1091         }
1092         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1093         
1094         if (this.toggle == true) {
1095             cfg={
1096                 tag: 'div',
1097                 cls: 'slider-frame roo-button',
1098                 cn: [
1099                     {
1100                         tag: 'span',
1101                         'data-on-text':'ON',
1102                         'data-off-text':'OFF',
1103                         cls: 'slider-button',
1104                         html: this.offtext
1105                     }
1106                 ]
1107             };
1108             // why are we validating the weights?
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 cfg.cls +=  ' ' + this.weight;
1111             }
1112             
1113             return cfg;
1114         }
1115         
1116         if (this.isClose) {
1117             cfg.cls += ' close';
1118             
1119             cfg["aria-hidden"] = true;
1120             
1121             cfg.html = "&times;";
1122             
1123             return cfg;
1124         }
1125              
1126         
1127         if (this.theme==='default') {
1128             cfg.cls = 'btn roo-button';
1129             
1130             //if (this.parentType != 'Navbar') {
1131             this.weight = this.weight.length ?  this.weight : 'default';
1132             //}
1133             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1134                 
1135                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1136                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1137                 cfg.cls += ' btn-' + outline + weight;
1138                 if (this.weight == 'default') {
1139                     // BC
1140                     cfg.cls += ' btn-' + this.weight;
1141                 }
1142             }
1143         } else if (this.theme==='glow') {
1144             
1145             cfg.tag = 'a';
1146             cfg.cls = 'btn-glow roo-button';
1147             
1148             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1149                 
1150                 cfg.cls += ' ' + this.weight;
1151             }
1152         }
1153    
1154         
1155         if (this.inverse) {
1156             this.cls += ' inverse';
1157         }
1158         
1159         
1160         if (this.active || this.pressed === true) {
1161             cfg.cls += ' active';
1162         }
1163         
1164         if (this.disabled) {
1165             cfg.disabled = 'disabled';
1166         }
1167         
1168         if (this.items) {
1169             Roo.log('changing to ul' );
1170             cfg.tag = 'ul';
1171             this.glyphicon = 'caret';
1172             if (Roo.bootstrap.version == 4) {
1173                 this.fa = 'caret-down';
1174             }
1175             
1176         }
1177         
1178         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1179          
1180         //gsRoo.log(this.parentType);
1181         if (this.parentType === 'Navbar' && !this.parent().bar) {
1182             Roo.log('changing to li?');
1183             
1184             cfg.tag = 'li';
1185             
1186             cfg.cls = '';
1187             cfg.cn =  [{
1188                 tag : 'a',
1189                 cls : 'roo-button',
1190                 html : this.html,
1191                 href : this.href || '#'
1192             }];
1193             if (this.menu) {
1194                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1195                 cfg.cls += ' dropdown';
1196             }   
1197             
1198             delete cfg.html;
1199             
1200         }
1201         
1202        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1203         
1204         if (this.glyphicon) {
1205             cfg.html = ' ' + cfg.html;
1206             
1207             cfg.cn = [
1208                 {
1209                     tag: 'span',
1210                     cls: 'glyphicon glyphicon-' + this.glyphicon
1211                 }
1212             ];
1213         }
1214         if (this.fa) {
1215             cfg.html = ' ' + cfg.html;
1216             
1217             cfg.cn = [
1218                 {
1219                     tag: 'i',
1220                     cls: 'fa fas fa-' + this.fa
1221                 }
1222             ];
1223         }
1224         
1225         if (this.badge) {
1226             cfg.html += ' ';
1227             
1228             cfg.tag = 'a';
1229             
1230 //            cfg.cls='btn roo-button';
1231             
1232             cfg.href=this.href;
1233             
1234             var value = cfg.html;
1235             
1236             if(this.glyphicon){
1237                 value = {
1238                     tag: 'span',
1239                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1240                     html: this.html
1241                 };
1242             }
1243             if(this.fa){
1244                 value = {
1245                     tag: 'i',
1246                     cls: 'fa fas fa-' + this.fa,
1247                     html: this.html
1248                 };
1249             }
1250             
1251             var bw = this.badge_weight.length ? this.badge_weight :
1252                 (this.weight.length ? this.weight : 'secondary');
1253             bw = bw == 'default' ? 'secondary' : bw;
1254             
1255             cfg.cn = [
1256                 value,
1257                 {
1258                     tag: 'span',
1259                     cls: 'badge badge-' + bw,
1260                     html: this.badge
1261                 }
1262             ];
1263             
1264             cfg.html='';
1265         }
1266         
1267         if (this.menu) {
1268             cfg.cls += ' dropdown';
1269             cfg.html = typeof(cfg.html) != 'undefined' ?
1270                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1271         }
1272         
1273         if (cfg.tag !== 'a' && this.href !== '') {
1274             throw "Tag must be a to set href.";
1275         } else if (this.href.length > 0) {
1276             cfg.href = this.href;
1277         }
1278         
1279         if(this.removeClass){
1280             cfg.cls = '';
1281         }
1282         
1283         if(this.target){
1284             cfg.target = this.target;
1285         }
1286         
1287         return cfg;
1288     },
1289     initEvents: function() {
1290        // Roo.log('init events?');
1291 //        Roo.log(this.el.dom);
1292         // add the menu...
1293         
1294         if (typeof (this.menu) != 'undefined') {
1295             this.menu.parentType = this.xtype;
1296             this.menu.triggerEl = this.el;
1297             this.addxtype(Roo.apply({}, this.menu));
1298         }
1299
1300
1301         if (this.el.hasClass('roo-button')) {
1302              this.el.on('click', this.onClick, this);
1303              this.el.on('dblclick', this.onDblClick, this);
1304         } else {
1305              this.el.select('.roo-button').on('click', this.onClick, this);
1306              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1307              
1308         }
1309         // why?
1310         if(this.removeClass){
1311             this.el.on('click', this.onClick, this);
1312         }
1313         
1314         if (this.group === true) {
1315              if (this.pressed === false || this.pressed === true) {
1316                 // nothing
1317             } else {
1318                 this.pressed = false;
1319                 this.setActive(this.pressed);
1320             }
1321             
1322         }
1323         
1324         this.el.enableDisplayMode();
1325         
1326     },
1327     onClick : function(e)
1328     {
1329         if (this.disabled) {
1330             return;
1331         }
1332         
1333         Roo.log('button on click ');
1334         if(this.preventDefault){
1335             e.preventDefault();
1336         }
1337         
1338         if (this.group) {
1339             if (this.pressed) {
1340                 // do nothing -
1341                 return;
1342             }
1343             this.setActive(true);
1344             var pi = this.parent().items;
1345             for (var i = 0;i < pi.length;i++) {
1346                 if (this == pi[i]) {
1347                     continue;
1348                 }
1349                 if (pi[i].el.hasClass('roo-button')) {
1350                     pi[i].setActive(false);
1351                 }
1352             }
1353             this.fireEvent('click', this, e);            
1354             return;
1355         }
1356         
1357         if (this.pressed === true || this.pressed === false) {
1358             this.toggleActive(e);
1359         }
1360         
1361         
1362         this.fireEvent('click', this, e);
1363     },
1364     onDblClick: function(e)
1365     {
1366         if (this.disabled) {
1367             return;
1368         }
1369         if(this.preventDefault){
1370             e.preventDefault();
1371         }
1372         this.fireEvent('dblclick', this, e);
1373     },
1374     /**
1375      * Enables this button
1376      */
1377     enable : function()
1378     {
1379         this.disabled = false;
1380         this.el.removeClass('disabled');
1381         this.el.dom.removeAttribute("disabled");
1382     },
1383     
1384     /**
1385      * Disable this button
1386      */
1387     disable : function()
1388     {
1389         this.disabled = true;
1390         this.el.addClass('disabled');
1391         this.el.attr("disabled", "disabled")
1392     },
1393      /**
1394      * sets the active state on/off, 
1395      * @param {Boolean} state (optional) Force a particular state
1396      */
1397     setActive : function(v) {
1398         
1399         this.el[v ? 'addClass' : 'removeClass']('active');
1400         this.pressed = v;
1401     },
1402      /**
1403      * toggles the current active state 
1404      */
1405     toggleActive : function(e)
1406     {
1407         this.setActive(!this.pressed); // this modifies pressed...
1408         this.fireEvent('toggle', this, e, this.pressed);
1409     },
1410      /**
1411      * get the current active state
1412      * @return {boolean} true if it's active
1413      */
1414     isActive : function()
1415     {
1416         return this.el.hasClass('active');
1417     },
1418     /**
1419      * set the text of the first selected button
1420      */
1421     setText : function(str)
1422     {
1423         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1424     },
1425     /**
1426      * get the text of the first selected button
1427      */
1428     getText : function()
1429     {
1430         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1431     },
1432     
1433     setWeight : function(str)
1434     {
1435         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1436         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1437         this.weight = str;
1438         var outline = this.outline ? 'outline-' : '';
1439         if (str == 'default') {
1440             this.el.addClass('btn-default btn-outline-secondary');        
1441             return;
1442         }
1443         this.el.addClass('btn-' + outline + str);        
1444     }
1445     
1446     
1447 });
1448 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1449
1450 Roo.bootstrap.Button.weights = [
1451     'default',
1452     'secondary' ,
1453     'primary',
1454     'success',
1455     'info',
1456     'warning',
1457     'danger',
1458     'link',
1459     'light',
1460     'dark'              
1461    
1462 ];/*
1463  * - LGPL
1464  *
1465  * column
1466  * 
1467  */
1468
1469 /**
1470  * @class Roo.bootstrap.Column
1471  * @extends Roo.bootstrap.Component
1472  * Bootstrap Column class
1473  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1477  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1478  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1479  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1480  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1481  *
1482  * 
1483  * @cfg {Boolean} hidden (true|false) hide the element
1484  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1485  * @cfg {String} fa (ban|check|...) font awesome icon
1486  * @cfg {Number} fasize (1|2|....) font awsome size
1487
1488  * @cfg {String} icon (info-sign|check|...) glyphicon name
1489
1490  * @cfg {String} html content of column.
1491  * 
1492  * @constructor
1493  * Create a new Column
1494  * @param {Object} config The config object
1495  */
1496
1497 Roo.bootstrap.Column = function(config){
1498     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1502     
1503     xs: false,
1504     sm: false,
1505     md: false,
1506     lg: false,
1507     xsoff: false,
1508     smoff: false,
1509     mdoff: false,
1510     lgoff: false,
1511     html: '',
1512     offset: 0,
1513     alert: false,
1514     fa: false,
1515     icon : false,
1516     hidden : false,
1517     fasize : 1,
1518     
1519     getAutoCreate : function(){
1520         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1521         
1522         cfg = {
1523             tag: 'div',
1524             cls: 'column'
1525         };
1526         
1527         var settings=this;
1528         var sizes =   ['xs','sm','md','lg'];
1529         sizes.map(function(size ,ix){
1530             //Roo.log( size + ':' + settings[size]);
1531             
1532             if (settings[size+'off'] !== false) {
1533                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1534             }
1535             
1536             if (settings[size] === false) {
1537                 return;
1538             }
1539             
1540             if (!settings[size]) { // 0 = hidden
1541                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1542                 // bootsrap4
1543                 for (var i = ix; i > -1; i--) {
1544                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1545                 }
1546                 
1547                 
1548                 return;
1549             }
1550             cfg.cls += ' col-' + size + '-' + settings[size] + (
1551                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1552             );
1553             
1554         });
1555         
1556         if (this.hidden) {
1557             cfg.cls += ' hidden';
1558         }
1559         
1560         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1561             cfg.cls +=' alert alert-' + this.alert;
1562         }
1563         
1564         
1565         if (this.html.length) {
1566             cfg.html = this.html;
1567         }
1568         if (this.fa) {
1569             var fasize = '';
1570             if (this.fasize > 1) {
1571                 fasize = ' fa-' + this.fasize + 'x';
1572             }
1573             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574             
1575             
1576         }
1577         if (this.icon) {
1578             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1579         }
1580         
1581         return cfg;
1582     }
1583    
1584 });
1585
1586  
1587
1588  /*
1589  * - LGPL
1590  *
1591  * page container.
1592  * 
1593  */
1594
1595
1596 /**
1597  * @class Roo.bootstrap.Container
1598  * @extends Roo.bootstrap.Component
1599  * @builder-top
1600  * Bootstrap Container class
1601  * @cfg {Boolean} jumbotron is it a jumbotron element
1602  * @cfg {String} html content of element
1603  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1604  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1605  * @cfg {String} header content of header (for panel)
1606  * @cfg {String} footer content of footer (for panel)
1607  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1608  * @cfg {String} tag (header|aside|section) type of HTML tag.
1609  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1610  * @cfg {String} fa font awesome icon
1611  * @cfg {String} icon (info-sign|check|...) glyphicon name
1612  * @cfg {Boolean} hidden (true|false) hide the element
1613  * @cfg {Boolean} expandable (true|false) default false
1614  * @cfg {Boolean} expanded (true|false) default true
1615  * @cfg {String} rheader contet on the right of header
1616  * @cfg {Boolean} clickable (true|false) default false
1617
1618  *     
1619  * @constructor
1620  * Create a new Container
1621  * @param {Object} config The config object
1622  */
1623
1624 Roo.bootstrap.Container = function(config){
1625     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626     
1627     this.addEvents({
1628         // raw events
1629          /**
1630          * @event expand
1631          * After the panel has been expand
1632          * 
1633          * @param {Roo.bootstrap.Container} this
1634          */
1635         "expand" : true,
1636         /**
1637          * @event collapse
1638          * After the panel has been collapsed
1639          * 
1640          * @param {Roo.bootstrap.Container} this
1641          */
1642         "collapse" : true,
1643         /**
1644          * @event click
1645          * When a element is chick
1646          * @param {Roo.bootstrap.Container} this
1647          * @param {Roo.EventObject} e
1648          */
1649         "click" : true
1650     });
1651 };
1652
1653 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1654     
1655     jumbotron : false,
1656     well: '',
1657     panel : '',
1658     header: '',
1659     footer : '',
1660     sticky: '',
1661     tag : false,
1662     alert : false,
1663     fa: false,
1664     icon : false,
1665     expandable : false,
1666     rheader : '',
1667     expanded : true,
1668     clickable: false,
1669   
1670      
1671     getChildContainer : function() {
1672         
1673         if(!this.el){
1674             return false;
1675         }
1676         
1677         if (this.panel.length) {
1678             return this.el.select('.panel-body',true).first();
1679         }
1680         
1681         return this.el;
1682     },
1683     
1684     
1685     getAutoCreate : function(){
1686         
1687         var cfg = {
1688             tag : this.tag || 'div',
1689             html : '',
1690             cls : ''
1691         };
1692         if (this.jumbotron) {
1693             cfg.cls = 'jumbotron';
1694         }
1695         
1696         
1697         
1698         // - this is applied by the parent..
1699         //if (this.cls) {
1700         //    cfg.cls = this.cls + '';
1701         //}
1702         
1703         if (this.sticky.length) {
1704             
1705             var bd = Roo.get(document.body);
1706             if (!bd.hasClass('bootstrap-sticky')) {
1707                 bd.addClass('bootstrap-sticky');
1708                 Roo.select('html',true).setStyle('height', '100%');
1709             }
1710              
1711             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1712         }
1713         
1714         
1715         if (this.well.length) {
1716             switch (this.well) {
1717                 case 'lg':
1718                 case 'sm':
1719                     cfg.cls +=' well well-' +this.well;
1720                     break;
1721                 default:
1722                     cfg.cls +=' well';
1723                     break;
1724             }
1725         }
1726         
1727         if (this.hidden) {
1728             cfg.cls += ' hidden';
1729         }
1730         
1731         
1732         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1733             cfg.cls +=' alert alert-' + this.alert;
1734         }
1735         
1736         var body = cfg;
1737         
1738         if (this.panel.length) {
1739             cfg.cls += ' panel panel-' + this.panel;
1740             cfg.cn = [];
1741             if (this.header.length) {
1742                 
1743                 var h = [];
1744                 
1745                 if(this.expandable){
1746                     
1747                     cfg.cls = cfg.cls + ' expandable';
1748                     
1749                     h.push({
1750                         tag: 'i',
1751                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1752                     });
1753                     
1754                 }
1755                 
1756                 h.push(
1757                     {
1758                         tag: 'span',
1759                         cls : 'panel-title',
1760                         html : (this.expandable ? '&nbsp;' : '') + this.header
1761                     },
1762                     {
1763                         tag: 'span',
1764                         cls: 'panel-header-right',
1765                         html: this.rheader
1766                     }
1767                 );
1768                 
1769                 cfg.cn.push({
1770                     cls : 'panel-heading',
1771                     style : this.expandable ? 'cursor: pointer' : '',
1772                     cn : h
1773                 });
1774                 
1775             }
1776             
1777             body = false;
1778             cfg.cn.push({
1779                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1780                 html : this.html
1781             });
1782             
1783             
1784             if (this.footer.length) {
1785                 cfg.cn.push({
1786                     cls : 'panel-footer',
1787                     html : this.footer
1788                     
1789                 });
1790             }
1791             
1792         }
1793         
1794         if (body) {
1795             body.html = this.html || cfg.html;
1796             // prefix with the icons..
1797             if (this.fa) {
1798                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1799             }
1800             if (this.icon) {
1801                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1802             }
1803             
1804             
1805         }
1806         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1807             cfg.cls =  'container';
1808         }
1809         
1810         return cfg;
1811     },
1812     
1813     initEvents: function() 
1814     {
1815         if(this.expandable){
1816             var headerEl = this.headerEl();
1817         
1818             if(headerEl){
1819                 headerEl.on('click', this.onToggleClick, this);
1820             }
1821         }
1822         
1823         if(this.clickable){
1824             this.el.on('click', this.onClick, this);
1825         }
1826         
1827     },
1828     
1829     onToggleClick : function()
1830     {
1831         var headerEl = this.headerEl();
1832         
1833         if(!headerEl){
1834             return;
1835         }
1836         
1837         if(this.expanded){
1838             this.collapse();
1839             return;
1840         }
1841         
1842         this.expand();
1843     },
1844     
1845     expand : function()
1846     {
1847         if(this.fireEvent('expand', this)) {
1848             
1849             this.expanded = true;
1850             
1851             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1852             
1853             this.el.select('.panel-body',true).first().removeClass('hide');
1854             
1855             var toggleEl = this.toggleEl();
1856
1857             if(!toggleEl){
1858                 return;
1859             }
1860
1861             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1862         }
1863         
1864     },
1865     
1866     collapse : function()
1867     {
1868         if(this.fireEvent('collapse', this)) {
1869             
1870             this.expanded = false;
1871             
1872             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1873             this.el.select('.panel-body',true).first().addClass('hide');
1874         
1875             var toggleEl = this.toggleEl();
1876
1877             if(!toggleEl){
1878                 return;
1879             }
1880
1881             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1882         }
1883     },
1884     
1885     toggleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-heading .fa',true).first();
1892     },
1893     
1894     headerEl : function()
1895     {
1896         if(!this.el || !this.panel.length || !this.header.length){
1897             return;
1898         }
1899         
1900         return this.el.select('.panel-heading',true).first()
1901     },
1902     
1903     bodyEl : function()
1904     {
1905         if(!this.el || !this.panel.length){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-body',true).first()
1910     },
1911     
1912     titleEl : function()
1913     {
1914         if(!this.el || !this.panel.length || !this.header.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-title',true).first();
1919     },
1920     
1921     setTitle : function(v)
1922     {
1923         var titleEl = this.titleEl();
1924         
1925         if(!titleEl){
1926             return;
1927         }
1928         
1929         titleEl.dom.innerHTML = v;
1930     },
1931     
1932     getTitle : function()
1933     {
1934         
1935         var titleEl = this.titleEl();
1936         
1937         if(!titleEl){
1938             return '';
1939         }
1940         
1941         return titleEl.dom.innerHTML;
1942     },
1943     
1944     setRightTitle : function(v)
1945     {
1946         var t = this.el.select('.panel-header-right',true).first();
1947         
1948         if(!t){
1949             return;
1950         }
1951         
1952         t.dom.innerHTML = v;
1953     },
1954     
1955     onClick : function(e)
1956     {
1957         e.preventDefault();
1958         
1959         this.fireEvent('click', this, e);
1960     }
1961 });
1962
1963  /*
1964  *  - LGPL
1965  *
1966  *  This is BS4's Card element.. - similar to our containers probably..
1967  * 
1968  */
1969 /**
1970  * @class Roo.bootstrap.Card
1971  * @extends Roo.bootstrap.Component
1972  * Bootstrap Card class
1973  *
1974  *
1975  * possible... may not be implemented..
1976  * @cfg {String} header_image  src url of image.
1977  * @cfg {String|Object} header
1978  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1979  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1980  * 
1981  * @cfg {String} title
1982  * @cfg {String} subtitle
1983  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1984  * @cfg {String} footer
1985  
1986  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1987  * 
1988  * @cfg {String} margin (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1991  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1992  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1993  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1995  *
1996  * @cfg {String} padding (0|1|2|3|4|5)
1997  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1998  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1999  * @cfg {String} padding_left (0|1|2|3|4|5)
2000  * @cfg {String} padding_right (0|1|2|3|4|5)
2001  * @cfg {String} padding_x (0|1|2|3|4|5)
2002  * @cfg {String} padding_y (0|1|2|3|4|5)
2003  *
2004  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2008  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2009  
2010  * @config {Boolean} dragable  if this card can be dragged.
2011  * @config {String} drag_group  group for drag
2012  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2013  * @config {String} drop_group  group for drag
2014  * 
2015  * @config {Boolean} collapsable can the body be collapsed.
2016  * @config {Boolean} collapsed is the body collapsed when rendered...
2017  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2018  * @config {Boolean} rotated is the body rotated when rendered...
2019  * 
2020  * @constructor
2021  * Create a new Container
2022  * @param {Object} config The config object
2023  */
2024
2025 Roo.bootstrap.Card = function(config){
2026     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027     
2028     this.addEvents({
2029          // raw events
2030         /**
2031          * @event drop
2032          * When a element a card is dropped
2033          * @param {Roo.bootstrap.Card} this
2034          *
2035          * 
2036          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2037          * @param {String} position 'above' or 'below'
2038          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039         
2040          */
2041         'drop' : true,
2042          /**
2043          * @event rotate
2044          * When a element a card is rotate
2045          * @param {Roo.bootstrap.Card} this
2046          * @param {Roo.Element} n the node being dropped?
2047          * @param {Boolean} rotate status
2048          */
2049         'rotate' : true,
2050         /**
2051          * @event cardover
2052          * When a card element is dragged over ready to drop (return false to block dropable)
2053          * @param {Roo.bootstrap.Card} this
2054          * @param {Object} data from dragdrop 
2055          */
2056          'cardover' : true
2057          
2058     });
2059 };
2060
2061
2062 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2063     
2064     
2065     weight : '',
2066     
2067     margin: '', /// may be better in component?
2068     margin_top: '', 
2069     margin_bottom: '', 
2070     margin_left: '',
2071     margin_right: '',
2072     margin_x: '',
2073     margin_y: '',
2074     
2075     padding : '',
2076     padding_top: '', 
2077     padding_bottom: '', 
2078     padding_left: '',
2079     padding_right: '',
2080     padding_x: '',
2081     padding_y: '',
2082     
2083     display: '', 
2084     display_xs: '', 
2085     display_sm: '', 
2086     display_lg: '',
2087     display_xl: '',
2088  
2089     header_image  : '',
2090     header : '',
2091     header_size : 0,
2092     title : '',
2093     subtitle : '',
2094     html : '',
2095     footer: '',
2096
2097     collapsable : false,
2098     collapsed : false,
2099     rotateable : false,
2100     rotated : false,
2101     
2102     dragable : false,
2103     drag_group : false,
2104     dropable : false,
2105     drop_group : false,
2106     childContainer : false,
2107     dropEl : false, /// the dom placeholde element that indicates drop location.
2108     containerEl: false, // body container
2109     bodyEl: false, // card-body
2110     headerContainerEl : false, //
2111     headerEl : false,
2112     header_imageEl : false,
2113     
2114     
2115     layoutCls : function()
2116     {
2117         var cls = '';
2118         var t = this;
2119         Roo.log(this.margin_bottom.length);
2120         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2121             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2122             
2123             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2124                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2125             }
2126             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2127                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2128             }
2129         });
2130         
2131         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2132             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2133                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2134             }
2135         });
2136         
2137         // more generic support?
2138         if (this.hidden) {
2139             cls += ' d-none';
2140         }
2141         
2142         return cls;
2143     },
2144  
2145        // Roo.log("Call onRender: " + this.xtype);
2146         /*  We are looking at something like this.
2147 <div class="card">
2148     <img src="..." class="card-img-top" alt="...">
2149     <div class="card-body">
2150         <h5 class="card-title">Card title</h5>
2151          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2152
2153         >> this bit is really the body...
2154         <div> << we will ad dthis in hopefully it will not break shit.
2155         
2156         ** card text does not actually have any styling...
2157         
2158             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2159         
2160         </div> <<
2161           <a href="#" class="card-link">Card link</a>
2162           
2163     </div>
2164     <div class="card-footer">
2165         <small class="text-muted">Last updated 3 mins ago</small>
2166     </div>
2167 </div>
2168          */
2169     getAutoCreate : function(){
2170         
2171         var cfg = {
2172             tag : 'div',
2173             cls : 'card',
2174             cn : [ ]
2175         };
2176         
2177         if (this.weight.length && this.weight != 'light') {
2178             cfg.cls += ' text-white';
2179         } else {
2180             cfg.cls += ' text-dark'; // need as it's nested..
2181         }
2182         if (this.weight.length) {
2183             cfg.cls += ' bg-' + this.weight;
2184         }
2185         
2186         cfg.cls += ' ' + this.layoutCls(); 
2187         
2188         var hdr = false;
2189         var hdr_ctr = false;
2190         if (this.header.length) {
2191             hdr = {
2192                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2193                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2194                 cn : []
2195             };
2196             cfg.cn.push(hdr);
2197             hdr_ctr = hdr;
2198         } else {
2199             hdr = {
2200                 tag : 'div',
2201                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202                 cn : []
2203             };
2204             cfg.cn.push(hdr);
2205             hdr_ctr = hdr;
2206         }
2207         if (this.collapsable) {
2208             hdr_ctr = {
2209             tag : 'a',
2210             cls : 'd-block user-select-none',
2211             cn: [
2212                     {
2213                         tag: 'i',
2214                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2215                     }
2216                    
2217                 ]
2218             };
2219             hdr.cn.push(hdr_ctr);
2220         }
2221         
2222         hdr_ctr.cn.push(        {
2223             tag: 'span',
2224             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2225             html : this.header
2226         });
2227         
2228         
2229         if (this.header_image.length) {
2230             cfg.cn.push({
2231                 tag : 'img',
2232                 cls : 'card-img-top',
2233                 src: this.header_image // escape?
2234             });
2235         } else {
2236             cfg.cn.push({
2237                     tag : 'div',
2238                     cls : 'card-img-top d-none' 
2239                 });
2240         }
2241             
2242         var body = {
2243             tag : 'div',
2244             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2245             cn : []
2246         };
2247         var obody = body;
2248         if (this.collapsable || this.rotateable) {
2249             obody = {
2250                 tag: 'div',
2251                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2252                 cn : [  body ]
2253             };
2254         }
2255         
2256         cfg.cn.push(obody);
2257         
2258         if (this.title.length) {
2259             body.cn.push({
2260                 tag : 'div',
2261                 cls : 'card-title',
2262                 src: this.title // escape?
2263             });
2264         }  
2265         
2266         if (this.subtitle.length) {
2267             body.cn.push({
2268                 tag : 'div',
2269                 cls : 'card-title',
2270                 src: this.subtitle // escape?
2271             });
2272         }
2273         
2274         body.cn.push({
2275             tag : 'div',
2276             cls : 'roo-card-body-ctr'
2277         });
2278         
2279         if (this.html.length) {
2280             body.cn.push({
2281                 tag: 'div',
2282                 html : this.html
2283             });
2284         }
2285         // fixme ? handle objects?
2286         
2287         if (this.footer.length) {
2288            
2289             cfg.cn.push({
2290                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2291                 html : this.footer
2292             });
2293             
2294         } else {
2295             cfg.cn.push({cls : 'card-footer d-none'});
2296         }
2297         
2298         // footer...
2299         
2300         return cfg;
2301     },
2302     
2303     
2304     getCardHeader : function()
2305     {
2306         var  ret = this.el.select('.card-header',true).first();
2307         if (ret.hasClass('d-none')) {
2308             ret.removeClass('d-none');
2309         }
2310         
2311         return ret;
2312     },
2313     getCardFooter : function()
2314     {
2315         var  ret = this.el.select('.card-footer',true).first();
2316         if (ret.hasClass('d-none')) {
2317             ret.removeClass('d-none');
2318         }
2319         
2320         return ret;
2321     },
2322     getCardImageTop : function()
2323     {
2324         var  ret = this.header_imageEl;
2325         if (ret.hasClass('d-none')) {
2326             ret.removeClass('d-none');
2327         }
2328             
2329         return ret;
2330     },
2331     
2332     getChildContainer : function()
2333     {
2334         
2335         if(!this.el){
2336             return false;
2337         }
2338         return this.el.select('.roo-card-body-ctr',true).first();    
2339     },
2340     
2341     initEvents: function() 
2342     {
2343         this.bodyEl = this.el.select('.card-body',true).first(); 
2344         this.containerEl = this.getChildContainer();
2345         if(this.dragable){
2346             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2347                     containerScroll: true,
2348                     ddGroup: this.drag_group || 'default_card_drag_group'
2349             });
2350             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2351         }
2352         if (this.dropable) {
2353             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2354                 containerScroll: true,
2355                 ddGroup: this.drop_group || 'default_card_drag_group'
2356             });
2357             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2358             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2359             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2360             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2361             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2362         }
2363         
2364         if (this.collapsable) {
2365             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2366         }
2367         if (this.rotateable) {
2368             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2369         }
2370         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2371          
2372         this.footerEl = this.el.select('.card-footer',true).first();
2373         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2374         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2375         this.headerEl = this.el.select('.card-header',true).first();
2376         
2377         if (this.rotated) {
2378             this.el.addClass('roo-card-rotated');
2379             this.fireEvent('rotate', this, true);
2380         }
2381         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2382         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2383         
2384     },
2385     getDragData : function(e)
2386     {
2387         var target = this.getEl();
2388         if (target) {
2389             //this.handleSelection(e);
2390             
2391             var dragData = {
2392                 source: this,
2393                 copy: false,
2394                 nodes: this.getEl(),
2395                 records: []
2396             };
2397             
2398             
2399             dragData.ddel = target.dom ;    // the div element
2400             Roo.log(target.getWidth( ));
2401             dragData.ddel.style.width = target.getWidth() + 'px';
2402             
2403             return dragData;
2404         }
2405         return false;
2406     },
2407     /**
2408     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2409     *    whole Element becomes the target, and this causes the drop gesture to append.
2410     *
2411     *    Returns an object:
2412     *     {
2413            
2414            position : 'below' or 'above'
2415            card  : relateive to card OBJECT (or true for no cards listed)
2416            items_n : relative to nth item in list
2417            card_n : relative to  nth card in list
2418     }
2419     *
2420     *    
2421     */
2422     getTargetFromEvent : function(e, dragged_card_el)
2423     {
2424         var target = e.getTarget();
2425         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2426             target = target.parentNode;
2427         }
2428         
2429         var ret = {
2430             position: '',
2431             cards : [],
2432             card_n : -1,
2433             items_n : -1,
2434             card : false 
2435         };
2436         
2437         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2438         // see if target is one of the 'cards'...
2439         
2440         
2441         //Roo.log(this.items.length);
2442         var pos = false;
2443         
2444         var last_card_n = 0;
2445         var cards_len  = 0;
2446         for (var i = 0;i< this.items.length;i++) {
2447             
2448             if (!this.items[i].el.hasClass('card')) {
2449                  continue;
2450             }
2451             pos = this.getDropPoint(e, this.items[i].el.dom);
2452             
2453             cards_len = ret.cards.length;
2454             //Roo.log(this.items[i].el.dom.id);
2455             ret.cards.push(this.items[i]);
2456             last_card_n  = i;
2457             if (ret.card_n < 0 && pos == 'above') {
2458                 ret.position = cards_len > 0 ? 'below' : pos;
2459                 ret.items_n = i > 0 ? i - 1 : 0;
2460                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2461                 ret.card = ret.cards[ret.card_n];
2462             }
2463         }
2464         if (!ret.cards.length) {
2465             ret.card = true;
2466             ret.position = 'below';
2467             ret.items_n;
2468             return ret;
2469         }
2470         // could not find a card.. stick it at the end..
2471         if (ret.card_n < 0) {
2472             ret.card_n = last_card_n;
2473             ret.card = ret.cards[last_card_n];
2474             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2475             ret.position = 'below';
2476         }
2477         
2478         if (this.items[ret.items_n].el == dragged_card_el) {
2479             return false;
2480         }
2481         
2482         if (ret.position == 'below') {
2483             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2484             
2485             if (card_after  && card_after.el == dragged_card_el) {
2486                 return false;
2487             }
2488             return ret;
2489         }
2490         
2491         // its's after ..
2492         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2493         
2494         if (card_before  && card_before.el == dragged_card_el) {
2495             return false;
2496         }
2497         
2498         return ret;
2499     },
2500     
2501     onNodeEnter : function(n, dd, e, data){
2502         return false;
2503     },
2504     onNodeOver : function(n, dd, e, data)
2505     {
2506        
2507         var target_info = this.getTargetFromEvent(e,data.source.el);
2508         if (target_info === false) {
2509             this.dropPlaceHolder('hide');
2510             return false;
2511         }
2512         Roo.log(['getTargetFromEvent', target_info ]);
2513         
2514         
2515         if (this.fireEvent('cardover', this, [ data ]) === false) {
2516             return false;
2517         }
2518         
2519         this.dropPlaceHolder('show', target_info,data);
2520         
2521         return false; 
2522     },
2523     onNodeOut : function(n, dd, e, data){
2524         this.dropPlaceHolder('hide');
2525      
2526     },
2527     onNodeDrop : function(n, dd, e, data)
2528     {
2529         
2530         // call drop - return false if
2531         
2532         // this could actually fail - if the Network drops..
2533         // we will ignore this at present..- client should probably reload
2534         // the whole set of cards if stuff like that fails.
2535         
2536         
2537         var info = this.getTargetFromEvent(e,data.source.el);
2538         if (info === false) {
2539             return false;
2540         }
2541         this.dropPlaceHolder('hide');
2542   
2543           
2544     
2545         this.acceptCard(data.source, info.position, info.card, info.items_n);
2546         return true;
2547          
2548     },
2549     firstChildCard : function()
2550     {
2551         for (var i = 0;i< this.items.length;i++) {
2552             
2553             if (!this.items[i].el.hasClass('card')) {
2554                  continue;
2555             }
2556             return this.items[i];
2557         }
2558         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2559     },
2560     /**
2561      * accept card
2562      *
2563      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2564      */
2565     acceptCard : function(move_card,  position, next_to_card )
2566     {
2567         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2568             return false;
2569         }
2570         
2571         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2572         
2573         move_card.parent().removeCard(move_card);
2574         
2575         
2576         var dom = move_card.el.dom;
2577         dom.style.width = ''; // clear with - which is set by drag.
2578         
2579         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2580             var cardel = next_to_card.el.dom;
2581             
2582             if (position == 'above' ) {
2583                 cardel.parentNode.insertBefore(dom, cardel);
2584             } else if (cardel.nextSibling) {
2585                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2586             } else {
2587                 cardel.parentNode.append(dom);
2588             }
2589         } else {
2590             // card container???
2591             this.containerEl.dom.append(dom);
2592         }
2593         
2594         //FIXME HANDLE card = true 
2595         
2596         // add this to the correct place in items.
2597         
2598         // remove Card from items.
2599         
2600        
2601         if (this.items.length) {
2602             var nitems = [];
2603             //Roo.log([info.items_n, info.position, this.items.length]);
2604             for (var i =0; i < this.items.length; i++) {
2605                 if (i == to_items_n && position == 'above') {
2606                     nitems.push(move_card);
2607                 }
2608                 nitems.push(this.items[i]);
2609                 if (i == to_items_n && position == 'below') {
2610                     nitems.push(move_card);
2611                 }
2612             }
2613             this.items = nitems;
2614             Roo.log(this.items);
2615         } else {
2616             this.items.push(move_card);
2617         }
2618         
2619         move_card.parentId = this.id;
2620         
2621         return true;
2622         
2623         
2624     },
2625     removeCard : function(c)
2626     {
2627         this.items = this.items.filter(function(e) { return e != c });
2628  
2629         var dom = c.el.dom;
2630         dom.parentNode.removeChild(dom);
2631         dom.style.width = ''; // clear with - which is set by drag.
2632         c.parentId = false;
2633         
2634     },
2635     
2636     /**    Decide whether to drop above or below a View node. */
2637     getDropPoint : function(e, n, dd)
2638     {
2639         if (dd) {
2640              return false;
2641         }
2642         if (n == this.containerEl.dom) {
2643             return "above";
2644         }
2645         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2646         var c = t + (b - t) / 2;
2647         var y = Roo.lib.Event.getPageY(e);
2648         if(y <= c) {
2649             return "above";
2650         }else{
2651             return "below";
2652         }
2653     },
2654     onToggleCollapse : function(e)
2655         {
2656         if (this.collapsed) {
2657             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2658             this.collapsableEl.addClass('show');
2659             this.collapsed = false;
2660             return;
2661         }
2662         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2663         this.collapsableEl.removeClass('show');
2664         this.collapsed = true;
2665         
2666     
2667     },
2668     
2669     onToggleRotate : function(e)
2670     {
2671         this.collapsableEl.removeClass('show');
2672         this.footerEl.removeClass('d-none');
2673         this.el.removeClass('roo-card-rotated');
2674         this.el.removeClass('d-none');
2675         if (this.rotated) {
2676             
2677             this.collapsableEl.addClass('show');
2678             this.rotated = false;
2679             this.fireEvent('rotate', this, this.rotated);
2680             return;
2681         }
2682         this.el.addClass('roo-card-rotated');
2683         this.footerEl.addClass('d-none');
2684         this.el.select('.roo-collapsable').removeClass('show');
2685         
2686         this.rotated = true;
2687         this.fireEvent('rotate', this, this.rotated);
2688     
2689     },
2690     
2691     dropPlaceHolder: function (action, info, data)
2692     {
2693         if (this.dropEl === false) {
2694             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2695             cls : 'd-none'
2696             },true);
2697         }
2698         this.dropEl.removeClass(['d-none', 'd-block']);        
2699         if (action == 'hide') {
2700             
2701             this.dropEl.addClass('d-none');
2702             return;
2703         }
2704         // FIXME - info.card == true!!!
2705         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2706         
2707         if (info.card !== true) {
2708             var cardel = info.card.el.dom;
2709             
2710             if (info.position == 'above') {
2711                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2712             } else if (cardel.nextSibling) {
2713                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2714             } else {
2715                 cardel.parentNode.append(this.dropEl.dom);
2716             }
2717         } else {
2718             // card container???
2719             this.containerEl.dom.append(this.dropEl.dom);
2720         }
2721         
2722         this.dropEl.addClass('d-block roo-card-dropzone');
2723         
2724         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2725         
2726         
2727     
2728     
2729     
2730     },
2731     setHeaderText: function(html)
2732     {
2733         this.header = html;
2734         if (this.headerContainerEl) {
2735             this.headerContainerEl.dom.innerHTML = html;
2736         }
2737     },
2738     onHeaderImageLoad : function(ev, he)
2739     {
2740         if (!this.header_image_fit_square) {
2741             return;
2742         }
2743         
2744         var hw = he.naturalHeight / he.naturalWidth;
2745         // wide image = < 0
2746         // tall image = > 1
2747         //var w = he.dom.naturalWidth;
2748         var ww = he.width;
2749         he.style.left =  0;
2750         he.style.position =  'relative';
2751         if (hw > 1) {
2752             var nw = (ww * (1/hw));
2753             Roo.get(he).setSize( ww * (1/hw),  ww);
2754             he.style.left =  ((ww - nw)/ 2) + 'px';
2755             he.style.position =  'relative';
2756         }
2757
2758     }
2759
2760     
2761 });
2762
2763 /*
2764  * - LGPL
2765  *
2766  * Card header - holder for the card header elements.
2767  * 
2768  */
2769
2770 /**
2771  * @class Roo.bootstrap.CardHeader
2772  * @extends Roo.bootstrap.Element
2773  * Bootstrap CardHeader class
2774  * @constructor
2775  * Create a new Card Header - that you can embed children into
2776  * @param {Object} config The config object
2777  */
2778
2779 Roo.bootstrap.CardHeader = function(config){
2780     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2781 };
2782
2783 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2784     
2785     
2786     container_method : 'getCardHeader' 
2787     
2788      
2789     
2790     
2791    
2792 });
2793
2794  
2795
2796  /*
2797  * - LGPL
2798  *
2799  * Card footer - holder for the card footer elements.
2800  * 
2801  */
2802
2803 /**
2804  * @class Roo.bootstrap.CardFooter
2805  * @extends Roo.bootstrap.Element
2806  * Bootstrap CardFooter class
2807  * @constructor
2808  * Create a new Card Footer - that you can embed children into
2809  * @param {Object} config The config object
2810  */
2811
2812 Roo.bootstrap.CardFooter = function(config){
2813     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2814 };
2815
2816 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2817     
2818     
2819     container_method : 'getCardFooter' 
2820     
2821      
2822     
2823     
2824    
2825 });
2826
2827  
2828
2829  /*
2830  * - LGPL
2831  *
2832  * Card header - holder for the card header elements.
2833  * 
2834  */
2835
2836 /**
2837  * @class Roo.bootstrap.CardImageTop
2838  * @extends Roo.bootstrap.Element
2839  * Bootstrap CardImageTop class
2840  * @constructor
2841  * Create a new Card Image Top container
2842  * @param {Object} config The config object
2843  */
2844
2845 Roo.bootstrap.CardImageTop = function(config){
2846     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2847 };
2848
2849 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2850     
2851    
2852     container_method : 'getCardImageTop' 
2853     
2854      
2855     
2856    
2857 });
2858
2859  
2860
2861  
2862 /*
2863 * Licence: LGPL
2864 */
2865
2866 /**
2867  * @class Roo.bootstrap.ButtonUploader
2868  * @extends Roo.bootstrap.Button
2869  * Bootstrap Button Uploader class - it's a button which when you add files to it
2870  *
2871  * 
2872  * @cfg {Number} errorTimeout default 3000
2873  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2874  * @cfg {Array}  html The button text.
2875  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2876  *
2877  * @constructor
2878  * Create a new CardUploader
2879  * @param {Object} config The config object
2880  */
2881
2882 Roo.bootstrap.ButtonUploader = function(config){
2883     
2884  
2885     
2886     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887     
2888      
2889      this.addEvents({
2890          // raw events
2891         /**
2892          * @event beforeselect
2893          * When button is pressed, before show upload files dialog is shown
2894          * @param {Roo.bootstrap.UploaderButton} this
2895          *
2896          */
2897         'beforeselect' : true,
2898          /**
2899          * @event fired when files have been selected, 
2900          * When a the download link is clicked
2901          * @param {Roo.bootstrap.UploaderButton} this
2902          * @param {Array} Array of files that have been uploaded
2903          */
2904         'uploaded' : true
2905         
2906     });
2907 };
2908  
2909 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2910     
2911      
2912     errorTimeout : 3000,
2913      
2914     images : false,
2915    
2916     fileCollection : false,
2917     allowBlank : true,
2918     
2919     multiple : true,
2920     
2921     getAutoCreate : function()
2922     {
2923         var im = {
2924             tag: 'input',
2925             type : 'file',
2926             cls : 'd-none  roo-card-upload-selector' 
2927           
2928         };
2929         if (this.multiple) {
2930             im.multiple = 'multiple';
2931         }
2932         
2933         return  {
2934             cls :'div' ,
2935             cn : [
2936                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2937                 im
2938
2939             ]
2940         };
2941            
2942          
2943     },
2944      
2945    
2946     initEvents : function()
2947     {
2948         
2949         Roo.bootstrap.Button.prototype.initEvents.call(this);
2950         
2951         
2952         
2953         
2954         
2955         this.urlAPI = (window.createObjectURL && window) || 
2956                                 (window.URL && URL.revokeObjectURL && URL) || 
2957                                 (window.webkitURL && webkitURL);
2958                         
2959          
2960          
2961          
2962         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2963         
2964         this.selectorEl.on('change', this.onFileSelected, this);
2965          
2966          
2967        
2968     },
2969     
2970    
2971     onClick : function(e)
2972     {
2973         e.preventDefault();
2974         
2975         if ( this.fireEvent('beforeselect', this) === false) {
2976             return;
2977         }
2978          
2979         this.selectorEl.dom.click();
2980          
2981     },
2982     
2983     onFileSelected : function(e)
2984     {
2985         e.preventDefault();
2986         
2987         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2988             return;
2989         }
2990         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2991         this.selectorEl.dom.value  = '';// hopefully reset..
2992         
2993         this.fireEvent('uploaded', this,  files );
2994         
2995     },
2996     
2997        
2998    
2999     
3000     /**
3001      * addCard - add an Attachment to the uploader
3002      * @param data - the data about the image to upload
3003      *
3004      * {
3005           id : 123
3006           title : "Title of file",
3007           is_uploaded : false,
3008           src : "http://.....",
3009           srcfile : { the File upload object },
3010           mimetype : file.type,
3011           preview : false,
3012           is_deleted : 0
3013           .. any other data...
3014         }
3015      *
3016      * 
3017     */
3018      
3019     reset: function()
3020     {
3021          
3022          this.selectorEl
3023     } 
3024     
3025     
3026     
3027     
3028 });
3029  /*
3030  * - LGPL
3031  *
3032  * image
3033  * 
3034  */
3035
3036
3037 /**
3038  * @class Roo.bootstrap.Img
3039  * @extends Roo.bootstrap.Component
3040  * Bootstrap Img class
3041  * @cfg {Boolean} imgResponsive false | true
3042  * @cfg {String} border rounded | circle | thumbnail
3043  * @cfg {String} src image source
3044  * @cfg {String} alt image alternative text
3045  * @cfg {String} href a tag href
3046  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3047  * @cfg {String} xsUrl xs image source
3048  * @cfg {String} smUrl sm image source
3049  * @cfg {String} mdUrl md image source
3050  * @cfg {String} lgUrl lg image source
3051  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3052  * 
3053  * @constructor
3054  * Create a new Input
3055  * @param {Object} config The config object
3056  */
3057
3058 Roo.bootstrap.Img = function(config){
3059     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060     
3061     this.addEvents({
3062         // img events
3063         /**
3064          * @event click
3065          * The img click event for the img.
3066          * @param {Roo.EventObject} e
3067          */
3068         "click" : true,
3069         /**
3070          * @event load
3071          * The when any image loads
3072          * @param {Roo.EventObject} e
3073          */
3074         "load" : true
3075     });
3076 };
3077
3078 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3079     
3080     imgResponsive: true,
3081     border: '',
3082     src: 'about:blank',
3083     href: false,
3084     target: false,
3085     xsUrl: '',
3086     smUrl: '',
3087     mdUrl: '',
3088     lgUrl: '',
3089     backgroundContain : false,
3090
3091     getAutoCreate : function()
3092     {   
3093         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3094             return this.createSingleImg();
3095         }
3096         
3097         var cfg = {
3098             tag: 'div',
3099             cls: 'roo-image-responsive-group',
3100             cn: []
3101         };
3102         var _this = this;
3103         
3104         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3105             
3106             if(!_this[size + 'Url']){
3107                 return;
3108             }
3109             
3110             var img = {
3111                 tag: 'img',
3112                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3113                 html: _this.html || cfg.html,
3114                 src: _this[size + 'Url']
3115             };
3116             
3117             img.cls += ' roo-image-responsive-' + size;
3118             
3119             var s = ['xs', 'sm', 'md', 'lg'];
3120             
3121             s.splice(s.indexOf(size), 1);
3122             
3123             Roo.each(s, function(ss){
3124                 img.cls += ' hidden-' + ss;
3125             });
3126             
3127             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3128                 cfg.cls += ' img-' + _this.border;
3129             }
3130             
3131             if(_this.alt){
3132                 cfg.alt = _this.alt;
3133             }
3134             
3135             if(_this.href){
3136                 var a = {
3137                     tag: 'a',
3138                     href: _this.href,
3139                     cn: [
3140                         img
3141                     ]
3142                 };
3143
3144                 if(this.target){
3145                     a.target = _this.target;
3146                 }
3147             }
3148             
3149             cfg.cn.push((_this.href) ? a : img);
3150             
3151         });
3152         
3153         return cfg;
3154     },
3155     
3156     createSingleImg : function()
3157     {
3158         var cfg = {
3159             tag: 'img',
3160             cls: (this.imgResponsive) ? 'img-responsive' : '',
3161             html : null,
3162             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3163         };
3164         
3165         if (this.backgroundContain) {
3166             cfg.cls += ' background-contain';
3167         }
3168         
3169         cfg.html = this.html || cfg.html;
3170         
3171         if (this.backgroundContain) {
3172             cfg.style="background-image: url(" + this.src + ')';
3173         } else {
3174             cfg.src = this.src || cfg.src;
3175         }
3176         
3177         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3178             cfg.cls += ' img-' + this.border;
3179         }
3180         
3181         if(this.alt){
3182             cfg.alt = this.alt;
3183         }
3184         
3185         if(this.href){
3186             var a = {
3187                 tag: 'a',
3188                 href: this.href,
3189                 cn: [
3190                     cfg
3191                 ]
3192             };
3193             
3194             if(this.target){
3195                 a.target = this.target;
3196             }
3197             
3198         }
3199         
3200         return (this.href) ? a : cfg;
3201     },
3202     
3203     initEvents: function() 
3204     {
3205         if(!this.href){
3206             this.el.on('click', this.onClick, this);
3207         }
3208         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3209             this.el.on('load', this.onImageLoad, this);
3210         } else {
3211             // not sure if this works.. not tested
3212             this.el.select('img', true).on('load', this.onImageLoad, this);
3213         }
3214         
3215     },
3216     
3217     onClick : function(e)
3218     {
3219         Roo.log('img onclick');
3220         this.fireEvent('click', this, e);
3221     },
3222     onImageLoad: function(e)
3223     {
3224         Roo.log('img load');
3225         this.fireEvent('load', this, e);
3226     },
3227     
3228     /**
3229      * Sets the url of the image - used to update it
3230      * @param {String} url the url of the image
3231      */
3232     
3233     setSrc : function(url)
3234     {
3235         this.src =  url;
3236         
3237         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3238             if (this.backgroundContain) {
3239                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3240             } else {
3241                 this.el.dom.src =  url;
3242             }
3243             return;
3244         }
3245         
3246         this.el.select('img', true).first().dom.src =  url;
3247     }
3248     
3249     
3250    
3251 });
3252
3253  /*
3254  * - LGPL
3255  *
3256  * image
3257  * 
3258  */
3259
3260
3261 /**
3262  * @class Roo.bootstrap.Link
3263  * @extends Roo.bootstrap.Component
3264  * Bootstrap Link Class
3265  * @cfg {String} alt image alternative text
3266  * @cfg {String} href a tag href
3267  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3268  * @cfg {String} html the content of the link.
3269  * @cfg {String} anchor name for the anchor link
3270  * @cfg {String} fa - favicon
3271
3272  * @cfg {Boolean} preventDefault (true | false) default false
3273
3274  * 
3275  * @constructor
3276  * Create a new Input
3277  * @param {Object} config The config object
3278  */
3279
3280 Roo.bootstrap.Link = function(config){
3281     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282     
3283     this.addEvents({
3284         // img events
3285         /**
3286          * @event click
3287          * The img click event for the img.
3288          * @param {Roo.EventObject} e
3289          */
3290         "click" : true
3291     });
3292 };
3293
3294 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3295     
3296     href: false,
3297     target: false,
3298     preventDefault: false,
3299     anchor : false,
3300     alt : false,
3301     fa: false,
3302
3303
3304     getAutoCreate : function()
3305     {
3306         var html = this.html || '';
3307         
3308         if (this.fa !== false) {
3309             html = '<i class="fa fa-' + this.fa + '"></i>';
3310         }
3311         var cfg = {
3312             tag: 'a'
3313         };
3314         // anchor's do not require html/href...
3315         if (this.anchor === false) {
3316             cfg.html = html;
3317             cfg.href = this.href || '#';
3318         } else {
3319             cfg.name = this.anchor;
3320             if (this.html !== false || this.fa !== false) {
3321                 cfg.html = html;
3322             }
3323             if (this.href !== false) {
3324                 cfg.href = this.href;
3325             }
3326         }
3327         
3328         if(this.alt !== false){
3329             cfg.alt = this.alt;
3330         }
3331         
3332         
3333         if(this.target !== false) {
3334             cfg.target = this.target;
3335         }
3336         
3337         return cfg;
3338     },
3339     
3340     initEvents: function() {
3341         
3342         if(!this.href || this.preventDefault){
3343             this.el.on('click', this.onClick, this);
3344         }
3345     },
3346     
3347     onClick : function(e)
3348     {
3349         if(this.preventDefault){
3350             e.preventDefault();
3351         }
3352         //Roo.log('img onclick');
3353         this.fireEvent('click', this, e);
3354     }
3355    
3356 });
3357
3358  /*
3359  * - LGPL
3360  *
3361  * header
3362  * 
3363  */
3364
3365 /**
3366  * @class Roo.bootstrap.Header
3367  * @extends Roo.bootstrap.Component
3368  * Bootstrap Header class
3369  * @cfg {String} html content of header
3370  * @cfg {Number} level (1|2|3|4|5|6) default 1
3371  * 
3372  * @constructor
3373  * Create a new Header
3374  * @param {Object} config The config object
3375  */
3376
3377
3378 Roo.bootstrap.Header  = function(config){
3379     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3380 };
3381
3382 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3383     
3384     //href : false,
3385     html : false,
3386     level : 1,
3387     
3388     
3389     
3390     getAutoCreate : function(){
3391         
3392         
3393         
3394         var cfg = {
3395             tag: 'h' + (1 *this.level),
3396             html: this.html || ''
3397         } ;
3398         
3399         return cfg;
3400     }
3401    
3402 });
3403
3404  
3405
3406  /*
3407  * Based on:
3408  * Ext JS Library 1.1.1
3409  * Copyright(c) 2006-2007, Ext JS, LLC.
3410  *
3411  * Originally Released Under LGPL - original licence link has changed is not relivant.
3412  *
3413  * Fork - LGPL
3414  * <script type="text/javascript">
3415  */
3416  
3417 /**
3418  * @class Roo.bootstrap.MenuMgr
3419  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3420  * @singleton
3421  */
3422 Roo.bootstrap.MenuMgr = function(){
3423    var menus, active, groups = {}, attached = false, lastShow = new Date();
3424
3425    // private - called when first menu is created
3426    function init(){
3427        menus = {};
3428        active = new Roo.util.MixedCollection();
3429        Roo.get(document).addKeyListener(27, function(){
3430            if(active.length > 0){
3431                hideAll();
3432            }
3433        });
3434    }
3435
3436    // private
3437    function hideAll(){
3438        if(active && active.length > 0){
3439            var c = active.clone();
3440            c.each(function(m){
3441                m.hide();
3442            });
3443        }
3444    }
3445
3446    // private
3447    function onHide(m){
3448        active.remove(m);
3449        if(active.length < 1){
3450            Roo.get(document).un("mouseup", onMouseDown);
3451             
3452            attached = false;
3453        }
3454    }
3455
3456    // private
3457    function onShow(m){
3458        var last = active.last();
3459        lastShow = new Date();
3460        active.add(m);
3461        if(!attached){
3462           Roo.get(document).on("mouseup", onMouseDown);
3463            
3464            attached = true;
3465        }
3466        if(m.parentMenu){
3467           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3468           m.parentMenu.activeChild = m;
3469        }else if(last && last.isVisible()){
3470           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3471        }
3472    }
3473
3474    // private
3475    function onBeforeHide(m){
3476        if(m.activeChild){
3477            m.activeChild.hide();
3478        }
3479        if(m.autoHideTimer){
3480            clearTimeout(m.autoHideTimer);
3481            delete m.autoHideTimer;
3482        }
3483    }
3484
3485    // private
3486    function onBeforeShow(m){
3487        var pm = m.parentMenu;
3488        if(!pm && !m.allowOtherMenus){
3489            hideAll();
3490        }else if(pm && pm.activeChild && active != m){
3491            pm.activeChild.hide();
3492        }
3493    }
3494
3495    // private this should really trigger on mouseup..
3496    function onMouseDown(e){
3497         Roo.log("on Mouse Up");
3498         
3499         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3500             Roo.log("MenuManager hideAll");
3501             hideAll();
3502             e.stopEvent();
3503         }
3504         
3505         
3506    }
3507
3508    // private
3509    function onBeforeCheck(mi, state){
3510        if(state){
3511            var g = groups[mi.group];
3512            for(var i = 0, l = g.length; i < l; i++){
3513                if(g[i] != mi){
3514                    g[i].setChecked(false);
3515                }
3516            }
3517        }
3518    }
3519
3520    return {
3521
3522        /**
3523         * Hides all menus that are currently visible
3524         */
3525        hideAll : function(){
3526             hideAll();  
3527        },
3528
3529        // private
3530        register : function(menu){
3531            if(!menus){
3532                init();
3533            }
3534            menus[menu.id] = menu;
3535            menu.on("beforehide", onBeforeHide);
3536            menu.on("hide", onHide);
3537            menu.on("beforeshow", onBeforeShow);
3538            menu.on("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                if(!groups[g]){
3542                    groups[g] = [];
3543                }
3544                groups[g].push(menu);
3545                menu.on("checkchange", onCheck);
3546            }
3547        },
3548
3549         /**
3550          * Returns a {@link Roo.menu.Menu} object
3551          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3552          * be used to generate and return a new Menu instance.
3553          */
3554        get : function(menu){
3555            if(typeof menu == "string"){ // menu id
3556                return menus[menu];
3557            }else if(menu.events){  // menu instance
3558                return menu;
3559            }
3560            /*else if(typeof menu.length == 'number'){ // array of menu items?
3561                return new Roo.bootstrap.Menu({items:menu});
3562            }else{ // otherwise, must be a config
3563                return new Roo.bootstrap.Menu(menu);
3564            }
3565            */
3566            return false;
3567        },
3568
3569        // private
3570        unregister : function(menu){
3571            delete menus[menu.id];
3572            menu.un("beforehide", onBeforeHide);
3573            menu.un("hide", onHide);
3574            menu.un("beforeshow", onBeforeShow);
3575            menu.un("show", onShow);
3576            var g = menu.group;
3577            if(g && menu.events["checkchange"]){
3578                groups[g].remove(menu);
3579                menu.un("checkchange", onCheck);
3580            }
3581        },
3582
3583        // private
3584        registerCheckable : function(menuItem){
3585            var g = menuItem.group;
3586            if(g){
3587                if(!groups[g]){
3588                    groups[g] = [];
3589                }
3590                groups[g].push(menuItem);
3591                menuItem.on("beforecheckchange", onBeforeCheck);
3592            }
3593        },
3594
3595        // private
3596        unregisterCheckable : function(menuItem){
3597            var g = menuItem.group;
3598            if(g){
3599                groups[g].remove(menuItem);
3600                menuItem.un("beforecheckchange", onBeforeCheck);
3601            }
3602        }
3603    };
3604 }();/*
3605  * - LGPL
3606  *
3607  * menu
3608  * 
3609  */
3610
3611 /**
3612  * @class Roo.bootstrap.Menu
3613  * @extends Roo.bootstrap.Component
3614  * Bootstrap Menu class - container for MenuItems
3615  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3616  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3617  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3618  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3619   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3620   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3621  
3622  * @constructor
3623  * Create a new Menu
3624  * @param {Object} config The config object
3625  */
3626
3627
3628 Roo.bootstrap.Menu = function(config){
3629     
3630     if (config.type == 'treeview') {
3631         // normally menu's are drawn attached to the document to handle layering etc..
3632         // however treeview (used by the docs menu is drawn into the parent element)
3633         this.container_method = 'getChildContainer'; 
3634     }
3635     
3636     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3637     if (this.registerMenu && this.type != 'treeview')  {
3638         Roo.bootstrap.MenuMgr.register(this);
3639     }
3640     
3641     
3642     this.addEvents({
3643         /**
3644          * @event beforeshow
3645          * Fires before this menu is displayed (return false to block)
3646          * @param {Roo.menu.Menu} this
3647          */
3648         beforeshow : true,
3649         /**
3650          * @event beforehide
3651          * Fires before this menu is hidden (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforehide : true,
3655         /**
3656          * @event show
3657          * Fires after this menu is displayed
3658          * @param {Roo.menu.Menu} this
3659          */
3660         show : true,
3661         /**
3662          * @event hide
3663          * Fires after this menu is hidden
3664          * @param {Roo.menu.Menu} this
3665          */
3666         hide : true,
3667         /**
3668          * @event click
3669          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3670          * @param {Roo.menu.Menu} this
3671          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3672          * @param {Roo.EventObject} e
3673          */
3674         click : true,
3675         /**
3676          * @event mouseover
3677          * Fires when the mouse is hovering over this menu
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.EventObject} e
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          */
3682         mouseover : true,
3683         /**
3684          * @event mouseout
3685          * Fires when the mouse exits this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseout : true,
3691         /**
3692          * @event itemclick
3693          * Fires when a menu item contained in this menu is clicked
3694          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3695          * @param {Roo.EventObject} e
3696          */
3697         itemclick: true
3698     });
3699     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3700 };
3701
3702 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3703     
3704    /// html : false,
3705    
3706     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3707     type: false,
3708     /**
3709      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3710      */
3711     registerMenu : true,
3712     
3713     menuItems :false, // stores the menu items..
3714     
3715     hidden:true,
3716         
3717     parentMenu : false,
3718     
3719     stopEvent : true,
3720     
3721     isLink : false,
3722     
3723     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3724     
3725     hideTrigger : false,
3726     
3727     align : 'tl-bl?',
3728     
3729     
3730     getChildContainer : function() {
3731         return this.el;  
3732     },
3733     
3734     getAutoCreate : function(){
3735          
3736         //if (['right'].indexOf(this.align)!==-1) {
3737         //    cfg.cn[1].cls += ' pull-right'
3738         //}
3739          
3740         var cfg = {
3741             tag : 'ul',
3742             cls : 'dropdown-menu shadow' ,
3743             style : 'z-index:1000'
3744             
3745         };
3746         
3747         if (this.type === 'submenu') {
3748             cfg.cls = 'submenu active';
3749         }
3750         if (this.type === 'treeview') {
3751             cfg.cls = 'treeview-menu';
3752         }
3753         
3754         return cfg;
3755     },
3756     initEvents : function() {
3757         
3758        // Roo.log("ADD event");
3759        // Roo.log(this.triggerEl.dom);
3760         if (this.triggerEl) {
3761             
3762             this.triggerEl.on('click', this.onTriggerClick, this);
3763             
3764             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3765             
3766             if (!this.hideTrigger) {
3767                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3768                     // dropdown toggle on the 'a' in BS4?
3769                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3770                 } else {
3771                     this.triggerEl.addClass('dropdown-toggle');
3772                 }
3773             }
3774         }
3775         
3776         if (Roo.isTouch) {
3777             this.el.on('touchstart'  , this.onTouch, this);
3778         }
3779         this.el.on('click' , this.onClick, this);
3780
3781         this.el.on("mouseover", this.onMouseOver, this);
3782         this.el.on("mouseout", this.onMouseOut, this);
3783         
3784     },
3785     
3786     findTargetItem : function(e)
3787     {
3788         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3789         if(!t){
3790             return false;
3791         }
3792         //Roo.log(t);         Roo.log(t.id);
3793         if(t && t.id){
3794             //Roo.log(this.menuitems);
3795             return this.menuitems.get(t.id);
3796             
3797             //return this.items.get(t.menuItemId);
3798         }
3799         
3800         return false;
3801     },
3802     
3803     onTouch : function(e) 
3804     {
3805         Roo.log("menu.onTouch");
3806         //e.stopEvent(); this make the user popdown broken
3807         this.onClick(e);
3808     },
3809     
3810     onClick : function(e)
3811     {
3812         Roo.log("menu.onClick");
3813         
3814         var t = this.findTargetItem(e);
3815         if(!t || t.isContainer){
3816             return;
3817         }
3818         Roo.log(e);
3819         /*
3820         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3821             if(t == this.activeItem && t.shouldDeactivate(e)){
3822                 this.activeItem.deactivate();
3823                 delete this.activeItem;
3824                 return;
3825             }
3826             if(t.canActivate){
3827                 this.setActiveItem(t, true);
3828             }
3829             return;
3830             
3831             
3832         }
3833         */
3834        
3835         Roo.log('pass click event');
3836         
3837         t.onClick(e);
3838         
3839         this.fireEvent("click", this, t, e);
3840         
3841         var _this = this;
3842         
3843         if(!t.href.length || t.href == '#'){
3844             (function() { _this.hide(); }).defer(100);
3845         }
3846         
3847     },
3848     
3849     onMouseOver : function(e){
3850         var t  = this.findTargetItem(e);
3851         //Roo.log(t);
3852         //if(t){
3853         //    if(t.canActivate && !t.disabled){
3854         //        this.setActiveItem(t, true);
3855         //    }
3856         //}
3857         
3858         this.fireEvent("mouseover", this, e, t);
3859     },
3860     isVisible : function(){
3861         return !this.hidden;
3862     },
3863     onMouseOut : function(e){
3864         var t  = this.findTargetItem(e);
3865         
3866         //if(t ){
3867         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3868         //        this.activeItem.deactivate();
3869         //        delete this.activeItem;
3870         //    }
3871         //}
3872         this.fireEvent("mouseout", this, e, t);
3873     },
3874     
3875     
3876     /**
3877      * Displays this menu relative to another element
3878      * @param {String/HTMLElement/Roo.Element} element The element to align to
3879      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3880      * the element (defaults to this.defaultAlign)
3881      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3882      */
3883     show : function(el, pos, parentMenu)
3884     {
3885         if (false === this.fireEvent("beforeshow", this)) {
3886             Roo.log("show canceled");
3887             return;
3888         }
3889         this.parentMenu = parentMenu;
3890         if(!this.el){
3891             this.render();
3892         }
3893         this.el.addClass('show'); // show otherwise we do not know how big we are..
3894          
3895         var xy = this.el.getAlignToXY(el, pos);
3896         
3897         // bl-tl << left align  below
3898         // tl-bl << left align 
3899         
3900         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3901             // if it goes to far to the right.. -> align left.
3902             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3903         }
3904         if(xy[0] < 0){
3905             // was left align - go right?
3906             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3907         }
3908         
3909         // goes down the bottom
3910         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3911            xy[1]  < 0 ){
3912             var a = this.align.replace('?', '').split('-');
3913             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3914             
3915         }
3916         
3917         this.showAt(  xy , parentMenu, false);
3918     },
3919      /**
3920      * Displays this menu at a specific xy position
3921      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3922      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3923      */
3924     showAt : function(xy, parentMenu, /* private: */_e){
3925         this.parentMenu = parentMenu;
3926         if(!this.el){
3927             this.render();
3928         }
3929         if(_e !== false){
3930             this.fireEvent("beforeshow", this);
3931             //xy = this.el.adjustForConstraints(xy);
3932         }
3933         
3934         //this.el.show();
3935         this.hideMenuItems();
3936         this.hidden = false;
3937         if (this.triggerEl) {
3938             this.triggerEl.addClass('open');
3939         }
3940         
3941         this.el.addClass('show');
3942         
3943         
3944         
3945         // reassign x when hitting right
3946         
3947         // reassign y when hitting bottom
3948         
3949         // but the list may align on trigger left or trigger top... should it be a properity?
3950         
3951         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3952             this.el.setXY(xy);
3953         }
3954         
3955         this.focus();
3956         this.fireEvent("show", this);
3957     },
3958     
3959     focus : function(){
3960         return;
3961         if(!this.hidden){
3962             this.doFocus.defer(50, this);
3963         }
3964     },
3965
3966     doFocus : function(){
3967         if(!this.hidden){
3968             this.focusEl.focus();
3969         }
3970     },
3971
3972     /**
3973      * Hides this menu and optionally all parent menus
3974      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3975      */
3976     hide : function(deep)
3977     {
3978         if (false === this.fireEvent("beforehide", this)) {
3979             Roo.log("hide canceled");
3980             return;
3981         }
3982         this.hideMenuItems();
3983         if(this.el && this.isVisible()){
3984            
3985             if(this.activeItem){
3986                 this.activeItem.deactivate();
3987                 this.activeItem = null;
3988             }
3989             if (this.triggerEl) {
3990                 this.triggerEl.removeClass('open');
3991             }
3992             
3993             this.el.removeClass('show');
3994             this.hidden = true;
3995             this.fireEvent("hide", this);
3996         }
3997         if(deep === true && this.parentMenu){
3998             this.parentMenu.hide(true);
3999         }
4000     },
4001     
4002     onTriggerClick : function(e)
4003     {
4004         Roo.log('trigger click');
4005         
4006         var target = e.getTarget();
4007         
4008         Roo.log(target.nodeName.toLowerCase());
4009         
4010         if(target.nodeName.toLowerCase() === 'i'){
4011             e.preventDefault();
4012         }
4013         
4014     },
4015     
4016     onTriggerPress  : function(e)
4017     {
4018         Roo.log('trigger press');
4019         //Roo.log(e.getTarget());
4020        // Roo.log(this.triggerEl.dom);
4021        
4022         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4023         var pel = Roo.get(e.getTarget());
4024         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4025             Roo.log('is treeview or dropdown?');
4026             return;
4027         }
4028         
4029         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4030             return;
4031         }
4032         
4033         if (this.isVisible()) {
4034             Roo.log('hide');
4035             this.hide();
4036         } else {
4037             Roo.log('show');
4038             
4039             this.show(this.triggerEl, this.align, false);
4040         }
4041         
4042         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4043             e.stopEvent();
4044         }
4045         
4046     },
4047        
4048     
4049     hideMenuItems : function()
4050     {
4051         Roo.log("hide Menu Items");
4052         if (!this.el) { 
4053             return;
4054         }
4055         
4056         this.el.select('.open',true).each(function(aa) {
4057             
4058             aa.removeClass('open');
4059          
4060         });
4061     },
4062     addxtypeChild : function (tree, cntr) {
4063         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4064           
4065         this.menuitems.add(comp);
4066         return comp;
4067
4068     },
4069     getEl : function()
4070     {
4071         Roo.log(this.el);
4072         return this.el;
4073     },
4074     
4075     clear : function()
4076     {
4077         this.getEl().dom.innerHTML = '';
4078         this.menuitems.clear();
4079     }
4080 });
4081
4082  
4083  /*
4084  * - LGPL
4085  *
4086  * menu item
4087  * 
4088  */
4089
4090
4091 /**
4092  * @class Roo.bootstrap.MenuItem
4093  * @extends Roo.bootstrap.Component
4094  * Bootstrap MenuItem class
4095  * @cfg {String} html the menu label
4096  * @cfg {String} href the link
4097  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4098  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4099  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4100  * @cfg {String} fa favicon to show on left of menu item.
4101  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4102  * 
4103  * 
4104  * @constructor
4105  * Create a new MenuItem
4106  * @param {Object} config The config object
4107  */
4108
4109
4110 Roo.bootstrap.MenuItem = function(config){
4111     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4112     this.addEvents({
4113         // raw events
4114         /**
4115          * @event click
4116          * The raw click event for the entire grid.
4117          * @param {Roo.bootstrap.MenuItem} this
4118          * @param {Roo.EventObject} e
4119          */
4120         "click" : true
4121     });
4122 };
4123
4124 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4125     
4126     href : false,
4127     html : false,
4128     preventDefault: false,
4129     isContainer : false,
4130     active : false,
4131     fa: false,
4132     
4133     getAutoCreate : function(){
4134         
4135         if(this.isContainer){
4136             return {
4137                 tag: 'li',
4138                 cls: 'dropdown-menu-item '
4139             };
4140         }
4141         var ctag = {
4142             tag: 'span',
4143             html: 'Link'
4144         };
4145         
4146         var anc = {
4147             tag : 'a',
4148             cls : 'dropdown-item',
4149             href : '#',
4150             cn : [  ]
4151         };
4152         
4153         if (this.fa !== false) {
4154             anc.cn.push({
4155                 tag : 'i',
4156                 cls : 'fa fa-' + this.fa
4157             });
4158         }
4159         
4160         anc.cn.push(ctag);
4161         
4162         
4163         var cfg= {
4164             tag: 'li',
4165             cls: 'dropdown-menu-item',
4166             cn: [ anc ]
4167         };
4168         if (this.parent().type == 'treeview') {
4169             cfg.cls = 'treeview-menu';
4170         }
4171         if (this.active) {
4172             cfg.cls += ' active';
4173         }
4174         
4175         
4176         
4177         anc.href = this.href || cfg.cn[0].href ;
4178         ctag.html = this.html || cfg.cn[0].html ;
4179         return cfg;
4180     },
4181     
4182     initEvents: function()
4183     {
4184         if (this.parent().type == 'treeview') {
4185             this.el.select('a').on('click', this.onClick, this);
4186         }
4187         
4188         if (this.menu) {
4189             this.menu.parentType = this.xtype;
4190             this.menu.triggerEl = this.el;
4191             this.menu = this.addxtype(Roo.apply({}, this.menu));
4192         }
4193         
4194     },
4195     onClick : function(e)
4196     {
4197         Roo.log('item on click ');
4198         
4199         if(this.preventDefault){
4200             e.preventDefault();
4201         }
4202         //this.parent().hideMenuItems();
4203         
4204         this.fireEvent('click', this, e);
4205     },
4206     getEl : function()
4207     {
4208         return this.el;
4209     } 
4210 });
4211
4212  
4213
4214  /*
4215  * - LGPL
4216  *
4217  * menu separator
4218  * 
4219  */
4220
4221
4222 /**
4223  * @class Roo.bootstrap.MenuSeparator
4224  * @extends Roo.bootstrap.Component
4225  * Bootstrap MenuSeparator class
4226  * 
4227  * @constructor
4228  * Create a new MenuItem
4229  * @param {Object} config The config object
4230  */
4231
4232
4233 Roo.bootstrap.MenuSeparator = function(config){
4234     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4235 };
4236
4237 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4238     
4239     getAutoCreate : function(){
4240         var cfg = {
4241             cls: 'divider',
4242             tag : 'li'
4243         };
4244         
4245         return cfg;
4246     }
4247    
4248 });
4249
4250  
4251
4252  
4253 /*
4254 * Licence: LGPL
4255 */
4256
4257 /**
4258  * @class Roo.bootstrap.Modal
4259  * @extends Roo.bootstrap.Component
4260  * @builder-top
4261  * @parent none
4262  * Bootstrap Modal class
4263  * @cfg {String} title Title of dialog
4264  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4265  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4266  * @cfg {Boolean} specificTitle default false
4267  * @cfg {Array} buttons Array of buttons or standard button set..
4268  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4269  * @cfg {Boolean} animate default true
4270  * @cfg {Boolean} allow_close default true
4271  * @cfg {Boolean} fitwindow default false
4272  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4273  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4274  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4275  * @cfg {String} size (sm|lg|xl) default empty
4276  * @cfg {Number} max_width set the max width of modal
4277  * @cfg {Boolean} editableTitle can the title be edited
4278
4279  *
4280  *
4281  * @constructor
4282  * Create a new Modal Dialog
4283  * @param {Object} config The config object
4284  */
4285
4286 Roo.bootstrap.Modal = function(config){
4287     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4288     this.addEvents({
4289         // raw events
4290         /**
4291          * @event btnclick
4292          * The raw btnclick event for the button
4293          * @param {Roo.EventObject} e
4294          */
4295         "btnclick" : true,
4296         /**
4297          * @event resize
4298          * Fire when dialog resize
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} e
4301          */
4302         "resize" : true,
4303         /**
4304          * @event titlechanged
4305          * Fire when the editable title has been changed
4306          * @param {Roo.bootstrap.Modal} this
4307          * @param {Roo.EventObject} value
4308          */
4309         "titlechanged" : true 
4310         
4311     });
4312     this.buttons = this.buttons || [];
4313
4314     if (this.tmpl) {
4315         this.tmpl = Roo.factory(this.tmpl);
4316     }
4317
4318 };
4319
4320 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4321
4322     title : 'test dialog',
4323
4324     buttons : false,
4325
4326     // set on load...
4327
4328     html: false,
4329
4330     tmp: false,
4331
4332     specificTitle: false,
4333
4334     buttonPosition: 'right',
4335
4336     allow_close : true,
4337
4338     animate : true,
4339
4340     fitwindow: false,
4341     
4342      // private
4343     dialogEl: false,
4344     bodyEl:  false,
4345     footerEl:  false,
4346     titleEl:  false,
4347     closeEl:  false,
4348
4349     size: '',
4350     
4351     max_width: 0,
4352     
4353     max_height: 0,
4354     
4355     fit_content: false,
4356     editableTitle  : false,
4357
4358     onRender : function(ct, position)
4359     {
4360         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4361
4362         if(!this.el){
4363             var cfg = Roo.apply({},  this.getAutoCreate());
4364             cfg.id = Roo.id();
4365             //if(!cfg.name){
4366             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367             //}
4368             //if (!cfg.name.length) {
4369             //    delete cfg.name;
4370            // }
4371             if (this.cls) {
4372                 cfg.cls += ' ' + this.cls;
4373             }
4374             if (this.style) {
4375                 cfg.style = this.style;
4376             }
4377             this.el = Roo.get(document.body).createChild(cfg, position);
4378         }
4379         //var type = this.el.dom.type;
4380
4381
4382         if(this.tabIndex !== undefined){
4383             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4384         }
4385
4386         this.dialogEl = this.el.select('.modal-dialog',true).first();
4387         this.bodyEl = this.el.select('.modal-body',true).first();
4388         this.closeEl = this.el.select('.modal-header .close', true).first();
4389         this.headerEl = this.el.select('.modal-header',true).first();
4390         this.titleEl = this.el.select('.modal-title',true).first();
4391         this.footerEl = this.el.select('.modal-footer',true).first();
4392
4393         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394         
4395         //this.el.addClass("x-dlg-modal");
4396
4397         if (this.buttons.length) {
4398             Roo.each(this.buttons, function(bb) {
4399                 var b = Roo.apply({}, bb);
4400                 b.xns = b.xns || Roo.bootstrap;
4401                 b.xtype = b.xtype || 'Button';
4402                 if (typeof(b.listeners) == 'undefined') {
4403                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4404                 }
4405
4406                 var btn = Roo.factory(b);
4407
4408                 btn.render(this.getButtonContainer());
4409
4410             },this);
4411         }
4412         // render the children.
4413         var nitems = [];
4414
4415         if(typeof(this.items) != 'undefined'){
4416             var items = this.items;
4417             delete this.items;
4418
4419             for(var i =0;i < items.length;i++) {
4420                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4421             }
4422         }
4423
4424         this.items = nitems;
4425
4426         // where are these used - they used to be body/close/footer
4427
4428
4429         this.initEvents();
4430         //this.el.addClass([this.fieldClass, this.cls]);
4431
4432     },
4433
4434     getAutoCreate : function()
4435     {
4436         // we will default to modal-body-overflow - might need to remove or make optional later.
4437         var bdy = {
4438                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4439                 html : this.html || ''
4440         };
4441
4442         var title = {
4443             tag: 'h5',
4444             cls : 'modal-title',
4445             html : this.title
4446         };
4447
4448         if(this.specificTitle){ // WTF is this?
4449             title = this.title;
4450         }
4451
4452         var header = [];
4453         if (this.allow_close && Roo.bootstrap.version == 3) {
4454             header.push({
4455                 tag: 'button',
4456                 cls : 'close',
4457                 html : '&times'
4458             });
4459         }
4460
4461         header.push(title);
4462
4463         if (this.editableTitle) {
4464             header.push({
4465                 cls: 'form-control roo-editable-title d-none',
4466                 tag: 'input',
4467                 type: 'text'
4468             });
4469         }
4470         
4471         if (this.allow_close && Roo.bootstrap.version == 4) {
4472             header.push({
4473                 tag: 'button',
4474                 cls : 'close',
4475                 html : '&times'
4476             });
4477         }
4478         
4479         var size = '';
4480
4481         if(this.size.length){
4482             size = 'modal-' + this.size;
4483         }
4484         
4485         var footer = Roo.bootstrap.version == 3 ?
4486             {
4487                 cls : 'modal-footer',
4488                 cn : [
4489                     {
4490                         tag: 'div',
4491                         cls: 'btn-' + this.buttonPosition
4492                     }
4493                 ]
4494
4495             } :
4496             {  // BS4 uses mr-auto on left buttons....
4497                 cls : 'modal-footer'
4498             };
4499
4500             
4501
4502         
4503         
4504         var modal = {
4505             cls: "modal",
4506              cn : [
4507                 {
4508                     cls: "modal-dialog " + size,
4509                     cn : [
4510                         {
4511                             cls : "modal-content",
4512                             cn : [
4513                                 {
4514                                     cls : 'modal-header',
4515                                     cn : header
4516                                 },
4517                                 bdy,
4518                                 footer
4519                             ]
4520
4521                         }
4522                     ]
4523
4524                 }
4525             ]
4526         };
4527
4528         if(this.animate){
4529             modal.cls += ' fade';
4530         }
4531
4532         return modal;
4533
4534     },
4535     getChildContainer : function() {
4536
4537          return this.bodyEl;
4538
4539     },
4540     getButtonContainer : function() {
4541         
4542          return Roo.bootstrap.version == 4 ?
4543             this.el.select('.modal-footer',true).first()
4544             : this.el.select('.modal-footer div',true).first();
4545
4546     },
4547     initEvents : function()
4548     {
4549         if (this.allow_close) {
4550             this.closeEl.on('click', this.hide, this);
4551         }
4552         Roo.EventManager.onWindowResize(this.resize, this, true);
4553         if (this.editableTitle) {
4554             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4555             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4556             this.headerEditEl.on('keyup', function(e) {
4557                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4558                         this.toggleHeaderInput(false)
4559                     }
4560                 }, this);
4561             this.headerEditEl.on('blur', function(e) {
4562                 this.toggleHeaderInput(false)
4563             },this);
4564         }
4565
4566     },
4567   
4568
4569     resize : function()
4570     {
4571         this.maskEl.setSize(
4572             Roo.lib.Dom.getViewWidth(true),
4573             Roo.lib.Dom.getViewHeight(true)
4574         );
4575         
4576         if (this.fitwindow) {
4577             
4578            this.dialogEl.setStyle( { 'max-width' : '100%' });
4579             this.setSize(
4580                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4581                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4582             );
4583             return;
4584         }
4585         
4586         if(this.max_width !== 0) {
4587             
4588             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4589             
4590             if(this.height) {
4591                 this.setSize(w, this.height);
4592                 return;
4593             }
4594             
4595             if(this.max_height) {
4596                 this.setSize(w,Math.min(
4597                     this.max_height,
4598                     Roo.lib.Dom.getViewportHeight(true) - 60
4599                 ));
4600                 
4601                 return;
4602             }
4603             
4604             if(!this.fit_content) {
4605                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4606                 return;
4607             }
4608             
4609             this.setSize(w, Math.min(
4610                 60 +
4611                 this.headerEl.getHeight() + 
4612                 this.footerEl.getHeight() + 
4613                 this.getChildHeight(this.bodyEl.dom.childNodes),
4614                 Roo.lib.Dom.getViewportHeight(true) - 60)
4615             );
4616         }
4617         
4618     },
4619
4620     setSize : function(w,h)
4621     {
4622         if (!w && !h) {
4623             return;
4624         }
4625         
4626         this.resizeTo(w,h);
4627     },
4628
4629     show : function() {
4630
4631         if (!this.rendered) {
4632             this.render();
4633         }
4634         this.toggleHeaderInput(false);
4635         //this.el.setStyle('display', 'block');
4636         this.el.removeClass('hideing');
4637         this.el.dom.style.display='block';
4638         
4639         Roo.get(document.body).addClass('modal-open');
4640  
4641         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4642             
4643             (function(){
4644                 this.el.addClass('show');
4645                 this.el.addClass('in');
4646             }).defer(50, this);
4647         }else{
4648             this.el.addClass('show');
4649             this.el.addClass('in');
4650         }
4651
4652         // not sure how we can show data in here..
4653         //if (this.tmpl) {
4654         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4655         //}
4656
4657         Roo.get(document.body).addClass("x-body-masked");
4658         
4659         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4660         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4661         this.maskEl.dom.style.display = 'block';
4662         this.maskEl.addClass('show');
4663         
4664         
4665         this.resize();
4666         
4667         this.fireEvent('show', this);
4668
4669         // set zindex here - otherwise it appears to be ignored...
4670         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4671
4672         (function () {
4673             this.items.forEach( function(e) {
4674                 e.layout ? e.layout() : false;
4675
4676             });
4677         }).defer(100,this);
4678
4679     },
4680     hide : function()
4681     {
4682         if(this.fireEvent("beforehide", this) !== false){
4683             
4684             this.maskEl.removeClass('show');
4685             
4686             this.maskEl.dom.style.display = '';
4687             Roo.get(document.body).removeClass("x-body-masked");
4688             this.el.removeClass('in');
4689             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4690
4691             if(this.animate){ // why
4692                 this.el.addClass('hideing');
4693                 this.el.removeClass('show');
4694                 (function(){
4695                     if (!this.el.hasClass('hideing')) {
4696                         return; // it's been shown again...
4697                     }
4698                     
4699                     this.el.dom.style.display='';
4700
4701                     Roo.get(document.body).removeClass('modal-open');
4702                     this.el.removeClass('hideing');
4703                 }).defer(150,this);
4704                 
4705             }else{
4706                 this.el.removeClass('show');
4707                 this.el.dom.style.display='';
4708                 Roo.get(document.body).removeClass('modal-open');
4709
4710             }
4711             this.fireEvent('hide', this);
4712         }
4713     },
4714     isVisible : function()
4715     {
4716         
4717         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4718         
4719     },
4720
4721     addButton : function(str, cb)
4722     {
4723
4724
4725         var b = Roo.apply({}, { html : str } );
4726         b.xns = b.xns || Roo.bootstrap;
4727         b.xtype = b.xtype || 'Button';
4728         if (typeof(b.listeners) == 'undefined') {
4729             b.listeners = { click : cb.createDelegate(this)  };
4730         }
4731
4732         var btn = Roo.factory(b);
4733
4734         btn.render(this.getButtonContainer());
4735
4736         return btn;
4737
4738     },
4739
4740     setDefaultButton : function(btn)
4741     {
4742         //this.el.select('.modal-footer').()
4743     },
4744
4745     resizeTo: function(w,h)
4746     {
4747         this.dialogEl.setWidth(w);
4748         
4749         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4750
4751         this.bodyEl.setHeight(h - diff);
4752         
4753         this.fireEvent('resize', this);
4754     },
4755     
4756     setContentSize  : function(w, h)
4757     {
4758
4759     },
4760     onButtonClick: function(btn,e)
4761     {
4762         //Roo.log([a,b,c]);
4763         this.fireEvent('btnclick', btn.name, e);
4764     },
4765      /**
4766      * Set the title of the Dialog
4767      * @param {String} str new Title
4768      */
4769     setTitle: function(str) {
4770         this.titleEl.dom.innerHTML = str;
4771         this.title = str;
4772     },
4773     /**
4774      * Set the body of the Dialog
4775      * @param {String} str new Title
4776      */
4777     setBody: function(str) {
4778         this.bodyEl.dom.innerHTML = str;
4779     },
4780     /**
4781      * Set the body of the Dialog using the template
4782      * @param {Obj} data - apply this data to the template and replace the body contents.
4783      */
4784     applyBody: function(obj)
4785     {
4786         if (!this.tmpl) {
4787             Roo.log("Error - using apply Body without a template");
4788             //code
4789         }
4790         this.tmpl.overwrite(this.bodyEl, obj);
4791     },
4792     
4793     getChildHeight : function(child_nodes)
4794     {
4795         if(
4796             !child_nodes ||
4797             child_nodes.length == 0
4798         ) {
4799             return 0;
4800         }
4801         
4802         var child_height = 0;
4803         
4804         for(var i = 0; i < child_nodes.length; i++) {
4805             
4806             /*
4807             * for modal with tabs...
4808             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4809                 
4810                 var layout_childs = child_nodes[i].childNodes;
4811                 
4812                 for(var j = 0; j < layout_childs.length; j++) {
4813                     
4814                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4815                         
4816                         var layout_body_childs = layout_childs[j].childNodes;
4817                         
4818                         for(var k = 0; k < layout_body_childs.length; k++) {
4819                             
4820                             if(layout_body_childs[k].classList.contains('navbar')) {
4821                                 child_height += layout_body_childs[k].offsetHeight;
4822                                 continue;
4823                             }
4824                             
4825                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4826                                 
4827                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4828                                 
4829                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4830                                     
4831                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4832                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4833                                         continue;
4834                                     }
4835                                     
4836                                 }
4837                                 
4838                             }
4839                             
4840                         }
4841                     }
4842                 }
4843                 continue;
4844             }
4845             */
4846             
4847             child_height += child_nodes[i].offsetHeight;
4848             // Roo.log(child_nodes[i].offsetHeight);
4849         }
4850         
4851         return child_height;
4852     },
4853     toggleHeaderInput : function(is_edit)
4854     {
4855         if (!this.editableTitle) {
4856             return; // not editable.
4857         }
4858         if (is_edit && this.is_header_editing) {
4859             return; // already editing..
4860         }
4861         if (is_edit) {
4862     
4863             this.headerEditEl.dom.value = this.title;
4864             this.headerEditEl.removeClass('d-none');
4865             this.headerEditEl.dom.focus();
4866             this.titleEl.addClass('d-none');
4867             
4868             this.is_header_editing = true;
4869             return
4870         }
4871         // flip back to not editing.
4872         this.title = this.headerEditEl.dom.value;
4873         this.headerEditEl.addClass('d-none');
4874         this.titleEl.removeClass('d-none');
4875         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4876         this.is_header_editing = false;
4877         this.fireEvent('titlechanged', this, this.title);
4878     
4879             
4880         
4881     }
4882
4883 });
4884
4885
4886 Roo.apply(Roo.bootstrap.Modal,  {
4887     /**
4888          * Button config that displays a single OK button
4889          * @type Object
4890          */
4891         OK :  [{
4892             name : 'ok',
4893             weight : 'primary',
4894             html : 'OK'
4895         }],
4896         /**
4897          * Button config that displays Yes and No buttons
4898          * @type Object
4899          */
4900         YESNO : [
4901             {
4902                 name  : 'no',
4903                 html : 'No'
4904             },
4905             {
4906                 name  :'yes',
4907                 weight : 'primary',
4908                 html : 'Yes'
4909             }
4910         ],
4911
4912         /**
4913          * Button config that displays OK and Cancel buttons
4914          * @type Object
4915          */
4916         OKCANCEL : [
4917             {
4918                name : 'cancel',
4919                 html : 'Cancel'
4920             },
4921             {
4922                 name : 'ok',
4923                 weight : 'primary',
4924                 html : 'OK'
4925             }
4926         ],
4927         /**
4928          * Button config that displays Yes, No and Cancel buttons
4929          * @type Object
4930          */
4931         YESNOCANCEL : [
4932             {
4933                 name : 'yes',
4934                 weight : 'primary',
4935                 html : 'Yes'
4936             },
4937             {
4938                 name : 'no',
4939                 html : 'No'
4940             },
4941             {
4942                 name : 'cancel',
4943                 html : 'Cancel'
4944             }
4945         ],
4946         
4947         zIndex : 10001
4948 });
4949
4950 /*
4951  * - LGPL
4952  *
4953  * messagebox - can be used as a replace
4954  * 
4955  */
4956 /**
4957  * @class Roo.MessageBox
4958  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4959  * Example usage:
4960  *<pre><code>
4961 // Basic alert:
4962 Roo.Msg.alert('Status', 'Changes saved successfully.');
4963
4964 // Prompt for user data:
4965 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4966     if (btn == 'ok'){
4967         // process text value...
4968     }
4969 });
4970
4971 // Show a dialog using config options:
4972 Roo.Msg.show({
4973    title:'Save Changes?',
4974    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4975    buttons: Roo.Msg.YESNOCANCEL,
4976    fn: processResult,
4977    animEl: 'elId'
4978 });
4979 </code></pre>
4980  * @singleton
4981  */
4982 Roo.bootstrap.MessageBox = function(){
4983     var dlg, opt, mask, waitTimer;
4984     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4985     var buttons, activeTextEl, bwidth;
4986
4987     
4988     // private
4989     var handleButton = function(button){
4990         dlg.hide();
4991         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4992     };
4993
4994     // private
4995     var handleHide = function(){
4996         if(opt && opt.cls){
4997             dlg.el.removeClass(opt.cls);
4998         }
4999         //if(waitTimer){
5000         //    Roo.TaskMgr.stop(waitTimer);
5001         //    waitTimer = null;
5002         //}
5003     };
5004
5005     // private
5006     var updateButtons = function(b){
5007         var width = 0;
5008         if(!b){
5009             buttons["ok"].hide();
5010             buttons["cancel"].hide();
5011             buttons["yes"].hide();
5012             buttons["no"].hide();
5013             dlg.footerEl.hide();
5014             
5015             return width;
5016         }
5017         dlg.footerEl.show();
5018         for(var k in buttons){
5019             if(typeof buttons[k] != "function"){
5020                 if(b[k]){
5021                     buttons[k].show();
5022                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5023                     width += buttons[k].el.getWidth()+15;
5024                 }else{
5025                     buttons[k].hide();
5026                 }
5027             }
5028         }
5029         return width;
5030     };
5031
5032     // private
5033     var handleEsc = function(d, k, e){
5034         if(opt && opt.closable !== false){
5035             dlg.hide();
5036         }
5037         if(e){
5038             e.stopEvent();
5039         }
5040     };
5041
5042     return {
5043         /**
5044          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5045          * @return {Roo.BasicDialog} The BasicDialog element
5046          */
5047         getDialog : function(){
5048            if(!dlg){
5049                 dlg = new Roo.bootstrap.Modal( {
5050                     //draggable: true,
5051                     //resizable:false,
5052                     //constraintoviewport:false,
5053                     //fixedcenter:true,
5054                     //collapsible : false,
5055                     //shim:true,
5056                     //modal: true,
5057                 //    width: 'auto',
5058                   //  height:100,
5059                     //buttonAlign:"center",
5060                     closeClick : function(){
5061                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5062                             handleButton("no");
5063                         }else{
5064                             handleButton("cancel");
5065                         }
5066                     }
5067                 });
5068                 dlg.render();
5069                 dlg.on("hide", handleHide);
5070                 mask = dlg.mask;
5071                 //dlg.addKeyListener(27, handleEsc);
5072                 buttons = {};
5073                 this.buttons = buttons;
5074                 var bt = this.buttonText;
5075                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5076                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5077                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5078                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5079                 //Roo.log(buttons);
5080                 bodyEl = dlg.bodyEl.createChild({
5081
5082                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5083                         '<textarea class="roo-mb-textarea"></textarea>' +
5084                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5085                 });
5086                 msgEl = bodyEl.dom.firstChild;
5087                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5088                 textboxEl.enableDisplayMode();
5089                 textboxEl.addKeyListener([10,13], function(){
5090                     if(dlg.isVisible() && opt && opt.buttons){
5091                         if(opt.buttons.ok){
5092                             handleButton("ok");
5093                         }else if(opt.buttons.yes){
5094                             handleButton("yes");
5095                         }
5096                     }
5097                 });
5098                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5099                 textareaEl.enableDisplayMode();
5100                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5101                 progressEl.enableDisplayMode();
5102                 
5103                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5104                 var pf = progressEl.dom.firstChild;
5105                 if (pf) {
5106                     pp = Roo.get(pf.firstChild);
5107                     pp.setHeight(pf.offsetHeight);
5108                 }
5109                 
5110             }
5111             return dlg;
5112         },
5113
5114         /**
5115          * Updates the message box body text
5116          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5117          * the XHTML-compliant non-breaking space character '&amp;#160;')
5118          * @return {Roo.MessageBox} This message box
5119          */
5120         updateText : function(text)
5121         {
5122             if(!dlg.isVisible() && !opt.width){
5123                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5124                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5125             }
5126             msgEl.innerHTML = text || '&#160;';
5127       
5128             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5129             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5130             var w = Math.max(
5131                     Math.min(opt.width || cw , this.maxWidth), 
5132                     Math.max(opt.minWidth || this.minWidth, bwidth)
5133             );
5134             if(opt.prompt){
5135                 activeTextEl.setWidth(w);
5136             }
5137             if(dlg.isVisible()){
5138                 dlg.fixedcenter = false;
5139             }
5140             // to big, make it scroll. = But as usual stupid IE does not support
5141             // !important..
5142             
5143             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5144                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5145                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.height = '';
5148                 bodyEl.dom.style.overflowY = '';
5149             }
5150             if (cw > w) {
5151                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5152             } else {
5153                 bodyEl.dom.style.overflowX = '';
5154             }
5155             
5156             dlg.setContentSize(w, bodyEl.getHeight());
5157             if(dlg.isVisible()){
5158                 dlg.fixedcenter = true;
5159             }
5160             return this;
5161         },
5162
5163         /**
5164          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5165          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5166          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5167          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5168          * @return {Roo.MessageBox} This message box
5169          */
5170         updateProgress : function(value, text){
5171             if(text){
5172                 this.updateText(text);
5173             }
5174             
5175             if (pp) { // weird bug on my firefox - for some reason this is not defined
5176                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5177                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5178             }
5179             return this;
5180         },        
5181
5182         /**
5183          * Returns true if the message box is currently displayed
5184          * @return {Boolean} True if the message box is visible, else false
5185          */
5186         isVisible : function(){
5187             return dlg && dlg.isVisible();  
5188         },
5189
5190         /**
5191          * Hides the message box if it is displayed
5192          */
5193         hide : function(){
5194             if(this.isVisible()){
5195                 dlg.hide();
5196             }  
5197         },
5198
5199         /**
5200          * Displays a new message box, or reinitializes an existing message box, based on the config options
5201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5202          * The following config object properties are supported:
5203          * <pre>
5204 Property    Type             Description
5205 ----------  ---------------  ------------------------------------------------------------------------------------
5206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5207                                    closes (defaults to undefined)
5208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5211                                    progress and wait dialogs will ignore this property and always hide the
5212                                    close button as they can only be closed programmatically.
5213 cls               String           A custom CSS class to apply to the message box element
5214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5215                                    displayed (defaults to 75)
5216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5217                                    function will be btn (the name of the button that was clicked, if applicable,
5218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5219                                    Progress and wait dialogs will ignore this option since they do not respond to
5220                                    user actions and can only be closed programmatically, so any required function
5221                                    should be called by the same code after it closes the dialog.
5222 icon              String           A CSS class that provides a background image to be used as an icon for
5223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5226 modal             Boolean          False to allow user interaction with the page while the message box is
5227                                    displayed (defaults to true)
5228 msg               String           A string that will replace the existing message box body text (defaults
5229                                    to the XHTML-compliant non-breaking space character '&#160;')
5230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5231 progress          Boolean          True to display a progress bar (defaults to false)
5232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5235 title             String           The title text
5236 value             String           The string value to set into the active textbox element if displayed
5237 wait              Boolean          True to display a progress bar (defaults to false)
5238 width             Number           The width of the dialog in pixels
5239 </pre>
5240          *
5241          * Example usage:
5242          * <pre><code>
5243 Roo.Msg.show({
5244    title: 'Address',
5245    msg: 'Please enter your address:',
5246    width: 300,
5247    buttons: Roo.MessageBox.OKCANCEL,
5248    multiline: true,
5249    fn: saveAddress,
5250    animEl: 'addAddressBtn'
5251 });
5252 </code></pre>
5253          * @param {Object} config Configuration options
5254          * @return {Roo.MessageBox} This message box
5255          */
5256         show : function(options)
5257         {
5258             
5259             // this causes nightmares if you show one dialog after another
5260             // especially on callbacks..
5261              
5262             if(this.isVisible()){
5263                 
5264                 this.hide();
5265                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5266                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5267                 Roo.log("New Dialog Message:" +  options.msg )
5268                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5269                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5270                 
5271             }
5272             var d = this.getDialog();
5273             opt = options;
5274             d.setTitle(opt.title || "&#160;");
5275             d.closeEl.setDisplayed(opt.closable !== false);
5276             activeTextEl = textboxEl;
5277             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5278             if(opt.prompt){
5279                 if(opt.multiline){
5280                     textboxEl.hide();
5281                     textareaEl.show();
5282                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5283                         opt.multiline : this.defaultTextHeight);
5284                     activeTextEl = textareaEl;
5285                 }else{
5286                     textboxEl.show();
5287                     textareaEl.hide();
5288                 }
5289             }else{
5290                 textboxEl.hide();
5291                 textareaEl.hide();
5292             }
5293             progressEl.setDisplayed(opt.progress === true);
5294             if (opt.progress) {
5295                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5296             }
5297             this.updateProgress(0);
5298             activeTextEl.dom.value = opt.value || "";
5299             if(opt.prompt){
5300                 dlg.setDefaultButton(activeTextEl);
5301             }else{
5302                 var bs = opt.buttons;
5303                 var db = null;
5304                 if(bs && bs.ok){
5305                     db = buttons["ok"];
5306                 }else if(bs && bs.yes){
5307                     db = buttons["yes"];
5308                 }
5309                 dlg.setDefaultButton(db);
5310             }
5311             bwidth = updateButtons(opt.buttons);
5312             this.updateText(opt.msg);
5313             if(opt.cls){
5314                 d.el.addClass(opt.cls);
5315             }
5316             d.proxyDrag = opt.proxyDrag === true;
5317             d.modal = opt.modal !== false;
5318             d.mask = opt.modal !== false ? mask : false;
5319             if(!d.isVisible()){
5320                 // force it to the end of the z-index stack so it gets a cursor in FF
5321                 document.body.appendChild(dlg.el.dom);
5322                 d.animateTarget = null;
5323                 d.show(options.animEl);
5324             }
5325             return this;
5326         },
5327
5328         /**
5329          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5330          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5331          * and closing the message box when the process is complete.
5332          * @param {String} title The title bar text
5333          * @param {String} msg The message box body text
5334          * @return {Roo.MessageBox} This message box
5335          */
5336         progress : function(title, msg){
5337             this.show({
5338                 title : title,
5339                 msg : msg,
5340                 buttons: false,
5341                 progress:true,
5342                 closable:false,
5343                 minWidth: this.minProgressWidth,
5344                 modal : true
5345             });
5346             return this;
5347         },
5348
5349         /**
5350          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5351          * If a callback function is passed it will be called after the user clicks the button, and the
5352          * id of the button that was clicked will be passed as the only parameter to the callback
5353          * (could also be the top-right close button).
5354          * @param {String} title The title bar text
5355          * @param {String} msg The message box body text
5356          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5357          * @param {Object} scope (optional) The scope of the callback function
5358          * @return {Roo.MessageBox} This message box
5359          */
5360         alert : function(title, msg, fn, scope)
5361         {
5362             this.show({
5363                 title : title,
5364                 msg : msg,
5365                 buttons: this.OK,
5366                 fn: fn,
5367                 closable : false,
5368                 scope : scope,
5369                 modal : true
5370             });
5371             return this;
5372         },
5373
5374         /**
5375          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5376          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5377          * You are responsible for closing the message box when the process is complete.
5378          * @param {String} msg The message box body text
5379          * @param {String} title (optional) The title bar text
5380          * @return {Roo.MessageBox} This message box
5381          */
5382         wait : function(msg, title){
5383             this.show({
5384                 title : title,
5385                 msg : msg,
5386                 buttons: false,
5387                 closable:false,
5388                 progress:true,
5389                 modal:true,
5390                 width:300,
5391                 wait:true
5392             });
5393             waitTimer = Roo.TaskMgr.start({
5394                 run: function(i){
5395                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396                 },
5397                 interval: 1000
5398             });
5399             return this;
5400         },
5401
5402         /**
5403          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5404          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5405          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5406          * @param {String} title The title bar text
5407          * @param {String} msg The message box body text
5408          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5409          * @param {Object} scope (optional) The scope of the callback function
5410          * @return {Roo.MessageBox} This message box
5411          */
5412         confirm : function(title, msg, fn, scope){
5413             this.show({
5414                 title : title,
5415                 msg : msg,
5416                 buttons: this.YESNO,
5417                 fn: fn,
5418                 scope : scope,
5419                 modal : true
5420             });
5421             return this;
5422         },
5423
5424         /**
5425          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5426          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5427          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5428          * (could also be the top-right close button) and the text that was entered will be passed as the two
5429          * parameters to the callback.
5430          * @param {String} title The title bar text
5431          * @param {String} msg The message box body text
5432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5433          * @param {Object} scope (optional) The scope of the callback function
5434          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5435          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5436          * @return {Roo.MessageBox} This message box
5437          */
5438         prompt : function(title, msg, fn, scope, multiline){
5439             this.show({
5440                 title : title,
5441                 msg : msg,
5442                 buttons: this.OKCANCEL,
5443                 fn: fn,
5444                 minWidth:250,
5445                 scope : scope,
5446                 prompt:true,
5447                 multiline: multiline,
5448                 modal : true
5449             });
5450             return this;
5451         },
5452
5453         /**
5454          * Button config that displays a single OK button
5455          * @type Object
5456          */
5457         OK : {ok:true},
5458         /**
5459          * Button config that displays Yes and No buttons
5460          * @type Object
5461          */
5462         YESNO : {yes:true, no:true},
5463         /**
5464          * Button config that displays OK and Cancel buttons
5465          * @type Object
5466          */
5467         OKCANCEL : {ok:true, cancel:true},
5468         /**
5469          * Button config that displays Yes, No and Cancel buttons
5470          * @type Object
5471          */
5472         YESNOCANCEL : {yes:true, no:true, cancel:true},
5473
5474         /**
5475          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5476          * @type Number
5477          */
5478         defaultTextHeight : 75,
5479         /**
5480          * The maximum width in pixels of the message box (defaults to 600)
5481          * @type Number
5482          */
5483         maxWidth : 600,
5484         /**
5485          * The minimum width in pixels of the message box (defaults to 100)
5486          * @type Number
5487          */
5488         minWidth : 100,
5489         /**
5490          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5491          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5492          * @type Number
5493          */
5494         minProgressWidth : 250,
5495         /**
5496          * An object containing the default button text strings that can be overriden for localized language support.
5497          * Supported properties are: ok, cancel, yes and no.
5498          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5499          * @type Object
5500          */
5501         buttonText : {
5502             ok : "OK",
5503             cancel : "Cancel",
5504             yes : "Yes",
5505             no : "No"
5506         }
5507     };
5508 }();
5509
5510 /**
5511  * Shorthand for {@link Roo.MessageBox}
5512  */
5513 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5514 Roo.Msg = Roo.Msg || Roo.MessageBox;
5515 /*
5516  * - LGPL
5517  *
5518  * navbar
5519  * 
5520  */
5521
5522 /**
5523  * @class Roo.bootstrap.Navbar
5524  * @extends Roo.bootstrap.Component
5525  * Bootstrap Navbar class
5526
5527  * @constructor
5528  * Create a new Navbar
5529  * @param {Object} config The config object
5530  */
5531
5532
5533 Roo.bootstrap.Navbar = function(config){
5534     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5535     this.addEvents({
5536         // raw events
5537         /**
5538          * @event beforetoggle
5539          * Fire before toggle the menu
5540          * @param {Roo.EventObject} e
5541          */
5542         "beforetoggle" : true
5543     });
5544 };
5545
5546 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5547     
5548     
5549    
5550     // private
5551     navItems : false,
5552     loadMask : false,
5553     
5554     
5555     getAutoCreate : function(){
5556         
5557         
5558         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5559         
5560     },
5561     
5562     initEvents :function ()
5563     {
5564         //Roo.log(this.el.select('.navbar-toggle',true));
5565         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5566         
5567         var mark = {
5568             tag: "div",
5569             cls:"x-dlg-mask"
5570         };
5571         
5572         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5573         
5574         var size = this.el.getSize();
5575         this.maskEl.setSize(size.width, size.height);
5576         this.maskEl.enableDisplayMode("block");
5577         this.maskEl.hide();
5578         
5579         if(this.loadMask){
5580             this.maskEl.show();
5581         }
5582     },
5583     
5584     
5585     getChildContainer : function()
5586     {
5587         if (this.el && this.el.select('.collapse').getCount()) {
5588             return this.el.select('.collapse',true).first();
5589         }
5590         
5591         return this.el;
5592     },
5593     
5594     mask : function()
5595     {
5596         this.maskEl.show();
5597     },
5598     
5599     unmask : function()
5600     {
5601         this.maskEl.hide();
5602     },
5603     onToggle : function()
5604     {
5605         
5606         if(this.fireEvent('beforetoggle', this) === false){
5607             return;
5608         }
5609         var ce = this.el.select('.navbar-collapse',true).first();
5610       
5611         if (!ce.hasClass('show')) {
5612            this.expand();
5613         } else {
5614             this.collapse();
5615         }
5616         
5617         
5618     
5619     },
5620     /**
5621      * Expand the navbar pulldown 
5622      */
5623     expand : function ()
5624     {
5625        
5626         var ce = this.el.select('.navbar-collapse',true).first();
5627         if (ce.hasClass('collapsing')) {
5628             return;
5629         }
5630         ce.dom.style.height = '';
5631                // show it...
5632         ce.addClass('in'); // old...
5633         ce.removeClass('collapse');
5634         ce.addClass('show');
5635         var h = ce.getHeight();
5636         Roo.log(h);
5637         ce.removeClass('show');
5638         // at this point we should be able to see it..
5639         ce.addClass('collapsing');
5640         
5641         ce.setHeight(0); // resize it ...
5642         ce.on('transitionend', function() {
5643             //Roo.log('done transition');
5644             ce.removeClass('collapsing');
5645             ce.addClass('show');
5646             ce.removeClass('collapse');
5647
5648             ce.dom.style.height = '';
5649         }, this, { single: true} );
5650         ce.setHeight(h);
5651         ce.dom.scrollTop = 0;
5652     },
5653     /**
5654      * Collapse the navbar pulldown 
5655      */
5656     collapse : function()
5657     {
5658          var ce = this.el.select('.navbar-collapse',true).first();
5659        
5660         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5661             // it's collapsed or collapsing..
5662             return;
5663         }
5664         ce.removeClass('in'); // old...
5665         ce.setHeight(ce.getHeight());
5666         ce.removeClass('show');
5667         ce.addClass('collapsing');
5668         
5669         ce.on('transitionend', function() {
5670             ce.dom.style.height = '';
5671             ce.removeClass('collapsing');
5672             ce.addClass('collapse');
5673         }, this, { single: true} );
5674         ce.setHeight(0);
5675     }
5676     
5677     
5678     
5679 });
5680
5681
5682
5683  
5684
5685  /*
5686  * - LGPL
5687  *
5688  * navbar
5689  * 
5690  */
5691
5692 /**
5693  * @class Roo.bootstrap.NavSimplebar
5694  * @extends Roo.bootstrap.Navbar
5695  * Bootstrap Sidebar class
5696  *
5697  * @cfg {Boolean} inverse is inverted color
5698  * 
5699  * @cfg {String} type (nav | pills | tabs)
5700  * @cfg {Boolean} arrangement stacked | justified
5701  * @cfg {String} align (left | right) alignment
5702  * 
5703  * @cfg {Boolean} main (true|false) main nav bar? default false
5704  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5705  * 
5706  * @cfg {String} tag (header|footer|nav|div) default is nav 
5707
5708  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5709  * 
5710  * 
5711  * @constructor
5712  * Create a new Sidebar
5713  * @param {Object} config The config object
5714  */
5715
5716
5717 Roo.bootstrap.NavSimplebar = function(config){
5718     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5719 };
5720
5721 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5722     
5723     inverse: false,
5724     
5725     type: false,
5726     arrangement: '',
5727     align : false,
5728     
5729     weight : 'light',
5730     
5731     main : false,
5732     
5733     
5734     tag : false,
5735     
5736     
5737     getAutoCreate : function(){
5738         
5739         
5740         var cfg = {
5741             tag : this.tag || 'div',
5742             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5743         };
5744         if (['light','white'].indexOf(this.weight) > -1) {
5745             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5746         }
5747         cfg.cls += ' bg-' + this.weight;
5748         
5749         if (this.inverse) {
5750             cfg.cls += ' navbar-inverse';
5751             
5752         }
5753         
5754         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5755         
5756         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5757             return cfg;
5758         }
5759         
5760         
5761     
5762         
5763         cfg.cn = [
5764             {
5765                 cls: 'nav nav-' + this.xtype,
5766                 tag : 'ul'
5767             }
5768         ];
5769         
5770          
5771         this.type = this.type || 'nav';
5772         if (['tabs','pills'].indexOf(this.type) != -1) {
5773             cfg.cn[0].cls += ' nav-' + this.type
5774         
5775         
5776         } else {
5777             if (this.type!=='nav') {
5778                 Roo.log('nav type must be nav/tabs/pills')
5779             }
5780             cfg.cn[0].cls += ' navbar-nav'
5781         }
5782         
5783         
5784         
5785         
5786         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5787             cfg.cn[0].cls += ' nav-' + this.arrangement;
5788         }
5789         
5790         
5791         if (this.align === 'right') {
5792             cfg.cn[0].cls += ' navbar-right';
5793         }
5794         
5795         
5796         
5797         
5798         return cfg;
5799     
5800         
5801     }
5802     
5803     
5804     
5805 });
5806
5807
5808
5809  
5810
5811  
5812        /*
5813  * - LGPL
5814  *
5815  * navbar
5816  * navbar-fixed-top
5817  * navbar-expand-md  fixed-top 
5818  */
5819
5820 /**
5821  * @class Roo.bootstrap.NavHeaderbar
5822  * @extends Roo.bootstrap.NavSimplebar
5823  * Bootstrap Sidebar class
5824  *
5825  * @cfg {String} brand what is brand
5826  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5827  * @cfg {String} brand_href href of the brand
5828  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5829  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5830  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5831  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5832  * 
5833  * @constructor
5834  * Create a new Sidebar
5835  * @param {Object} config The config object
5836  */
5837
5838
5839 Roo.bootstrap.NavHeaderbar = function(config){
5840     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5841       
5842 };
5843
5844 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5845     
5846     position: '',
5847     brand: '',
5848     brand_href: false,
5849     srButton : true,
5850     autohide : false,
5851     desktopCenter : false,
5852    
5853     
5854     getAutoCreate : function(){
5855         
5856         var   cfg = {
5857             tag: this.nav || 'nav',
5858             cls: 'navbar navbar-expand-md',
5859             role: 'navigation',
5860             cn: []
5861         };
5862         
5863         var cn = cfg.cn;
5864         if (this.desktopCenter) {
5865             cn.push({cls : 'container', cn : []});
5866             cn = cn[0].cn;
5867         }
5868         
5869         if(this.srButton){
5870             var btn = {
5871                 tag: 'button',
5872                 type: 'button',
5873                 cls: 'navbar-toggle navbar-toggler',
5874                 'data-toggle': 'collapse',
5875                 cn: [
5876                     {
5877                         tag: 'span',
5878                         cls: 'sr-only',
5879                         html: 'Toggle navigation'
5880                     },
5881                     {
5882                         tag: 'span',
5883                         cls: 'icon-bar navbar-toggler-icon'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     }
5893                 ]
5894             };
5895             
5896             cn.push( Roo.bootstrap.version == 4 ? btn : {
5897                 tag: 'div',
5898                 cls: 'navbar-header',
5899                 cn: [
5900                     btn
5901                 ]
5902             });
5903         }
5904         
5905         cn.push({
5906             tag: 'div',
5907             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5908             cn : []
5909         });
5910         
5911         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5912         
5913         if (['light','white'].indexOf(this.weight) > -1) {
5914             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5915         }
5916         cfg.cls += ' bg-' + this.weight;
5917         
5918         
5919         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5920             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5921             
5922             // tag can override this..
5923             
5924             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5925         }
5926         
5927         if (this.brand !== '') {
5928             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5929             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5930                 tag: 'a',
5931                 href: this.brand_href ? this.brand_href : '#',
5932                 cls: 'navbar-brand',
5933                 cn: [
5934                 this.brand
5935                 ]
5936             });
5937         }
5938         
5939         if(this.main){
5940             cfg.cls += ' main-nav';
5941         }
5942         
5943         
5944         return cfg;
5945
5946         
5947     },
5948     getHeaderChildContainer : function()
5949     {
5950         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5951             return this.el.select('.navbar-header',true).first();
5952         }
5953         
5954         return this.getChildContainer();
5955     },
5956     
5957     getChildContainer : function()
5958     {
5959          
5960         return this.el.select('.roo-navbar-collapse',true).first();
5961          
5962         
5963     },
5964     
5965     initEvents : function()
5966     {
5967         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5968         
5969         if (this.autohide) {
5970             
5971             var prevScroll = 0;
5972             var ft = this.el;
5973             
5974             Roo.get(document).on('scroll',function(e) {
5975                 var ns = Roo.get(document).getScroll().top;
5976                 var os = prevScroll;
5977                 prevScroll = ns;
5978                 
5979                 if(ns > os){
5980                     ft.removeClass('slideDown');
5981                     ft.addClass('slideUp');
5982                     return;
5983                 }
5984                 ft.removeClass('slideUp');
5985                 ft.addClass('slideDown');
5986                  
5987               
5988           },this);
5989         }
5990     }    
5991     
5992 });
5993
5994
5995
5996  
5997
5998  /*
5999  * - LGPL
6000  *
6001  * navbar
6002  * 
6003  */
6004
6005 /**
6006  * @class Roo.bootstrap.NavSidebar
6007  * @extends Roo.bootstrap.Navbar
6008  * Bootstrap Sidebar class
6009  * 
6010  * @constructor
6011  * Create a new Sidebar
6012  * @param {Object} config The config object
6013  */
6014
6015
6016 Roo.bootstrap.NavSidebar = function(config){
6017     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6018 };
6019
6020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6021     
6022     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6023     
6024     getAutoCreate : function(){
6025         
6026         
6027         return  {
6028             tag: 'div',
6029             cls: 'sidebar sidebar-nav'
6030         };
6031     
6032         
6033     }
6034     
6035     
6036     
6037 });
6038
6039
6040
6041  
6042
6043  /*
6044  * - LGPL
6045  *
6046  * nav group
6047  * 
6048  */
6049
6050 /**
6051  * @class Roo.bootstrap.NavGroup
6052  * @extends Roo.bootstrap.Component
6053  * Bootstrap NavGroup class
6054  * @cfg {String} align (left|right)
6055  * @cfg {Boolean} inverse
6056  * @cfg {String} type (nav|pills|tab) default nav
6057  * @cfg {String} navId - reference Id for navbar.
6058  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6059  * 
6060  * @constructor
6061  * Create a new nav group
6062  * @param {Object} config The config object
6063  */
6064
6065 Roo.bootstrap.NavGroup = function(config){
6066     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6067     this.navItems = [];
6068    
6069     Roo.bootstrap.NavGroup.register(this);
6070      this.addEvents({
6071         /**
6072              * @event changed
6073              * Fires when the active item changes
6074              * @param {Roo.bootstrap.NavGroup} this
6075              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6076              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6077          */
6078         'changed': true
6079      });
6080     
6081 };
6082
6083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6084     
6085     align: '',
6086     inverse: false,
6087     form: false,
6088     type: 'nav',
6089     navId : '',
6090     // private
6091     pilltype : true,
6092     
6093     navItems : false, 
6094     
6095     getAutoCreate : function()
6096     {
6097         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6098         
6099         cfg = {
6100             tag : 'ul',
6101             cls: 'nav' 
6102         };
6103         if (Roo.bootstrap.version == 4) {
6104             if (['tabs','pills'].indexOf(this.type) != -1) {
6105                 cfg.cls += ' nav-' + this.type; 
6106             } else {
6107                 // trying to remove so header bar can right align top?
6108                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6109                     // do not use on header bar... 
6110                     cfg.cls += ' navbar-nav';
6111                 }
6112             }
6113             
6114         } else {
6115             if (['tabs','pills'].indexOf(this.type) != -1) {
6116                 cfg.cls += ' nav-' + this.type
6117             } else {
6118                 if (this.type !== 'nav') {
6119                     Roo.log('nav type must be nav/tabs/pills')
6120                 }
6121                 cfg.cls += ' navbar-nav'
6122             }
6123         }
6124         
6125         if (this.parent() && this.parent().sidebar) {
6126             cfg = {
6127                 tag: 'ul',
6128                 cls: 'dashboard-menu sidebar-menu'
6129             };
6130             
6131             return cfg;
6132         }
6133         
6134         if (this.form === true) {
6135             cfg = {
6136                 tag: 'form',
6137                 cls: 'navbar-form form-inline'
6138             };
6139             //nav navbar-right ml-md-auto
6140             if (this.align === 'right') {
6141                 cfg.cls += ' navbar-right ml-md-auto';
6142             } else {
6143                 cfg.cls += ' navbar-left';
6144             }
6145         }
6146         
6147         if (this.align === 'right') {
6148             cfg.cls += ' navbar-right ml-md-auto';
6149         } else {
6150             cfg.cls += ' mr-auto';
6151         }
6152         
6153         if (this.inverse) {
6154             cfg.cls += ' navbar-inverse';
6155             
6156         }
6157         
6158         
6159         return cfg;
6160     },
6161     /**
6162     * sets the active Navigation item
6163     * @param {Roo.bootstrap.NavItem} the new current navitem
6164     */
6165     setActiveItem : function(item)
6166     {
6167         var prev = false;
6168         Roo.each(this.navItems, function(v){
6169             if (v == item) {
6170                 return ;
6171             }
6172             if (v.isActive()) {
6173                 v.setActive(false, true);
6174                 prev = v;
6175                 
6176             }
6177             
6178         });
6179
6180         item.setActive(true, true);
6181         this.fireEvent('changed', this, item, prev);
6182         
6183         
6184     },
6185     /**
6186     * gets the active Navigation item
6187     * @return {Roo.bootstrap.NavItem} the current navitem
6188     */
6189     getActive : function()
6190     {
6191         
6192         var prev = false;
6193         Roo.each(this.navItems, function(v){
6194             
6195             if (v.isActive()) {
6196                 prev = v;
6197                 
6198             }
6199             
6200         });
6201         return prev;
6202     },
6203     
6204     indexOfNav : function()
6205     {
6206         
6207         var prev = false;
6208         Roo.each(this.navItems, function(v,i){
6209             
6210             if (v.isActive()) {
6211                 prev = i;
6212                 
6213             }
6214             
6215         });
6216         return prev;
6217     },
6218     /**
6219     * adds a Navigation item
6220     * @param {Roo.bootstrap.NavItem} the navitem to add
6221     */
6222     addItem : function(cfg)
6223     {
6224         if (this.form && Roo.bootstrap.version == 4) {
6225             cfg.tag = 'div';
6226         }
6227         var cn = new Roo.bootstrap.NavItem(cfg);
6228         this.register(cn);
6229         cn.parentId = this.id;
6230         cn.onRender(this.el, null);
6231         return cn;
6232     },
6233     /**
6234     * register a Navigation item
6235     * @param {Roo.bootstrap.NavItem} the navitem to add
6236     */
6237     register : function(item)
6238     {
6239         this.navItems.push( item);
6240         item.navId = this.navId;
6241     
6242     },
6243     
6244     /**
6245     * clear all the Navigation item
6246     */
6247    
6248     clearAll : function()
6249     {
6250         this.navItems = [];
6251         this.el.dom.innerHTML = '';
6252     },
6253     
6254     getNavItem: function(tabId)
6255     {
6256         var ret = false;
6257         Roo.each(this.navItems, function(e) {
6258             if (e.tabId == tabId) {
6259                ret =  e;
6260                return false;
6261             }
6262             return true;
6263             
6264         });
6265         return ret;
6266     },
6267     
6268     setActiveNext : function()
6269     {
6270         var i = this.indexOfNav(this.getActive());
6271         if (i > this.navItems.length) {
6272             return;
6273         }
6274         this.setActiveItem(this.navItems[i+1]);
6275     },
6276     setActivePrev : function()
6277     {
6278         var i = this.indexOfNav(this.getActive());
6279         if (i  < 1) {
6280             return;
6281         }
6282         this.setActiveItem(this.navItems[i-1]);
6283     },
6284     clearWasActive : function(except) {
6285         Roo.each(this.navItems, function(e) {
6286             if (e.tabId != except.tabId && e.was_active) {
6287                e.was_active = false;
6288                return false;
6289             }
6290             return true;
6291             
6292         });
6293     },
6294     getWasActive : function ()
6295     {
6296         var r = false;
6297         Roo.each(this.navItems, function(e) {
6298             if (e.was_active) {
6299                r = e;
6300                return false;
6301             }
6302             return true;
6303             
6304         });
6305         return r;
6306     }
6307     
6308     
6309 });
6310
6311  
6312 Roo.apply(Roo.bootstrap.NavGroup, {
6313     
6314     groups: {},
6315      /**
6316     * register a Navigation Group
6317     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6318     */
6319     register : function(navgrp)
6320     {
6321         this.groups[navgrp.navId] = navgrp;
6322         
6323     },
6324     /**
6325     * fetch a Navigation Group based on the navigation ID
6326     * @param {string} the navgroup to add
6327     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6328     */
6329     get: function(navId) {
6330         if (typeof(this.groups[navId]) == 'undefined') {
6331             return false;
6332             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6333         }
6334         return this.groups[navId] ;
6335     }
6336     
6337     
6338     
6339 });
6340
6341  /*
6342  * - LGPL
6343  *
6344  * row
6345  * 
6346  */
6347
6348 /**
6349  * @class Roo.bootstrap.NavItem
6350  * @extends Roo.bootstrap.Component
6351  * Bootstrap Navbar.NavItem class
6352  * @cfg {String} href  link to
6353  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6354  * @cfg {Boolean} button_outline show and outlined button
6355  * @cfg {String} html content of button
6356  * @cfg {String} badge text inside badge
6357  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6358  * @cfg {String} glyphicon DEPRICATED - use fa
6359  * @cfg {String} icon DEPRICATED - use fa
6360  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6361  * @cfg {Boolean} active Is item active
6362  * @cfg {Boolean} disabled Is item disabled
6363  * @cfg {String} linkcls  Link Class
6364  * @cfg {Boolean} preventDefault (true | false) default false
6365  * @cfg {String} tabId the tab that this item activates.
6366  * @cfg {String} tagtype (a|span) render as a href or span?
6367  * @cfg {Boolean} animateRef (true|false) link to element default false  
6368   
6369  * @constructor
6370  * Create a new Navbar Item
6371  * @param {Object} config The config object
6372  */
6373 Roo.bootstrap.NavItem = function(config){
6374     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6375     this.addEvents({
6376         // raw events
6377         /**
6378          * @event click
6379          * The raw click event for the entire grid.
6380          * @param {Roo.EventObject} e
6381          */
6382         "click" : true,
6383          /**
6384             * @event changed
6385             * Fires when the active item active state changes
6386             * @param {Roo.bootstrap.NavItem} this
6387             * @param {boolean} state the new state
6388              
6389          */
6390         'changed': true,
6391         /**
6392             * @event scrollto
6393             * Fires when scroll to element
6394             * @param {Roo.bootstrap.NavItem} this
6395             * @param {Object} options
6396             * @param {Roo.EventObject} e
6397              
6398          */
6399         'scrollto': true
6400     });
6401    
6402 };
6403
6404 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6405     
6406     href: false,
6407     html: '',
6408     badge: '',
6409     icon: false,
6410     fa : false,
6411     glyphicon: false,
6412     active: false,
6413     preventDefault : false,
6414     tabId : false,
6415     tagtype : 'a',
6416     tag: 'li',
6417     disabled : false,
6418     animateRef : false,
6419     was_active : false,
6420     button_weight : '',
6421     button_outline : false,
6422     linkcls : '',
6423     navLink: false,
6424     
6425     getAutoCreate : function(){
6426          
6427         var cfg = {
6428             tag: this.tag,
6429             cls: 'nav-item'
6430         };
6431         
6432         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6433         
6434         if (this.active) {
6435             cfg.cls +=  ' active' ;
6436         }
6437         if (this.disabled) {
6438             cfg.cls += ' disabled';
6439         }
6440         
6441         // BS4 only?
6442         if (this.button_weight.length) {
6443             cfg.tag = this.href ? 'a' : 'button';
6444             cfg.html = this.html || '';
6445             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6446             if (this.href) {
6447                 cfg.href = this.href;
6448             }
6449             if (this.fa) {
6450                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6451             } else {
6452                 cfg.cls += " nav-html";
6453             }
6454             
6455             // menu .. should add dropdown-menu class - so no need for carat..
6456             
6457             if (this.badge !== '') {
6458                  
6459                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6460             }
6461             return cfg;
6462         }
6463         
6464         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6465             cfg.cn = [
6466                 {
6467                     tag: this.tagtype,
6468                     href : this.href || "#",
6469                     html: this.html || '',
6470                     cls : ''
6471                 }
6472             ];
6473             if (this.tagtype == 'a') {
6474                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6475         
6476             }
6477             if (this.icon) {
6478                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6479             } else  if (this.fa) {
6480                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6481             } else if(this.glyphicon) {
6482                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6483             } else {
6484                 cfg.cn[0].cls += " nav-html";
6485             }
6486             
6487             if (this.menu) {
6488                 cfg.cn[0].html += " <span class='caret'></span>";
6489              
6490             }
6491             
6492             if (this.badge !== '') {
6493                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494             }
6495         }
6496         
6497         
6498         
6499         return cfg;
6500     },
6501     onRender : function(ct, position)
6502     {
6503        // Roo.log("Call onRender: " + this.xtype);
6504         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6505             this.tag = 'div';
6506         }
6507         
6508         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6509         this.navLink = this.el.select('.nav-link',true).first();
6510         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6511         return ret;
6512     },
6513       
6514     
6515     initEvents: function() 
6516     {
6517         if (typeof (this.menu) != 'undefined') {
6518             this.menu.parentType = this.xtype;
6519             this.menu.triggerEl = this.el;
6520             this.menu = this.addxtype(Roo.apply({}, this.menu));
6521         }
6522         
6523         this.el.on('click', this.onClick, this);
6524         
6525         //if(this.tagtype == 'span'){
6526         //    this.el.select('span',true).on('click', this.onClick, this);
6527         //}
6528        
6529         // at this point parent should be available..
6530         this.parent().register(this);
6531     },
6532     
6533     onClick : function(e)
6534     {
6535         if (e.getTarget('.dropdown-menu-item')) {
6536             // did you click on a menu itemm.... - then don't trigger onclick..
6537             return;
6538         }
6539         
6540         if(
6541                 this.preventDefault || 
6542                 this.href == '#' 
6543         ){
6544             Roo.log("NavItem - prevent Default?");
6545             e.preventDefault();
6546         }
6547         
6548         if (this.disabled) {
6549             return;
6550         }
6551         
6552         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6553         if (tg && tg.transition) {
6554             Roo.log("waiting for the transitionend");
6555             return;
6556         }
6557         
6558         
6559         
6560         //Roo.log("fire event clicked");
6561         if(this.fireEvent('click', this, e) === false){
6562             return;
6563         };
6564         
6565         if(this.tagtype == 'span'){
6566             return;
6567         }
6568         
6569         //Roo.log(this.href);
6570         var ael = this.el.select('a',true).first();
6571         //Roo.log(ael);
6572         
6573         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6574             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6575             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6576                 return; // ignore... - it's a 'hash' to another page.
6577             }
6578             Roo.log("NavItem - prevent Default?");
6579             e.preventDefault();
6580             this.scrollToElement(e);
6581         }
6582         
6583         
6584         var p =  this.parent();
6585    
6586         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6587             if (typeof(p.setActiveItem) !== 'undefined') {
6588                 p.setActiveItem(this);
6589             }
6590         }
6591         
6592         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6593         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6594             // remove the collapsed menu expand...
6595             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6596         }
6597     },
6598     
6599     isActive: function () {
6600         return this.active
6601     },
6602     setActive : function(state, fire, is_was_active)
6603     {
6604         if (this.active && !state && this.navId) {
6605             this.was_active = true;
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 nv.clearWasActive(this);
6609             }
6610             
6611         }
6612         this.active = state;
6613         
6614         if (!state ) {
6615             this.el.removeClass('active');
6616             this.navLink ? this.navLink.removeClass('active') : false;
6617         } else if (!this.el.hasClass('active')) {
6618             
6619             this.el.addClass('active');
6620             if (Roo.bootstrap.version == 4 && this.navLink ) {
6621                 this.navLink.addClass('active');
6622             }
6623             
6624         }
6625         if (fire) {
6626             this.fireEvent('changed', this, state);
6627         }
6628         
6629         // show a panel if it's registered and related..
6630         
6631         if (!this.navId || !this.tabId || !state || is_was_active) {
6632             return;
6633         }
6634         
6635         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6636         if (!tg) {
6637             return;
6638         }
6639         var pan = tg.getPanelByName(this.tabId);
6640         if (!pan) {
6641             return;
6642         }
6643         // if we can not flip to new panel - go back to old nav highlight..
6644         if (false == tg.showPanel(pan)) {
6645             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6646             if (nv) {
6647                 var onav = nv.getWasActive();
6648                 if (onav) {
6649                     onav.setActive(true, false, true);
6650                 }
6651             }
6652             
6653         }
6654         
6655         
6656         
6657     },
6658      // this should not be here...
6659     setDisabled : function(state)
6660     {
6661         this.disabled = state;
6662         if (!state ) {
6663             this.el.removeClass('disabled');
6664         } else if (!this.el.hasClass('disabled')) {
6665             this.el.addClass('disabled');
6666         }
6667         
6668     },
6669     
6670     /**
6671      * Fetch the element to display the tooltip on.
6672      * @return {Roo.Element} defaults to this.el
6673      */
6674     tooltipEl : function()
6675     {
6676         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6677     },
6678     
6679     scrollToElement : function(e)
6680     {
6681         var c = document.body;
6682         
6683         /*
6684          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6685          */
6686         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6687             c = document.documentElement;
6688         }
6689         
6690         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6691         
6692         if(!target){
6693             return;
6694         }
6695
6696         var o = target.calcOffsetsTo(c);
6697         
6698         var options = {
6699             target : target,
6700             value : o[1]
6701         };
6702         
6703         this.fireEvent('scrollto', this, options, e);
6704         
6705         Roo.get(c).scrollTo('top', options.value, true);
6706         
6707         return;
6708     },
6709     /**
6710      * Set the HTML (text content) of the item
6711      * @param {string} html  content for the nav item
6712      */
6713     setHtml : function(html)
6714     {
6715         this.html = html;
6716         this.htmlEl.dom.innerHTML = html;
6717         
6718     } 
6719 });
6720  
6721
6722  /*
6723  * - LGPL
6724  *
6725  * sidebar item
6726  *
6727  *  li
6728  *    <span> icon </span>
6729  *    <span> text </span>
6730  *    <span>badge </span>
6731  */
6732
6733 /**
6734  * @class Roo.bootstrap.NavSidebarItem
6735  * @extends Roo.bootstrap.NavItem
6736  * Bootstrap Navbar.NavSidebarItem class
6737  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6738  * {Boolean} open is the menu open
6739  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6740  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6741  * {String} buttonSize (sm|md|lg)the extra classes for the button
6742  * {Boolean} showArrow show arrow next to the text (default true)
6743  * @constructor
6744  * Create a new Navbar Button
6745  * @param {Object} config The config object
6746  */
6747 Roo.bootstrap.NavSidebarItem = function(config){
6748     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6749     this.addEvents({
6750         // raw events
6751         /**
6752          * @event click
6753          * The raw click event for the entire grid.
6754          * @param {Roo.EventObject} e
6755          */
6756         "click" : true,
6757          /**
6758             * @event changed
6759             * Fires when the active item active state changes
6760             * @param {Roo.bootstrap.NavSidebarItem} this
6761             * @param {boolean} state the new state
6762              
6763          */
6764         'changed': true
6765     });
6766    
6767 };
6768
6769 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6770     
6771     badgeWeight : 'default',
6772     
6773     open: false,
6774     
6775     buttonView : false,
6776     
6777     buttonWeight : 'default',
6778     
6779     buttonSize : 'md',
6780     
6781     showArrow : true,
6782     
6783     getAutoCreate : function(){
6784         
6785         
6786         var a = {
6787                 tag: 'a',
6788                 href : this.href || '#',
6789                 cls: '',
6790                 html : '',
6791                 cn : []
6792         };
6793         
6794         if(this.buttonView){
6795             a = {
6796                 tag: 'button',
6797                 href : this.href || '#',
6798                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6799                 html : this.html,
6800                 cn : []
6801             };
6802         }
6803         
6804         var cfg = {
6805             tag: 'li',
6806             cls: '',
6807             cn: [ a ]
6808         };
6809         
6810         if (this.active) {
6811             cfg.cls += ' active';
6812         }
6813         
6814         if (this.disabled) {
6815             cfg.cls += ' disabled';
6816         }
6817         if (this.open) {
6818             cfg.cls += ' open x-open';
6819         }
6820         // left icon..
6821         if (this.glyphicon || this.icon) {
6822             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6823             a.cn.push({ tag : 'i', cls : c }) ;
6824         }
6825         
6826         if(!this.buttonView){
6827             var span = {
6828                 tag: 'span',
6829                 html : this.html || ''
6830             };
6831
6832             a.cn.push(span);
6833             
6834         }
6835         
6836         if (this.badge !== '') {
6837             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6838         }
6839         
6840         if (this.menu) {
6841             
6842             if(this.showArrow){
6843                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6844             }
6845             
6846             a.cls += ' dropdown-toggle treeview' ;
6847         }
6848         
6849         return cfg;
6850     },
6851     
6852     initEvents : function()
6853     { 
6854         if (typeof (this.menu) != 'undefined') {
6855             this.menu.parentType = this.xtype;
6856             this.menu.triggerEl = this.el;
6857             this.menu = this.addxtype(Roo.apply({}, this.menu));
6858         }
6859         
6860         this.el.on('click', this.onClick, this);
6861         
6862         if(this.badge !== ''){
6863             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6864         }
6865         
6866     },
6867     
6868     onClick : function(e)
6869     {
6870         if(this.disabled){
6871             e.preventDefault();
6872             return;
6873         }
6874         
6875         if(this.preventDefault){
6876             e.preventDefault();
6877         }
6878         
6879         this.fireEvent('click', this, e);
6880     },
6881     
6882     disable : function()
6883     {
6884         this.setDisabled(true);
6885     },
6886     
6887     enable : function()
6888     {
6889         this.setDisabled(false);
6890     },
6891     
6892     setDisabled : function(state)
6893     {
6894         if(this.disabled == state){
6895             return;
6896         }
6897         
6898         this.disabled = state;
6899         
6900         if (state) {
6901             this.el.addClass('disabled');
6902             return;
6903         }
6904         
6905         this.el.removeClass('disabled');
6906         
6907         return;
6908     },
6909     
6910     setActive : function(state)
6911     {
6912         if(this.active == state){
6913             return;
6914         }
6915         
6916         this.active = state;
6917         
6918         if (state) {
6919             this.el.addClass('active');
6920             return;
6921         }
6922         
6923         this.el.removeClass('active');
6924         
6925         return;
6926     },
6927     
6928     isActive: function () 
6929     {
6930         return this.active;
6931     },
6932     
6933     setBadge : function(str)
6934     {
6935         if(!this.badgeEl){
6936             return;
6937         }
6938         
6939         this.badgeEl.dom.innerHTML = str;
6940     }
6941     
6942    
6943      
6944  
6945 });
6946  
6947
6948  /*
6949  * - LGPL
6950  *
6951  *  Breadcrumb Nav
6952  * 
6953  */
6954 Roo.namespace('Roo.bootstrap.breadcrumb');
6955
6956
6957 /**
6958  * @class Roo.bootstrap.breadcrumb.Nav
6959  * @extends Roo.bootstrap.Component
6960  * Bootstrap Breadcrumb Nav Class
6961  *  
6962  * @children Roo.bootstrap.breadcrumb.Item
6963  * 
6964  * @constructor
6965  * Create a new breadcrumb.Nav
6966  * @param {Object} config The config object
6967  */
6968
6969
6970 Roo.bootstrap.breadcrumb.Nav = function(config){
6971     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6972     
6973     
6974 };
6975
6976 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6977     
6978     getAutoCreate : function()
6979     {
6980
6981         var cfg = {
6982             tag: 'nav',
6983             cn : [
6984                 {
6985                     tag : 'ol',
6986                     cls : 'breadcrumb'
6987                 }
6988             ]
6989             
6990         };
6991           
6992         return cfg;
6993     },
6994     
6995     initEvents: function()
6996     {
6997         this.olEl = this.el.select('ol',true).first();    
6998     },
6999     getChildContainer : function()
7000     {
7001         return this.olEl;  
7002     }
7003     
7004 });
7005
7006  /*
7007  * - LGPL
7008  *
7009  *  Breadcrumb Item
7010  * 
7011  */
7012
7013
7014 /**
7015  * @class Roo.bootstrap.breadcrumb.Nav
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap Breadcrumb Nav Class
7018  *  
7019  * @children Roo.bootstrap.breadcrumb.Component
7020  * @cfg {String} html the content of the link.
7021  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7022  * @cfg {Boolean} active is it active
7023
7024  * 
7025  * @constructor
7026  * Create a new breadcrumb.Nav
7027  * @param {Object} config The config object
7028  */
7029
7030 Roo.bootstrap.breadcrumb.Item = function(config){
7031     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7032     this.addEvents({
7033         // img events
7034         /**
7035          * @event click
7036          * The img click event for the img.
7037          * @param {Roo.EventObject} e
7038          */
7039         "click" : true
7040     });
7041     
7042 };
7043
7044 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7045     
7046     href: false,
7047     html : '',
7048     
7049     getAutoCreate : function()
7050     {
7051
7052         var cfg = {
7053             tag: 'li',
7054             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7055         };
7056         if (this.href !== false) {
7057             cfg.cn = [{
7058                 tag : 'a',
7059                 href : this.href,
7060                 html : this.html
7061             }];
7062         } else {
7063             cfg.html = this.html;
7064         }
7065         
7066         return cfg;
7067     },
7068     
7069     initEvents: function()
7070     {
7071         if (this.href) {
7072             this.el.select('a', true).first().on('click',this.onClick, this)
7073         }
7074         
7075     },
7076     onClick : function(e)
7077     {
7078         e.preventDefault();
7079         this.fireEvent('click',this,  e);
7080     }
7081     
7082 });
7083
7084  /*
7085  * - LGPL
7086  *
7087  * row
7088  * 
7089  */
7090
7091 /**
7092  * @class Roo.bootstrap.Row
7093  * @extends Roo.bootstrap.Component
7094  * Bootstrap Row class (contains columns...)
7095  * 
7096  * @constructor
7097  * Create a new Row
7098  * @param {Object} config The config object
7099  */
7100
7101 Roo.bootstrap.Row = function(config){
7102     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7103 };
7104
7105 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7106     
7107     getAutoCreate : function(){
7108        return {
7109             cls: 'row clearfix'
7110        };
7111     }
7112     
7113     
7114 });
7115
7116  
7117
7118  /*
7119  * - LGPL
7120  *
7121  * pagination
7122  * 
7123  */
7124
7125 /**
7126  * @class Roo.bootstrap.Pagination
7127  * @extends Roo.bootstrap.Component
7128  * Bootstrap Pagination class
7129  * @cfg {String} size xs | sm | md | lg
7130  * @cfg {Boolean} inverse false | true
7131  * 
7132  * @constructor
7133  * Create a new Pagination
7134  * @param {Object} config The config object
7135  */
7136
7137 Roo.bootstrap.Pagination = function(config){
7138     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7139 };
7140
7141 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7142     
7143     cls: false,
7144     size: false,
7145     inverse: false,
7146     
7147     getAutoCreate : function(){
7148         var cfg = {
7149             tag: 'ul',
7150                 cls: 'pagination'
7151         };
7152         if (this.inverse) {
7153             cfg.cls += ' inverse';
7154         }
7155         if (this.html) {
7156             cfg.html=this.html;
7157         }
7158         if (this.cls) {
7159             cfg.cls += " " + this.cls;
7160         }
7161         return cfg;
7162     }
7163    
7164 });
7165
7166  
7167
7168  /*
7169  * - LGPL
7170  *
7171  * Pagination item
7172  * 
7173  */
7174
7175
7176 /**
7177  * @class Roo.bootstrap.PaginationItem
7178  * @extends Roo.bootstrap.Component
7179  * Bootstrap PaginationItem class
7180  * @cfg {String} html text
7181  * @cfg {String} href the link
7182  * @cfg {Boolean} preventDefault (true | false) default true
7183  * @cfg {Boolean} active (true | false) default false
7184  * @cfg {Boolean} disabled default false
7185  * 
7186  * 
7187  * @constructor
7188  * Create a new PaginationItem
7189  * @param {Object} config The config object
7190  */
7191
7192
7193 Roo.bootstrap.PaginationItem = function(config){
7194     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7195     this.addEvents({
7196         // raw events
7197         /**
7198          * @event click
7199          * The raw click event for the entire grid.
7200          * @param {Roo.EventObject} e
7201          */
7202         "click" : true
7203     });
7204 };
7205
7206 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7207     
7208     href : false,
7209     html : false,
7210     preventDefault: true,
7211     active : false,
7212     cls : false,
7213     disabled: false,
7214     
7215     getAutoCreate : function(){
7216         var cfg= {
7217             tag: 'li',
7218             cn: [
7219                 {
7220                     tag : 'a',
7221                     href : this.href ? this.href : '#',
7222                     html : this.html ? this.html : ''
7223                 }
7224             ]
7225         };
7226         
7227         if(this.cls){
7228             cfg.cls = this.cls;
7229         }
7230         
7231         if(this.disabled){
7232             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7233         }
7234         
7235         if(this.active){
7236             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7237         }
7238         
7239         return cfg;
7240     },
7241     
7242     initEvents: function() {
7243         
7244         this.el.on('click', this.onClick, this);
7245         
7246     },
7247     onClick : function(e)
7248     {
7249         Roo.log('PaginationItem on click ');
7250         if(this.preventDefault){
7251             e.preventDefault();
7252         }
7253         
7254         if(this.disabled){
7255             return;
7256         }
7257         
7258         this.fireEvent('click', this, e);
7259     }
7260    
7261 });
7262
7263  
7264
7265  /*
7266  * - LGPL
7267  *
7268  * slider
7269  * 
7270  */
7271
7272
7273 /**
7274  * @class Roo.bootstrap.Slider
7275  * @extends Roo.bootstrap.Component
7276  * Bootstrap Slider class
7277  *    
7278  * @constructor
7279  * Create a new Slider
7280  * @param {Object} config The config object
7281  */
7282
7283 Roo.bootstrap.Slider = function(config){
7284     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7285 };
7286
7287 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7288     
7289     getAutoCreate : function(){
7290         
7291         var cfg = {
7292             tag: 'div',
7293             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7294             cn: [
7295                 {
7296                     tag: 'a',
7297                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7298                 }
7299             ]
7300         };
7301         
7302         return cfg;
7303     }
7304    
7305 });
7306
7307  /*
7308  * Based on:
7309  * Ext JS Library 1.1.1
7310  * Copyright(c) 2006-2007, Ext JS, LLC.
7311  *
7312  * Originally Released Under LGPL - original licence link has changed is not relivant.
7313  *
7314  * Fork - LGPL
7315  * <script type="text/javascript">
7316  */
7317  /**
7318  * @extends Roo.dd.DDProxy
7319  * @class Roo.grid.SplitDragZone
7320  * Support for Column Header resizing
7321  * @constructor
7322  * @param {Object} config
7323  */
7324 // private
7325 // This is a support class used internally by the Grid components
7326 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7327     this.grid = grid;
7328     this.view = grid.getView();
7329     this.proxy = this.view.resizeProxy;
7330     Roo.grid.SplitDragZone.superclass.constructor.call(
7331         this,
7332         hd, // ID
7333         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7334         {  // CONFIG
7335             dragElId : Roo.id(this.proxy.dom),
7336             resizeFrame:false
7337         }
7338     );
7339     
7340     this.setHandleElId(Roo.id(hd));
7341     if (hd2 !== false) {
7342         this.setOuterHandleElId(Roo.id(hd2));
7343     }
7344     
7345     this.scroll = false;
7346 };
7347 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7348     fly: Roo.Element.fly,
7349
7350     b4StartDrag : function(x, y){
7351         this.view.headersDisabled = true;
7352         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7353                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7354         );
7355         this.proxy.setHeight(h);
7356         
7357         // for old system colWidth really stored the actual width?
7358         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7359         // which in reality did not work.. - it worked only for fixed sizes
7360         // for resizable we need to use actual sizes.
7361         var w = this.cm.getColumnWidth(this.cellIndex);
7362         if (!this.view.mainWrap) {
7363             // bootstrap.
7364             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7365         }
7366         
7367         
7368         
7369         // this was w-this.grid.minColumnWidth;
7370         // doesnt really make sense? - w = thie curren width or the rendered one?
7371         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7372         this.resetConstraints();
7373         this.setXConstraint(minw, 1000);
7374         this.setYConstraint(0, 0);
7375         this.minX = x - minw;
7376         this.maxX = x + 1000;
7377         this.startPos = x;
7378         if (!this.view.mainWrap) { // this is Bootstrap code..
7379             this.getDragEl().style.display='block';
7380         }
7381         
7382         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7383     },
7384
7385
7386     handleMouseDown : function(e){
7387         ev = Roo.EventObject.setEvent(e);
7388         var t = this.fly(ev.getTarget());
7389         if(t.hasClass("x-grid-split")){
7390             this.cellIndex = this.view.getCellIndex(t.dom);
7391             this.split = t.dom;
7392             this.cm = this.grid.colModel;
7393             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7394                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7395             }
7396         }
7397     },
7398
7399     endDrag : function(e){
7400         this.view.headersDisabled = false;
7401         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7402         var diff = endX - this.startPos;
7403         // 
7404         var w = this.cm.getColumnWidth(this.cellIndex);
7405         if (!this.view.mainWrap) {
7406             w = 0;
7407         }
7408         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7409     },
7410
7411     autoOffset : function(){
7412         this.setDelta(0,0);
7413     }
7414 });/*
7415  * Based on:
7416  * Ext JS Library 1.1.1
7417  * Copyright(c) 2006-2007, Ext JS, LLC.
7418  *
7419  * Originally Released Under LGPL - original licence link has changed is not relivant.
7420  *
7421  * Fork - LGPL
7422  * <script type="text/javascript">
7423  */
7424
7425 /**
7426  * @class Roo.grid.AbstractSelectionModel
7427  * @extends Roo.util.Observable
7428  * @abstract
7429  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7430  * implemented by descendant classes.  This class should not be directly instantiated.
7431  * @constructor
7432  */
7433 Roo.grid.AbstractSelectionModel = function(){
7434     this.locked = false;
7435     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7436 };
7437
7438 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7439     /** @ignore Called by the grid automatically. Do not call directly. */
7440     init : function(grid){
7441         this.grid = grid;
7442         this.initEvents();
7443     },
7444
7445     /**
7446      * Locks the selections.
7447      */
7448     lock : function(){
7449         this.locked = true;
7450     },
7451
7452     /**
7453      * Unlocks the selections.
7454      */
7455     unlock : function(){
7456         this.locked = false;
7457     },
7458
7459     /**
7460      * Returns true if the selections are locked.
7461      * @return {Boolean}
7462      */
7463     isLocked : function(){
7464         return this.locked;
7465     }
7466 });/*
7467  * Based on:
7468  * Ext JS Library 1.1.1
7469  * Copyright(c) 2006-2007, Ext JS, LLC.
7470  *
7471  * Originally Released Under LGPL - original licence link has changed is not relivant.
7472  *
7473  * Fork - LGPL
7474  * <script type="text/javascript">
7475  */
7476 /**
7477  * @extends Roo.grid.AbstractSelectionModel
7478  * @class Roo.grid.RowSelectionModel
7479  * The default SelectionModel used by {@link Roo.grid.Grid}.
7480  * It supports multiple selections and keyboard selection/navigation. 
7481  * @constructor
7482  * @param {Object} config
7483  */
7484 Roo.grid.RowSelectionModel = function(config){
7485     Roo.apply(this, config);
7486     this.selections = new Roo.util.MixedCollection(false, function(o){
7487         return o.id;
7488     });
7489
7490     this.last = false;
7491     this.lastActive = false;
7492
7493     this.addEvents({
7494         /**
7495         * @event selectionchange
7496         * Fires when the selection changes
7497         * @param {SelectionModel} this
7498         */
7499        "selectionchange" : true,
7500        /**
7501         * @event afterselectionchange
7502         * Fires after the selection changes (eg. by key press or clicking)
7503         * @param {SelectionModel} this
7504         */
7505        "afterselectionchange" : true,
7506        /**
7507         * @event beforerowselect
7508         * Fires when a row is selected being selected, return false to cancel.
7509         * @param {SelectionModel} this
7510         * @param {Number} rowIndex The selected index
7511         * @param {Boolean} keepExisting False if other selections will be cleared
7512         */
7513        "beforerowselect" : true,
7514        /**
7515         * @event rowselect
7516         * Fires when a row is selected.
7517         * @param {SelectionModel} this
7518         * @param {Number} rowIndex The selected index
7519         * @param {Roo.data.Record} r The record
7520         */
7521        "rowselect" : true,
7522        /**
7523         * @event rowdeselect
7524         * Fires when a row is deselected.
7525         * @param {SelectionModel} this
7526         * @param {Number} rowIndex The selected index
7527         */
7528         "rowdeselect" : true
7529     });
7530     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7531     this.locked = false;
7532 };
7533
7534 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7535     /**
7536      * @cfg {Boolean} singleSelect
7537      * True to allow selection of only one row at a time (defaults to false)
7538      */
7539     singleSelect : false,
7540
7541     // private
7542     initEvents : function(){
7543
7544         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7545             this.grid.on("mousedown", this.handleMouseDown, this);
7546         }else{ // allow click to work like normal
7547             this.grid.on("rowclick", this.handleDragableRowClick, this);
7548         }
7549         // bootstrap does not have a view..
7550         var view = this.grid.view ? this.grid.view : this.grid;
7551         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7552             "up" : function(e){
7553                 if(!e.shiftKey){
7554                     this.selectPrevious(e.shiftKey);
7555                 }else if(this.last !== false && this.lastActive !== false){
7556                     var last = this.last;
7557                     this.selectRange(this.last,  this.lastActive-1);
7558                     view.focusRow(this.lastActive);
7559                     if(last !== false){
7560                         this.last = last;
7561                     }
7562                 }else{
7563                     this.selectFirstRow();
7564                 }
7565                 this.fireEvent("afterselectionchange", this);
7566             },
7567             "down" : function(e){
7568                 if(!e.shiftKey){
7569                     this.selectNext(e.shiftKey);
7570                 }else if(this.last !== false && this.lastActive !== false){
7571                     var last = this.last;
7572                     this.selectRange(this.last,  this.lastActive+1);
7573                     view.focusRow(this.lastActive);
7574                     if(last !== false){
7575                         this.last = last;
7576                     }
7577                 }else{
7578                     this.selectFirstRow();
7579                 }
7580                 this.fireEvent("afterselectionchange", this);
7581             },
7582             scope: this
7583         });
7584
7585          
7586         view.on("refresh", this.onRefresh, this);
7587         view.on("rowupdated", this.onRowUpdated, this);
7588         view.on("rowremoved", this.onRemove, this);
7589     },
7590
7591     // private
7592     onRefresh : function(){
7593         var ds = this.grid.ds, i, v = this.grid.view;
7594         var s = this.selections;
7595         s.each(function(r){
7596             if((i = ds.indexOfId(r.id)) != -1){
7597                 v.onRowSelect(i);
7598                 s.add(ds.getAt(i)); // updating the selection relate data
7599             }else{
7600                 s.remove(r);
7601             }
7602         });
7603     },
7604
7605     // private
7606     onRemove : function(v, index, r){
7607         this.selections.remove(r);
7608     },
7609
7610     // private
7611     onRowUpdated : function(v, index, r){
7612         if(this.isSelected(r)){
7613             v.onRowSelect(index);
7614         }
7615     },
7616
7617     /**
7618      * Select records.
7619      * @param {Array} records The records to select
7620      * @param {Boolean} keepExisting (optional) True to keep existing selections
7621      */
7622     selectRecords : function(records, keepExisting){
7623         if(!keepExisting){
7624             this.clearSelections();
7625         }
7626         var ds = this.grid.ds;
7627         for(var i = 0, len = records.length; i < len; i++){
7628             this.selectRow(ds.indexOf(records[i]), true);
7629         }
7630     },
7631
7632     /**
7633      * Gets the number of selected rows.
7634      * @return {Number}
7635      */
7636     getCount : function(){
7637         return this.selections.length;
7638     },
7639
7640     /**
7641      * Selects the first row in the grid.
7642      */
7643     selectFirstRow : function(){
7644         this.selectRow(0);
7645     },
7646
7647     /**
7648      * Select the last row.
7649      * @param {Boolean} keepExisting (optional) True to keep existing selections
7650      */
7651     selectLastRow : function(keepExisting){
7652         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7653     },
7654
7655     /**
7656      * Selects the row immediately following the last selected row.
7657      * @param {Boolean} keepExisting (optional) True to keep existing selections
7658      */
7659     selectNext : function(keepExisting){
7660         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7661             this.selectRow(this.last+1, keepExisting);
7662             var view = this.grid.view ? this.grid.view : this.grid;
7663             view.focusRow(this.last);
7664         }
7665     },
7666
7667     /**
7668      * Selects the row that precedes the last selected row.
7669      * @param {Boolean} keepExisting (optional) True to keep existing selections
7670      */
7671     selectPrevious : function(keepExisting){
7672         if(this.last){
7673             this.selectRow(this.last-1, keepExisting);
7674             var view = this.grid.view ? this.grid.view : this.grid;
7675             view.focusRow(this.last);
7676         }
7677     },
7678
7679     /**
7680      * Returns the selected records
7681      * @return {Array} Array of selected records
7682      */
7683     getSelections : function(){
7684         return [].concat(this.selections.items);
7685     },
7686
7687     /**
7688      * Returns the first selected record.
7689      * @return {Record}
7690      */
7691     getSelected : function(){
7692         return this.selections.itemAt(0);
7693     },
7694
7695
7696     /**
7697      * Clears all selections.
7698      */
7699     clearSelections : function(fast){
7700         if(this.locked) {
7701             return;
7702         }
7703         if(fast !== true){
7704             var ds = this.grid.ds;
7705             var s = this.selections;
7706             s.each(function(r){
7707                 this.deselectRow(ds.indexOfId(r.id));
7708             }, this);
7709             s.clear();
7710         }else{
7711             this.selections.clear();
7712         }
7713         this.last = false;
7714     },
7715
7716
7717     /**
7718      * Selects all rows.
7719      */
7720     selectAll : function(){
7721         if(this.locked) {
7722             return;
7723         }
7724         this.selections.clear();
7725         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7726             this.selectRow(i, true);
7727         }
7728     },
7729
7730     /**
7731      * Returns True if there is a selection.
7732      * @return {Boolean}
7733      */
7734     hasSelection : function(){
7735         return this.selections.length > 0;
7736     },
7737
7738     /**
7739      * Returns True if the specified row is selected.
7740      * @param {Number/Record} record The record or index of the record to check
7741      * @return {Boolean}
7742      */
7743     isSelected : function(index){
7744         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7745         return (r && this.selections.key(r.id) ? true : false);
7746     },
7747
7748     /**
7749      * Returns True if the specified record id is selected.
7750      * @param {String} id The id of record to check
7751      * @return {Boolean}
7752      */
7753     isIdSelected : function(id){
7754         return (this.selections.key(id) ? true : false);
7755     },
7756
7757     // private
7758     handleMouseDown : function(e, t)
7759     {
7760         var view = this.grid.view ? this.grid.view : this.grid;
7761         var rowIndex;
7762         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7763             return;
7764         };
7765         if(e.shiftKey && this.last !== false){
7766             var last = this.last;
7767             this.selectRange(last, rowIndex, e.ctrlKey);
7768             this.last = last; // reset the last
7769             view.focusRow(rowIndex);
7770         }else{
7771             var isSelected = this.isSelected(rowIndex);
7772             if(e.button !== 0 && isSelected){
7773                 view.focusRow(rowIndex);
7774             }else if(e.ctrlKey && isSelected){
7775                 this.deselectRow(rowIndex);
7776             }else if(!isSelected){
7777                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7778                 view.focusRow(rowIndex);
7779             }
7780         }
7781         this.fireEvent("afterselectionchange", this);
7782     },
7783     // private
7784     handleDragableRowClick :  function(grid, rowIndex, e) 
7785     {
7786         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7787             this.selectRow(rowIndex, false);
7788             var view = this.grid.view ? this.grid.view : this.grid;
7789             view.focusRow(rowIndex);
7790              this.fireEvent("afterselectionchange", this);
7791         }
7792     },
7793     
7794     /**
7795      * Selects multiple rows.
7796      * @param {Array} rows Array of the indexes of the row to select
7797      * @param {Boolean} keepExisting (optional) True to keep existing selections
7798      */
7799     selectRows : function(rows, keepExisting){
7800         if(!keepExisting){
7801             this.clearSelections();
7802         }
7803         for(var i = 0, len = rows.length; i < len; i++){
7804             this.selectRow(rows[i], true);
7805         }
7806     },
7807
7808     /**
7809      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7810      * @param {Number} startRow The index of the first row in the range
7811      * @param {Number} endRow The index of the last row in the range
7812      * @param {Boolean} keepExisting (optional) True to retain existing selections
7813      */
7814     selectRange : function(startRow, endRow, keepExisting){
7815         if(this.locked) {
7816             return;
7817         }
7818         if(!keepExisting){
7819             this.clearSelections();
7820         }
7821         if(startRow <= endRow){
7822             for(var i = startRow; i <= endRow; i++){
7823                 this.selectRow(i, true);
7824             }
7825         }else{
7826             for(var i = startRow; i >= endRow; i--){
7827                 this.selectRow(i, true);
7828             }
7829         }
7830     },
7831
7832     /**
7833      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7834      * @param {Number} startRow The index of the first row in the range
7835      * @param {Number} endRow The index of the last row in the range
7836      */
7837     deselectRange : function(startRow, endRow, preventViewNotify){
7838         if(this.locked) {
7839             return;
7840         }
7841         for(var i = startRow; i <= endRow; i++){
7842             this.deselectRow(i, preventViewNotify);
7843         }
7844     },
7845
7846     /**
7847      * Selects a row.
7848      * @param {Number} row The index of the row to select
7849      * @param {Boolean} keepExisting (optional) True to keep existing selections
7850      */
7851     selectRow : function(index, keepExisting, preventViewNotify){
7852         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7853             return;
7854         }
7855         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7856             if(!keepExisting || this.singleSelect){
7857                 this.clearSelections();
7858             }
7859             var r = this.grid.ds.getAt(index);
7860             this.selections.add(r);
7861             this.last = this.lastActive = index;
7862             if(!preventViewNotify){
7863                 var view = this.grid.view ? this.grid.view : this.grid;
7864                 view.onRowSelect(index);
7865             }
7866             this.fireEvent("rowselect", this, index, r);
7867             this.fireEvent("selectionchange", this);
7868         }
7869     },
7870
7871     /**
7872      * Deselects a row.
7873      * @param {Number} row The index of the row to deselect
7874      */
7875     deselectRow : function(index, preventViewNotify){
7876         if(this.locked) {
7877             return;
7878         }
7879         if(this.last == index){
7880             this.last = false;
7881         }
7882         if(this.lastActive == index){
7883             this.lastActive = false;
7884         }
7885         var r = this.grid.ds.getAt(index);
7886         this.selections.remove(r);
7887         if(!preventViewNotify){
7888             var view = this.grid.view ? this.grid.view : this.grid;
7889             view.onRowDeselect(index);
7890         }
7891         this.fireEvent("rowdeselect", this, index);
7892         this.fireEvent("selectionchange", this);
7893     },
7894
7895     // private
7896     restoreLast : function(){
7897         if(this._last){
7898             this.last = this._last;
7899         }
7900     },
7901
7902     // private
7903     acceptsNav : function(row, col, cm){
7904         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7905     },
7906
7907     // private
7908     onEditorKey : function(field, e){
7909         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7910         if(k == e.TAB){
7911             e.stopEvent();
7912             ed.completeEdit();
7913             if(e.shiftKey){
7914                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7915             }else{
7916                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7917             }
7918         }else if(k == e.ENTER && !e.ctrlKey){
7919             e.stopEvent();
7920             ed.completeEdit();
7921             if(e.shiftKey){
7922                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7923             }else{
7924                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7925             }
7926         }else if(k == e.ESC){
7927             ed.cancelEdit();
7928         }
7929         if(newCell){
7930             g.startEditing(newCell[0], newCell[1]);
7931         }
7932     }
7933 });/*
7934  * Based on:
7935  * Ext JS Library 1.1.1
7936  * Copyright(c) 2006-2007, Ext JS, LLC.
7937  *
7938  * Originally Released Under LGPL - original licence link has changed is not relivant.
7939  *
7940  * Fork - LGPL
7941  * <script type="text/javascript">
7942  */
7943  
7944
7945 /**
7946  * @class Roo.grid.ColumnModel
7947  * @extends Roo.util.Observable
7948  * This is the default implementation of a ColumnModel used by the Grid. It defines
7949  * the columns in the grid.
7950  * <br>Usage:<br>
7951  <pre><code>
7952  var colModel = new Roo.grid.ColumnModel([
7953         {header: "Ticker", width: 60, sortable: true, locked: true},
7954         {header: "Company Name", width: 150, sortable: true},
7955         {header: "Market Cap.", width: 100, sortable: true},
7956         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7957         {header: "Employees", width: 100, sortable: true, resizable: false}
7958  ]);
7959  </code></pre>
7960  * <p>
7961  
7962  * The config options listed for this class are options which may appear in each
7963  * individual column definition.
7964  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7965  * @constructor
7966  * @param {Object} config An Array of column config objects. See this class's
7967  * config objects for details.
7968 */
7969 Roo.grid.ColumnModel = function(config){
7970         /**
7971      * The config passed into the constructor
7972      */
7973     this.config = []; //config;
7974     this.lookup = {};
7975
7976     // if no id, create one
7977     // if the column does not have a dataIndex mapping,
7978     // map it to the order it is in the config
7979     for(var i = 0, len = config.length; i < len; i++){
7980         this.addColumn(config[i]);
7981         
7982     }
7983
7984     /**
7985      * The width of columns which have no width specified (defaults to 100)
7986      * @type Number
7987      */
7988     this.defaultWidth = 100;
7989
7990     /**
7991      * Default sortable of columns which have no sortable specified (defaults to false)
7992      * @type Boolean
7993      */
7994     this.defaultSortable = false;
7995
7996     this.addEvents({
7997         /**
7998              * @event widthchange
7999              * Fires when the width of a column changes.
8000              * @param {ColumnModel} this
8001              * @param {Number} columnIndex The column index
8002              * @param {Number} newWidth The new width
8003              */
8004             "widthchange": true,
8005         /**
8006              * @event headerchange
8007              * Fires when the text of a header changes.
8008              * @param {ColumnModel} this
8009              * @param {Number} columnIndex The column index
8010              * @param {Number} newText The new header text
8011              */
8012             "headerchange": true,
8013         /**
8014              * @event hiddenchange
8015              * Fires when a column is hidden or "unhidden".
8016              * @param {ColumnModel} this
8017              * @param {Number} columnIndex The column index
8018              * @param {Boolean} hidden true if hidden, false otherwise
8019              */
8020             "hiddenchange": true,
8021             /**
8022          * @event columnmoved
8023          * Fires when a column is moved.
8024          * @param {ColumnModel} this
8025          * @param {Number} oldIndex
8026          * @param {Number} newIndex
8027          */
8028         "columnmoved" : true,
8029         /**
8030          * @event columlockchange
8031          * Fires when a column's locked state is changed
8032          * @param {ColumnModel} this
8033          * @param {Number} colIndex
8034          * @param {Boolean} locked true if locked
8035          */
8036         "columnlockchange" : true
8037     });
8038     Roo.grid.ColumnModel.superclass.constructor.call(this);
8039 };
8040 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8041     /**
8042      * @cfg {String} header The header text to display in the Grid view.
8043      */
8044         /**
8045      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8046      */
8047         /**
8048      * @cfg {String} smHeader Header at Bootsrap Small width
8049      */
8050         /**
8051      * @cfg {String} mdHeader Header at Bootsrap Medium width
8052      */
8053         /**
8054      * @cfg {String} lgHeader Header at Bootsrap Large width
8055      */
8056         /**
8057      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8058      */
8059     /**
8060      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8061      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8062      * specified, the column's index is used as an index into the Record's data Array.
8063      */
8064     /**
8065      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8066      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8067      */
8068     /**
8069      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8070      * Defaults to the value of the {@link #defaultSortable} property.
8071      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8072      */
8073     /**
8074      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8075      */
8076     /**
8077      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8078      */
8079     /**
8080      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8081      */
8082     /**
8083      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8084      */
8085     /**
8086      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8087      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8088      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8089      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8090      */
8091        /**
8092      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8093      */
8094     /**
8095      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8096      */
8097     /**
8098      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8099      */
8100     /**
8101      * @cfg {String} cursor (Optional)
8102      */
8103     /**
8104      * @cfg {String} tooltip (Optional)
8105      */
8106     /**
8107      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8108      */
8109     /**
8110      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8111      */
8112     /**
8113      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115     /**
8116      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118         /**
8119      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8120      */
8121     /**
8122      * Returns the id of the column at the specified index.
8123      * @param {Number} index The column index
8124      * @return {String} the id
8125      */
8126     getColumnId : function(index){
8127         return this.config[index].id;
8128     },
8129
8130     /**
8131      * Returns the column for a specified id.
8132      * @param {String} id The column id
8133      * @return {Object} the column
8134      */
8135     getColumnById : function(id){
8136         return this.lookup[id];
8137     },
8138
8139     
8140     /**
8141      * Returns the column Object for a specified dataIndex.
8142      * @param {String} dataIndex The column dataIndex
8143      * @return {Object|Boolean} the column or false if not found
8144      */
8145     getColumnByDataIndex: function(dataIndex){
8146         var index = this.findColumnIndex(dataIndex);
8147         return index > -1 ? this.config[index] : false;
8148     },
8149     
8150     /**
8151      * Returns the index for a specified column id.
8152      * @param {String} id The column id
8153      * @return {Number} the index, or -1 if not found
8154      */
8155     getIndexById : function(id){
8156         for(var i = 0, len = this.config.length; i < len; i++){
8157             if(this.config[i].id == id){
8158                 return i;
8159             }
8160         }
8161         return -1;
8162     },
8163     
8164     /**
8165      * Returns the index for a specified column dataIndex.
8166      * @param {String} dataIndex The column dataIndex
8167      * @return {Number} the index, or -1 if not found
8168      */
8169     
8170     findColumnIndex : function(dataIndex){
8171         for(var i = 0, len = this.config.length; i < len; i++){
8172             if(this.config[i].dataIndex == dataIndex){
8173                 return i;
8174             }
8175         }
8176         return -1;
8177     },
8178     
8179     
8180     moveColumn : function(oldIndex, newIndex){
8181         var c = this.config[oldIndex];
8182         this.config.splice(oldIndex, 1);
8183         this.config.splice(newIndex, 0, c);
8184         this.dataMap = null;
8185         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8186     },
8187
8188     isLocked : function(colIndex){
8189         return this.config[colIndex].locked === true;
8190     },
8191
8192     setLocked : function(colIndex, value, suppressEvent){
8193         if(this.isLocked(colIndex) == value){
8194             return;
8195         }
8196         this.config[colIndex].locked = value;
8197         if(!suppressEvent){
8198             this.fireEvent("columnlockchange", this, colIndex, value);
8199         }
8200     },
8201
8202     getTotalLockedWidth : function(){
8203         var totalWidth = 0;
8204         for(var i = 0; i < this.config.length; i++){
8205             if(this.isLocked(i) && !this.isHidden(i)){
8206                 this.totalWidth += this.getColumnWidth(i);
8207             }
8208         }
8209         return totalWidth;
8210     },
8211
8212     getLockedCount : function(){
8213         for(var i = 0, len = this.config.length; i < len; i++){
8214             if(!this.isLocked(i)){
8215                 return i;
8216             }
8217         }
8218         
8219         return this.config.length;
8220     },
8221
8222     /**
8223      * Returns the number of columns.
8224      * @return {Number}
8225      */
8226     getColumnCount : function(visibleOnly){
8227         if(visibleOnly === true){
8228             var c = 0;
8229             for(var i = 0, len = this.config.length; i < len; i++){
8230                 if(!this.isHidden(i)){
8231                     c++;
8232                 }
8233             }
8234             return c;
8235         }
8236         return this.config.length;
8237     },
8238
8239     /**
8240      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8241      * @param {Function} fn
8242      * @param {Object} scope (optional)
8243      * @return {Array} result
8244      */
8245     getColumnsBy : function(fn, scope){
8246         var r = [];
8247         for(var i = 0, len = this.config.length; i < len; i++){
8248             var c = this.config[i];
8249             if(fn.call(scope||this, c, i) === true){
8250                 r[r.length] = c;
8251             }
8252         }
8253         return r;
8254     },
8255
8256     /**
8257      * Returns true if the specified column is sortable.
8258      * @param {Number} col The column index
8259      * @return {Boolean}
8260      */
8261     isSortable : function(col){
8262         if(typeof this.config[col].sortable == "undefined"){
8263             return this.defaultSortable;
8264         }
8265         return this.config[col].sortable;
8266     },
8267
8268     /**
8269      * Returns the rendering (formatting) function defined for the column.
8270      * @param {Number} col The column index.
8271      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8272      */
8273     getRenderer : function(col){
8274         if(!this.config[col].renderer){
8275             return Roo.grid.ColumnModel.defaultRenderer;
8276         }
8277         return this.config[col].renderer;
8278     },
8279
8280     /**
8281      * Sets the rendering (formatting) function for a column.
8282      * @param {Number} col The column index
8283      * @param {Function} fn The function to use to process the cell's raw data
8284      * to return HTML markup for the grid view. The render function is called with
8285      * the following parameters:<ul>
8286      * <li>Data value.</li>
8287      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8288      * <li>css A CSS style string to apply to the table cell.</li>
8289      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8290      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8291      * <li>Row index</li>
8292      * <li>Column index</li>
8293      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8294      */
8295     setRenderer : function(col, fn){
8296         this.config[col].renderer = fn;
8297     },
8298
8299     /**
8300      * Returns the width for the specified column.
8301      * @param {Number} col The column index
8302      * @param (optional) {String} gridSize bootstrap width size.
8303      * @return {Number}
8304      */
8305     getColumnWidth : function(col, gridSize)
8306         {
8307                 var cfg = this.config[col];
8308                 
8309                 if (typeof(gridSize) == 'undefined') {
8310                         return cfg.width * 1 || this.defaultWidth;
8311                 }
8312                 if (gridSize === false) { // if we set it..
8313                         return cfg.width || false;
8314                 }
8315                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8316                 
8317                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8318                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8319                                 continue;
8320                         }
8321                         return cfg[ sizes[i] ];
8322                 }
8323                 return 1;
8324                 
8325     },
8326
8327     /**
8328      * Sets the width for a column.
8329      * @param {Number} col The column index
8330      * @param {Number} width The new width
8331      */
8332     setColumnWidth : function(col, width, suppressEvent){
8333         this.config[col].width = width;
8334         this.totalWidth = null;
8335         if(!suppressEvent){
8336              this.fireEvent("widthchange", this, col, width);
8337         }
8338     },
8339
8340     /**
8341      * Returns the total width of all columns.
8342      * @param {Boolean} includeHidden True to include hidden column widths
8343      * @return {Number}
8344      */
8345     getTotalWidth : function(includeHidden){
8346         if(!this.totalWidth){
8347             this.totalWidth = 0;
8348             for(var i = 0, len = this.config.length; i < len; i++){
8349                 if(includeHidden || !this.isHidden(i)){
8350                     this.totalWidth += this.getColumnWidth(i);
8351                 }
8352             }
8353         }
8354         return this.totalWidth;
8355     },
8356
8357     /**
8358      * Returns the header for the specified column.
8359      * @param {Number} col The column index
8360      * @return {String}
8361      */
8362     getColumnHeader : function(col){
8363         return this.config[col].header;
8364     },
8365
8366     /**
8367      * Sets the header for a column.
8368      * @param {Number} col The column index
8369      * @param {String} header The new header
8370      */
8371     setColumnHeader : function(col, header){
8372         this.config[col].header = header;
8373         this.fireEvent("headerchange", this, col, header);
8374     },
8375
8376     /**
8377      * Returns the tooltip for the specified column.
8378      * @param {Number} col The column index
8379      * @return {String}
8380      */
8381     getColumnTooltip : function(col){
8382             return this.config[col].tooltip;
8383     },
8384     /**
8385      * Sets the tooltip for a column.
8386      * @param {Number} col The column index
8387      * @param {String} tooltip The new tooltip
8388      */
8389     setColumnTooltip : function(col, tooltip){
8390             this.config[col].tooltip = tooltip;
8391     },
8392
8393     /**
8394      * Returns the dataIndex for the specified column.
8395      * @param {Number} col The column index
8396      * @return {Number}
8397      */
8398     getDataIndex : function(col){
8399         return this.config[col].dataIndex;
8400     },
8401
8402     /**
8403      * Sets the dataIndex for a column.
8404      * @param {Number} col The column index
8405      * @param {Number} dataIndex The new dataIndex
8406      */
8407     setDataIndex : function(col, dataIndex){
8408         this.config[col].dataIndex = dataIndex;
8409     },
8410
8411     
8412     
8413     /**
8414      * Returns true if the cell is editable.
8415      * @param {Number} colIndex The column index
8416      * @param {Number} rowIndex The row index - this is nto actually used..?
8417      * @return {Boolean}
8418      */
8419     isCellEditable : function(colIndex, rowIndex){
8420         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8421     },
8422
8423     /**
8424      * Returns the editor defined for the cell/column.
8425      * return false or null to disable editing.
8426      * @param {Number} colIndex The column index
8427      * @param {Number} rowIndex The row index
8428      * @return {Object}
8429      */
8430     getCellEditor : function(colIndex, rowIndex){
8431         return this.config[colIndex].editor;
8432     },
8433
8434     /**
8435      * Sets if a column is editable.
8436      * @param {Number} col The column index
8437      * @param {Boolean} editable True if the column is editable
8438      */
8439     setEditable : function(col, editable){
8440         this.config[col].editable = editable;
8441     },
8442
8443
8444     /**
8445      * Returns true if the column is hidden.
8446      * @param {Number} colIndex The column index
8447      * @return {Boolean}
8448      */
8449     isHidden : function(colIndex){
8450         return this.config[colIndex].hidden;
8451     },
8452
8453
8454     /**
8455      * Returns true if the column width cannot be changed
8456      */
8457     isFixed : function(colIndex){
8458         return this.config[colIndex].fixed;
8459     },
8460
8461     /**
8462      * Returns true if the column can be resized
8463      * @return {Boolean}
8464      */
8465     isResizable : function(colIndex){
8466         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8467     },
8468     /**
8469      * Sets if a column is hidden.
8470      * @param {Number} colIndex The column index
8471      * @param {Boolean} hidden True if the column is hidden
8472      */
8473     setHidden : function(colIndex, hidden){
8474         this.config[colIndex].hidden = hidden;
8475         this.totalWidth = null;
8476         this.fireEvent("hiddenchange", this, colIndex, hidden);
8477     },
8478
8479     /**
8480      * Sets the editor for a column.
8481      * @param {Number} col The column index
8482      * @param {Object} editor The editor object
8483      */
8484     setEditor : function(col, editor){
8485         this.config[col].editor = editor;
8486     },
8487     /**
8488      * Add a column (experimental...) - defaults to adding to the end..
8489      * @param {Object} config 
8490     */
8491     addColumn : function(c)
8492     {
8493     
8494         var i = this.config.length;
8495         this.config[i] = c;
8496         
8497         if(typeof c.dataIndex == "undefined"){
8498             c.dataIndex = i;
8499         }
8500         if(typeof c.renderer == "string"){
8501             c.renderer = Roo.util.Format[c.renderer];
8502         }
8503         if(typeof c.id == "undefined"){
8504             c.id = Roo.id();
8505         }
8506         if(c.editor && c.editor.xtype){
8507             c.editor  = Roo.factory(c.editor, Roo.grid);
8508         }
8509         if(c.editor && c.editor.isFormField){
8510             c.editor = new Roo.grid.GridEditor(c.editor);
8511         }
8512         this.lookup[c.id] = c;
8513     }
8514     
8515 });
8516
8517 Roo.grid.ColumnModel.defaultRenderer = function(value)
8518 {
8519     if(typeof value == "object") {
8520         return value;
8521     }
8522         if(typeof value == "string" && value.length < 1){
8523             return "&#160;";
8524         }
8525     
8526         return String.format("{0}", value);
8527 };
8528
8529 // Alias for backwards compatibility
8530 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8531 /*
8532  * Based on:
8533  * Ext JS Library 1.1.1
8534  * Copyright(c) 2006-2007, Ext JS, LLC.
8535  *
8536  * Originally Released Under LGPL - original licence link has changed is not relivant.
8537  *
8538  * Fork - LGPL
8539  * <script type="text/javascript">
8540  */
8541  
8542 /**
8543  * @class Roo.LoadMask
8544  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8545  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8546  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8547  * element's UpdateManager load indicator and will be destroyed after the initial load.
8548  * @constructor
8549  * Create a new LoadMask
8550  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8551  * @param {Object} config The config object
8552  */
8553 Roo.LoadMask = function(el, config){
8554     this.el = Roo.get(el);
8555     Roo.apply(this, config);
8556     if(this.store){
8557         this.store.on('beforeload', this.onBeforeLoad, this);
8558         this.store.on('load', this.onLoad, this);
8559         this.store.on('loadexception', this.onLoadException, this);
8560         this.removeMask = false;
8561     }else{
8562         var um = this.el.getUpdateManager();
8563         um.showLoadIndicator = false; // disable the default indicator
8564         um.on('beforeupdate', this.onBeforeLoad, this);
8565         um.on('update', this.onLoad, this);
8566         um.on('failure', this.onLoad, this);
8567         this.removeMask = true;
8568     }
8569 };
8570
8571 Roo.LoadMask.prototype = {
8572     /**
8573      * @cfg {Boolean} removeMask
8574      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8575      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8576      */
8577     removeMask : false,
8578     /**
8579      * @cfg {String} msg
8580      * The text to display in a centered loading message box (defaults to 'Loading...')
8581      */
8582     msg : 'Loading...',
8583     /**
8584      * @cfg {String} msgCls
8585      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8586      */
8587     msgCls : 'x-mask-loading',
8588
8589     /**
8590      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8591      * @type Boolean
8592      */
8593     disabled: false,
8594
8595     /**
8596      * Disables the mask to prevent it from being displayed
8597      */
8598     disable : function(){
8599        this.disabled = true;
8600     },
8601
8602     /**
8603      * Enables the mask so that it can be displayed
8604      */
8605     enable : function(){
8606         this.disabled = false;
8607     },
8608     
8609     onLoadException : function()
8610     {
8611         Roo.log(arguments);
8612         
8613         if (typeof(arguments[3]) != 'undefined') {
8614             Roo.MessageBox.alert("Error loading",arguments[3]);
8615         } 
8616         /*
8617         try {
8618             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8619                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8620             }   
8621         } catch(e) {
8622             
8623         }
8624         */
8625     
8626         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8627     },
8628     // private
8629     onLoad : function()
8630     {
8631         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8632     },
8633
8634     // private
8635     onBeforeLoad : function(){
8636         if(!this.disabled){
8637             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8638         }
8639     },
8640
8641     // private
8642     destroy : function(){
8643         if(this.store){
8644             this.store.un('beforeload', this.onBeforeLoad, this);
8645             this.store.un('load', this.onLoad, this);
8646             this.store.un('loadexception', this.onLoadException, this);
8647         }else{
8648             var um = this.el.getUpdateManager();
8649             um.un('beforeupdate', this.onBeforeLoad, this);
8650             um.un('update', this.onLoad, this);
8651             um.un('failure', this.onLoad, this);
8652         }
8653     }
8654 };/**
8655  * @class Roo.bootstrap.Table
8656  * @licence LGBL
8657  * @extends Roo.bootstrap.Component
8658  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8659  * Similar to Roo.grid.Grid
8660  * <pre><code>
8661  var table = Roo.factory({
8662     xtype : 'Table',
8663     xns : Roo.bootstrap,
8664     autoSizeColumns: true,
8665     
8666     
8667     store : {
8668         xtype : 'Store',
8669         xns : Roo.data,
8670         remoteSort : true,
8671         sortInfo : { direction : 'ASC', field: 'name' },
8672         proxy : {
8673            xtype : 'HttpProxy',
8674            xns : Roo.data,
8675            method : 'GET',
8676            url : 'https://example.com/some.data.url.json'
8677         },
8678         reader : {
8679            xtype : 'JsonReader',
8680            xns : Roo.data,
8681            fields : [ 'id', 'name', whatever' ],
8682            id : 'id',
8683            root : 'data'
8684         }
8685     },
8686     cm : [
8687         {
8688             xtype : 'ColumnModel',
8689             xns : Roo.grid,
8690             align : 'center',
8691             cursor : 'pointer',
8692             dataIndex : 'is_in_group',
8693             header : "Name",
8694             sortable : true,
8695             renderer : function(v, x , r) {  
8696             
8697                 return String.format("{0}", v)
8698             }
8699             width : 3
8700         } // more columns..
8701     ],
8702     selModel : {
8703         xtype : 'RowSelectionModel',
8704         xns : Roo.bootstrap.Table
8705         // you can add listeners to catch selection change here....
8706     }
8707      
8708
8709  });
8710  // set any options
8711  grid.render(Roo.get("some-div"));
8712 </code></pre>
8713
8714 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8715
8716
8717
8718  *
8719  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8720  * @cfg {Roo.data.Store} store The data store to use
8721  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8722  * 
8723  * @cfg {String} cls table class
8724  *
8725  * 
8726  * @cfg {boolean} striped Should the rows be alternative striped
8727  * @cfg {boolean} bordered Add borders to the table
8728  * @cfg {boolean} hover Add hover highlighting
8729  * @cfg {boolean} condensed Format condensed
8730  * @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,
8731  *                also adds table-responsive (see bootstrap docs for details)
8732  * @cfg {Boolean} loadMask (true|false) default false
8733  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8734  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8735  * @cfg {Boolean} rowSelection (true|false) default false
8736  * @cfg {Boolean} cellSelection (true|false) default false
8737  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8738  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8739  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8740  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8741  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8742  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8743  * 
8744  * @constructor
8745  * Create a new Table
8746  * @param {Object} config The config object
8747  */
8748
8749 Roo.bootstrap.Table = function(config)
8750 {
8751     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8752      
8753     // BC...
8754     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8755     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8756     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8757     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8758     
8759     this.view = this; // compat with grid.
8760     
8761     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8762     if (this.sm) {
8763         this.sm.grid = this;
8764         this.selModel = Roo.factory(this.sm, Roo.grid);
8765         this.sm = this.selModel;
8766         this.sm.xmodule = this.xmodule || false;
8767     }
8768     
8769     if (this.cm && typeof(this.cm.config) == 'undefined') {
8770         this.colModel = new Roo.grid.ColumnModel(this.cm);
8771         this.cm = this.colModel;
8772         this.cm.xmodule = this.xmodule || false;
8773     }
8774     if (this.store) {
8775         this.store= Roo.factory(this.store, Roo.data);
8776         this.ds = this.store;
8777         this.ds.xmodule = this.xmodule || false;
8778          
8779     }
8780     if (this.footer && this.store) {
8781         this.footer.dataSource = this.ds;
8782         this.footer = Roo.factory(this.footer);
8783     }
8784     
8785     /** @private */
8786     this.addEvents({
8787         /**
8788          * @event cellclick
8789          * Fires when a cell is clicked
8790          * @param {Roo.bootstrap.Table} this
8791          * @param {Roo.Element} el
8792          * @param {Number} rowIndex
8793          * @param {Number} columnIndex
8794          * @param {Roo.EventObject} e
8795          */
8796         "cellclick" : true,
8797         /**
8798          * @event celldblclick
8799          * Fires when a cell is double clicked
8800          * @param {Roo.bootstrap.Table} this
8801          * @param {Roo.Element} el
8802          * @param {Number} rowIndex
8803          * @param {Number} columnIndex
8804          * @param {Roo.EventObject} e
8805          */
8806         "celldblclick" : true,
8807         /**
8808          * @event rowclick
8809          * Fires when a row is clicked
8810          * @param {Roo.bootstrap.Table} this
8811          * @param {Roo.Element} el
8812          * @param {Number} rowIndex
8813          * @param {Roo.EventObject} e
8814          */
8815         "rowclick" : true,
8816         /**
8817          * @event rowdblclick
8818          * Fires when a row is double clicked
8819          * @param {Roo.bootstrap.Table} this
8820          * @param {Roo.Element} el
8821          * @param {Number} rowIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "rowdblclick" : true,
8825         /**
8826          * @event mouseover
8827          * Fires when a mouseover occur
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Number} columnIndex
8832          * @param {Roo.EventObject} e
8833          */
8834         "mouseover" : true,
8835         /**
8836          * @event mouseout
8837          * Fires when a mouseout occur
8838          * @param {Roo.bootstrap.Table} this
8839          * @param {Roo.Element} el
8840          * @param {Number} rowIndex
8841          * @param {Number} columnIndex
8842          * @param {Roo.EventObject} e
8843          */
8844         "mouseout" : true,
8845         /**
8846          * @event rowclass
8847          * Fires when a row is rendered, so you can change add a style to it.
8848          * @param {Roo.bootstrap.Table} this
8849          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8850          */
8851         'rowclass' : true,
8852           /**
8853          * @event rowsrendered
8854          * Fires when all the  rows have been rendered
8855          * @param {Roo.bootstrap.Table} this
8856          */
8857         'rowsrendered' : true,
8858         /**
8859          * @event contextmenu
8860          * The raw contextmenu event for the entire grid.
8861          * @param {Roo.EventObject} e
8862          */
8863         "contextmenu" : true,
8864         /**
8865          * @event rowcontextmenu
8866          * Fires when a row is right clicked
8867          * @param {Roo.bootstrap.Table} this
8868          * @param {Number} rowIndex
8869          * @param {Roo.EventObject} e
8870          */
8871         "rowcontextmenu" : true,
8872         /**
8873          * @event cellcontextmenu
8874          * Fires when a cell is right clicked
8875          * @param {Roo.bootstrap.Table} this
8876          * @param {Number} rowIndex
8877          * @param {Number} cellIndex
8878          * @param {Roo.EventObject} e
8879          */
8880          "cellcontextmenu" : true,
8881          /**
8882          * @event headercontextmenu
8883          * Fires when a header is right clicked
8884          * @param {Roo.bootstrap.Table} this
8885          * @param {Number} columnIndex
8886          * @param {Roo.EventObject} e
8887          */
8888         "headercontextmenu" : true,
8889         /**
8890          * @event mousedown
8891          * The raw mousedown event for the entire grid.
8892          * @param {Roo.EventObject} e
8893          */
8894         "mousedown" : true
8895         
8896     });
8897 };
8898
8899 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8900     
8901     cls: false,
8902     
8903     striped : false,
8904     scrollBody : false,
8905     bordered: false,
8906     hover:  false,
8907     condensed : false,
8908     responsive : false,
8909     sm : false,
8910     cm : false,
8911     store : false,
8912     loadMask : false,
8913     footerShow : true,
8914     headerShow : true,
8915     enableColumnResize: true,
8916   
8917     rowSelection : false,
8918     cellSelection : false,
8919     layout : false,
8920
8921     minColumnWidth : 50,
8922     
8923     // Roo.Element - the tbody
8924     bodyEl: false,  // <tbody> Roo.Element - thead element    
8925     headEl: false,  // <thead> Roo.Element - thead element
8926     resizeProxy : false, // proxy element for dragging?
8927
8928
8929     
8930     container: false, // used by gridpanel...
8931     
8932     lazyLoad : false,
8933     
8934     CSS : Roo.util.CSS,
8935     
8936     auto_hide_footer : false,
8937     
8938     view: false, // actually points to this..
8939     
8940     getAutoCreate : function()
8941     {
8942         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8943         
8944         cfg = {
8945             tag: 'table',
8946             cls : 'table', 
8947             cn : []
8948         };
8949         // this get's auto added by panel.Grid
8950         if (this.scrollBody) {
8951             cfg.cls += ' table-body-fixed';
8952         }    
8953         if (this.striped) {
8954             cfg.cls += ' table-striped';
8955         }
8956         
8957         if (this.hover) {
8958             cfg.cls += ' table-hover';
8959         }
8960         if (this.bordered) {
8961             cfg.cls += ' table-bordered';
8962         }
8963         if (this.condensed) {
8964             cfg.cls += ' table-condensed';
8965         }
8966         
8967         if (this.responsive) {
8968             cfg.cls += ' table-responsive';
8969         }
8970         
8971         if (this.cls) {
8972             cfg.cls+=  ' ' +this.cls;
8973         }
8974         
8975         
8976         
8977         if (this.layout) {
8978             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8979         }
8980         
8981         if(this.store || this.cm){
8982             if(this.headerShow){
8983                 cfg.cn.push(this.renderHeader());
8984             }
8985             
8986             cfg.cn.push(this.renderBody());
8987             
8988             if(this.footerShow){
8989                 cfg.cn.push(this.renderFooter());
8990             }
8991             // where does this come from?
8992             //cfg.cls+=  ' TableGrid';
8993         }
8994         
8995         return { cn : [ cfg ] };
8996     },
8997     
8998     initEvents : function()
8999     {   
9000         if(!this.store || !this.cm){
9001             return;
9002         }
9003         if (this.selModel) {
9004             this.selModel.initEvents();
9005         }
9006         
9007         
9008         //Roo.log('initEvents with ds!!!!');
9009         
9010         this.bodyEl = this.el.select('tbody', true).first();
9011         this.headEl = this.el.select('thead', true).first();
9012         this.mainFoot = this.el.select('tfoot', true).first();
9013         
9014         
9015         
9016         
9017         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9018             e.on('click', this.sort, this);
9019         }, this);
9020         
9021         
9022         // why is this done????? = it breaks dialogs??
9023         //this.parent().el.setStyle('position', 'relative');
9024         
9025         
9026         if (this.footer) {
9027             this.footer.parentId = this.id;
9028             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9029             
9030             if(this.lazyLoad){
9031                 this.el.select('tfoot tr td').first().addClass('hide');
9032             }
9033         } 
9034         
9035         if(this.loadMask) {
9036             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9037         }
9038         
9039         this.store.on('load', this.onLoad, this);
9040         this.store.on('beforeload', this.onBeforeLoad, this);
9041         this.store.on('update', this.onUpdate, this);
9042         this.store.on('add', this.onAdd, this);
9043         this.store.on("clear", this.clear, this);
9044         
9045         this.el.on("contextmenu", this.onContextMenu, this);
9046         
9047         
9048         this.cm.on("headerchange", this.onHeaderChange, this);
9049         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9050
9051  //?? does bodyEl get replaced on render?
9052         this.bodyEl.on("click", this.onClick, this);
9053         this.bodyEl.on("dblclick", this.onDblClick, this);        
9054         this.bodyEl.on('scroll', this.onBodyScroll, this);
9055
9056         // guessing mainbody will work - this relays usually caught by selmodel at present.
9057         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9058   
9059   
9060         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9061         
9062   
9063         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9064             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9065         }
9066         
9067         this.initCSS();
9068     },
9069     // Compatibility with grid - we implement all the view features at present.
9070     getView : function()
9071     {
9072         return this;
9073     },
9074     
9075     initCSS : function()
9076     {
9077         
9078         
9079         var cm = this.cm, styles = [];
9080         this.CSS.removeStyleSheet(this.id + '-cssrules');
9081         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9082         // we can honour xs/sm/md/xl  as widths...
9083         // we first have to decide what widht we are currently at...
9084         var sz = Roo.getGridSize();
9085         
9086         var total = 0;
9087         var last = -1;
9088         var cols = []; // visable cols.
9089         var total_abs = 0;
9090         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9091             var w = cm.getColumnWidth(i, false);
9092             if(cm.isHidden(i)){
9093                 cols.push( { rel : false, abs : 0 });
9094                 continue;
9095             }
9096             if (w !== false) {
9097                 cols.push( { rel : false, abs : w });
9098                 total_abs += w;
9099                 last = i; // not really..
9100                 continue;
9101             }
9102             var w = cm.getColumnWidth(i, sz);
9103             if (w > 0) {
9104                 last = i
9105             }
9106             total += w;
9107             cols.push( { rel : w, abs : false });
9108         }
9109         
9110         var avail = this.bodyEl.dom.clientWidth - total_abs;
9111         
9112         var unitWidth = Math.floor(avail / total);
9113         var rem = avail - (unitWidth * total);
9114         
9115         var hidden, width, pos = 0 , splithide , left;
9116         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9117             
9118             hidden = 'display:none;';
9119             left = '';
9120             width  = 'width:0px;';
9121             splithide = '';
9122             if(!cm.isHidden(i)){
9123                 hidden = '';
9124                 
9125                 
9126                 // we can honour xs/sm/md/xl ?
9127                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9128                 if (w===0) {
9129                     hidden = 'display:none;';
9130                 }
9131                 // width should return a small number...
9132                 if (i == last) {
9133                     w+=rem; // add the remaining with..
9134                 }
9135                 pos += w;
9136                 left = "left:" + (pos -4) + "px;";
9137                 width = "width:" + w+ "px;";
9138                 
9139             }
9140             if (this.responsive) {
9141                 width = '';
9142                 left = '';
9143                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9144                 splithide = 'display: none;';
9145             }
9146             
9147             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9148             if (this.headEl) {
9149                 if (i == last) {
9150                     splithide = 'display:none;';
9151                 }
9152                 
9153                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9154                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9155                 );
9156             }
9157             
9158         }
9159         //Roo.log(styles.join(''));
9160         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9161         
9162     },
9163     
9164     
9165     
9166     onContextMenu : function(e, t)
9167     {
9168         this.processEvent("contextmenu", e);
9169     },
9170     
9171     processEvent : function(name, e)
9172     {
9173         if (name != 'touchstart' ) {
9174             this.fireEvent(name, e);    
9175         }
9176         
9177         var t = e.getTarget();
9178         
9179         var cell = Roo.get(t);
9180         
9181         if(!cell){
9182             return;
9183         }
9184         
9185         if(cell.findParent('tfoot', false, true)){
9186             return;
9187         }
9188         
9189         if(cell.findParent('thead', false, true)){
9190             
9191             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9192                 cell = Roo.get(t).findParent('th', false, true);
9193                 if (!cell) {
9194                     Roo.log("failed to find th in thead?");
9195                     Roo.log(e.getTarget());
9196                     return;
9197                 }
9198             }
9199             
9200             var cellIndex = cell.dom.cellIndex;
9201             
9202             var ename = name == 'touchstart' ? 'click' : name;
9203             this.fireEvent("header" + ename, this, cellIndex, e);
9204             
9205             return;
9206         }
9207         
9208         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9209             cell = Roo.get(t).findParent('td', false, true);
9210             if (!cell) {
9211                 Roo.log("failed to find th in tbody?");
9212                 Roo.log(e.getTarget());
9213                 return;
9214             }
9215         }
9216         
9217         var row = cell.findParent('tr', false, true);
9218         var cellIndex = cell.dom.cellIndex;
9219         var rowIndex = row.dom.rowIndex - 1;
9220         
9221         if(row !== false){
9222             
9223             this.fireEvent("row" + name, this, rowIndex, e);
9224             
9225             if(cell !== false){
9226             
9227                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9228             }
9229         }
9230         
9231     },
9232     
9233     onMouseover : function(e, el)
9234     {
9235         var cell = Roo.get(el);
9236         
9237         if(!cell){
9238             return;
9239         }
9240         
9241         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9242             cell = cell.findParent('td', false, true);
9243         }
9244         
9245         var row = cell.findParent('tr', false, true);
9246         var cellIndex = cell.dom.cellIndex;
9247         var rowIndex = row.dom.rowIndex - 1; // start from 0
9248         
9249         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9250         
9251     },
9252     
9253     onMouseout : function(e, el)
9254     {
9255         var cell = Roo.get(el);
9256         
9257         if(!cell){
9258             return;
9259         }
9260         
9261         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9262             cell = cell.findParent('td', false, true);
9263         }
9264         
9265         var row = cell.findParent('tr', false, true);
9266         var cellIndex = cell.dom.cellIndex;
9267         var rowIndex = row.dom.rowIndex - 1; // start from 0
9268         
9269         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9270         
9271     },
9272     
9273     onClick : function(e, el)
9274     {
9275         var cell = Roo.get(el);
9276         
9277         if(!cell || (!this.cellSelection && !this.rowSelection)){
9278             return;
9279         }
9280         
9281         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9282             cell = cell.findParent('td', false, true);
9283         }
9284         
9285         if(!cell || typeof(cell) == 'undefined'){
9286             return;
9287         }
9288         
9289         var row = cell.findParent('tr', false, true);
9290         
9291         if(!row || typeof(row) == 'undefined'){
9292             return;
9293         }
9294         
9295         var cellIndex = cell.dom.cellIndex;
9296         var rowIndex = this.getRowIndex(row);
9297         
9298         // why??? - should these not be based on SelectionModel?
9299         //if(this.cellSelection){
9300             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9301         //}
9302         
9303         //if(this.rowSelection){
9304             this.fireEvent('rowclick', this, row, rowIndex, e);
9305         //}
9306          
9307     },
9308         
9309     onDblClick : function(e,el)
9310     {
9311         var cell = Roo.get(el);
9312         
9313         if(!cell || (!this.cellSelection && !this.rowSelection)){
9314             return;
9315         }
9316         
9317         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9318             cell = cell.findParent('td', false, true);
9319         }
9320         
9321         if(!cell || typeof(cell) == 'undefined'){
9322             return;
9323         }
9324         
9325         var row = cell.findParent('tr', false, true);
9326         
9327         if(!row || typeof(row) == 'undefined'){
9328             return;
9329         }
9330         
9331         var cellIndex = cell.dom.cellIndex;
9332         var rowIndex = this.getRowIndex(row);
9333         
9334         if(this.cellSelection){
9335             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9336         }
9337         
9338         if(this.rowSelection){
9339             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9340         }
9341     },
9342     findRowIndex : function(el)
9343     {
9344         var cell = Roo.get(el);
9345         if(!cell) {
9346             return false;
9347         }
9348         var row = cell.findParent('tr', false, true);
9349         
9350         if(!row || typeof(row) == 'undefined'){
9351             return false;
9352         }
9353         return this.getRowIndex(row);
9354     },
9355     sort : function(e,el)
9356     {
9357         var col = Roo.get(el);
9358         
9359         if(!col.hasClass('sortable')){
9360             return;
9361         }
9362         
9363         var sort = col.attr('sort');
9364         var dir = 'ASC';
9365         
9366         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9367             dir = 'DESC';
9368         }
9369         
9370         this.store.sortInfo = {field : sort, direction : dir};
9371         
9372         if (this.footer) {
9373             Roo.log("calling footer first");
9374             this.footer.onClick('first');
9375         } else {
9376         
9377             this.store.load({ params : { start : 0 } });
9378         }
9379     },
9380     
9381     renderHeader : function()
9382     {
9383         var header = {
9384             tag: 'thead',
9385             cn : []
9386         };
9387         
9388         var cm = this.cm;
9389         this.totalWidth = 0;
9390         
9391         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9392             
9393             var config = cm.config[i];
9394             
9395             var c = {
9396                 tag: 'th',
9397                 cls : 'x-hcol-' + i,
9398                 style : '',
9399                 
9400                 html: cm.getColumnHeader(i)
9401             };
9402             
9403             var tooltip = cm.getColumnTooltip(i);
9404             if (tooltip) {
9405                 c.tooltip = tooltip;
9406             }
9407             
9408             
9409             var hh = '';
9410             
9411             if(typeof(config.sortable) != 'undefined' && config.sortable){
9412                 c.cls += ' sortable';
9413                 c.html = '<i class="fa"></i>' + c.html;
9414             }
9415             
9416             // could use BS4 hidden-..-down 
9417             
9418             if(typeof(config.lgHeader) != 'undefined'){
9419                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9420             }
9421             
9422             if(typeof(config.mdHeader) != 'undefined'){
9423                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9424             }
9425             
9426             if(typeof(config.smHeader) != 'undefined'){
9427                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9428             }
9429             
9430             if(typeof(config.xsHeader) != 'undefined'){
9431                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9432             }
9433             
9434             if(hh.length){
9435                 c.html = hh;
9436             }
9437             
9438             if(typeof(config.tooltip) != 'undefined'){
9439                 c.tooltip = config.tooltip;
9440             }
9441             
9442             if(typeof(config.colspan) != 'undefined'){
9443                 c.colspan = config.colspan;
9444             }
9445             
9446             // hidden is handled by CSS now
9447             
9448             if(typeof(config.dataIndex) != 'undefined'){
9449                 c.sort = config.dataIndex;
9450             }
9451             
9452            
9453             
9454             if(typeof(config.align) != 'undefined' && config.align.length){
9455                 c.style += ' text-align:' + config.align + ';';
9456             }
9457             
9458             /* width is done in CSS
9459              *if(typeof(config.width) != 'undefined'){
9460                 c.style += ' width:' + config.width + 'px;';
9461                 this.totalWidth += config.width;
9462             } else {
9463                 this.totalWidth += 100; // assume minimum of 100 per column?
9464             }
9465             */
9466             
9467             if(typeof(config.cls) != 'undefined'){
9468                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9469             }
9470             // this is the bit that doesnt reall work at all...
9471             
9472             if (this.responsive) {
9473                  
9474             
9475                 ['xs','sm','md','lg'].map(function(size){
9476                     
9477                     if(typeof(config[size]) == 'undefined'){
9478                         return;
9479                     }
9480                      
9481                     if (!config[size]) { // 0 = hidden
9482                         // BS 4 '0' is treated as hide that column and below.
9483                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9484                         return;
9485                     }
9486                     
9487                     c.cls += ' col-' + size + '-' + config[size] + (
9488                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9489                     );
9490                     
9491                     
9492                 });
9493             }
9494             // at the end?
9495             
9496             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9497             
9498             
9499             
9500             
9501             header.cn.push(c)
9502         }
9503         
9504         return header;
9505     },
9506     
9507     renderBody : function()
9508     {
9509         var body = {
9510             tag: 'tbody',
9511             cn : [
9512                 {
9513                     tag: 'tr',
9514                     cn : [
9515                         {
9516                             tag : 'td',
9517                             colspan :  this.cm.getColumnCount()
9518                         }
9519                     ]
9520                 }
9521             ]
9522         };
9523         
9524         return body;
9525     },
9526     
9527     renderFooter : function()
9528     {
9529         var footer = {
9530             tag: 'tfoot',
9531             cn : [
9532                 {
9533                     tag: 'tr',
9534                     cn : [
9535                         {
9536                             tag : 'td',
9537                             colspan :  this.cm.getColumnCount()
9538                         }
9539                     ]
9540                 }
9541             ]
9542         };
9543         
9544         return footer;
9545     },
9546     
9547     
9548     
9549     onLoad : function()
9550     {
9551 //        Roo.log('ds onload');
9552         this.clear();
9553         
9554         var _this = this;
9555         var cm = this.cm;
9556         var ds = this.store;
9557         
9558         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9559             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9560             if (_this.store.sortInfo) {
9561                     
9562                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9563                     e.select('i', true).addClass(['fa-arrow-up']);
9564                 }
9565                 
9566                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9567                     e.select('i', true).addClass(['fa-arrow-down']);
9568                 }
9569             }
9570         });
9571         
9572         var tbody =  this.bodyEl;
9573               
9574         if(ds.getCount() > 0){
9575             ds.data.each(function(d,rowIndex){
9576                 var row =  this.renderRow(cm, ds, rowIndex);
9577                 
9578                 tbody.createChild(row);
9579                 
9580                 var _this = this;
9581                 
9582                 if(row.cellObjects.length){
9583                     Roo.each(row.cellObjects, function(r){
9584                         _this.renderCellObject(r);
9585                     })
9586                 }
9587                 
9588             }, this);
9589         }
9590         
9591         var tfoot = this.el.select('tfoot', true).first();
9592         
9593         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9594             
9595             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9596             
9597             var total = this.ds.getTotalCount();
9598             
9599             if(this.footer.pageSize < total){
9600                 this.mainFoot.show();
9601             }
9602         }
9603         
9604         Roo.each(this.el.select('tbody td', true).elements, function(e){
9605             e.on('mouseover', _this.onMouseover, _this);
9606         });
9607         
9608         Roo.each(this.el.select('tbody td', true).elements, function(e){
9609             e.on('mouseout', _this.onMouseout, _this);
9610         });
9611         this.fireEvent('rowsrendered', this);
9612         
9613         this.autoSize();
9614         
9615         this.initCSS(); /// resize cols
9616
9617         
9618     },
9619     
9620     
9621     onUpdate : function(ds,record)
9622     {
9623         this.refreshRow(record);
9624         this.autoSize();
9625     },
9626     
9627     onRemove : function(ds, record, index, isUpdate){
9628         if(isUpdate !== true){
9629             this.fireEvent("beforerowremoved", this, index, record);
9630         }
9631         var bt = this.bodyEl.dom;
9632         
9633         var rows = this.el.select('tbody > tr', true).elements;
9634         
9635         if(typeof(rows[index]) != 'undefined'){
9636             bt.removeChild(rows[index].dom);
9637         }
9638         
9639 //        if(bt.rows[index]){
9640 //            bt.removeChild(bt.rows[index]);
9641 //        }
9642         
9643         if(isUpdate !== true){
9644             //this.stripeRows(index);
9645             //this.syncRowHeights(index, index);
9646             //this.layout();
9647             this.fireEvent("rowremoved", this, index, record);
9648         }
9649     },
9650     
9651     onAdd : function(ds, records, rowIndex)
9652     {
9653         //Roo.log('on Add called');
9654         // - note this does not handle multiple adding very well..
9655         var bt = this.bodyEl.dom;
9656         for (var i =0 ; i < records.length;i++) {
9657             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9658             //Roo.log(records[i]);
9659             //Roo.log(this.store.getAt(rowIndex+i));
9660             this.insertRow(this.store, rowIndex + i, false);
9661             return;
9662         }
9663         
9664     },
9665     
9666     
9667     refreshRow : function(record){
9668         var ds = this.store, index;
9669         if(typeof record == 'number'){
9670             index = record;
9671             record = ds.getAt(index);
9672         }else{
9673             index = ds.indexOf(record);
9674             if (index < 0) {
9675                 return; // should not happen - but seems to 
9676             }
9677         }
9678         this.insertRow(ds, index, true);
9679         this.autoSize();
9680         this.onRemove(ds, record, index+1, true);
9681         this.autoSize();
9682         //this.syncRowHeights(index, index);
9683         //this.layout();
9684         this.fireEvent("rowupdated", this, index, record);
9685     },
9686     // private - called by RowSelection
9687     onRowSelect : function(rowIndex){
9688         var row = this.getRowDom(rowIndex);
9689         row.addClass(['bg-info','info']);
9690     },
9691     // private - called by RowSelection
9692     onRowDeselect : function(rowIndex)
9693     {
9694         if (rowIndex < 0) {
9695             return;
9696         }
9697         var row = this.getRowDom(rowIndex);
9698         row.removeClass(['bg-info','info']);
9699     },
9700       /**
9701      * Focuses the specified row.
9702      * @param {Number} row The row index
9703      */
9704     focusRow : function(row)
9705     {
9706         //Roo.log('GridView.focusRow');
9707         var x = this.bodyEl.dom.scrollLeft;
9708         this.focusCell(row, 0, false);
9709         this.bodyEl.dom.scrollLeft = x;
9710
9711     },
9712      /**
9713      * Focuses the specified cell.
9714      * @param {Number} row The row index
9715      * @param {Number} col The column index
9716      * @param {Boolean} hscroll false to disable horizontal scrolling
9717      */
9718     focusCell : function(row, col, hscroll)
9719     {
9720         //Roo.log('GridView.focusCell');
9721         var el = this.ensureVisible(row, col, hscroll);
9722         // not sure what focusEL achives = it's a <a> pos relative 
9723         //this.focusEl.alignTo(el, "tl-tl");
9724         //if(Roo.isGecko){
9725         //    this.focusEl.focus();
9726         //}else{
9727         //    this.focusEl.focus.defer(1, this.focusEl);
9728         //}
9729     },
9730     
9731      /**
9732      * Scrolls the specified cell into view
9733      * @param {Number} row The row index
9734      * @param {Number} col The column index
9735      * @param {Boolean} hscroll false to disable horizontal scrolling
9736      */
9737     ensureVisible : function(row, col, hscroll)
9738     {
9739         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9740         //return null; //disable for testing.
9741         if(typeof row != "number"){
9742             row = row.rowIndex;
9743         }
9744         if(row < 0 && row >= this.ds.getCount()){
9745             return  null;
9746         }
9747         col = (col !== undefined ? col : 0);
9748         var cm = this.cm;
9749         while(cm.isHidden(col)){
9750             col++;
9751         }
9752
9753         var el = this.getCellDom(row, col);
9754         if(!el){
9755             return null;
9756         }
9757         var c = this.bodyEl.dom;
9758
9759         var ctop = parseInt(el.offsetTop, 10);
9760         var cleft = parseInt(el.offsetLeft, 10);
9761         var cbot = ctop + el.offsetHeight;
9762         var cright = cleft + el.offsetWidth;
9763
9764         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9765         var ch = 0; //?? header is not withing the area?
9766         var stop = parseInt(c.scrollTop, 10);
9767         var sleft = parseInt(c.scrollLeft, 10);
9768         var sbot = stop + ch;
9769         var sright = sleft + c.clientWidth;
9770         /*
9771         Roo.log('GridView.ensureVisible:' +
9772                 ' ctop:' + ctop +
9773                 ' c.clientHeight:' + c.clientHeight +
9774                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9775                 ' stop:' + stop +
9776                 ' cbot:' + cbot +
9777                 ' sbot:' + sbot +
9778                 ' ch:' + ch  
9779                 );
9780         */
9781         if(ctop < stop){
9782             c.scrollTop = ctop;
9783             //Roo.log("set scrolltop to ctop DISABLE?");
9784         }else if(cbot > sbot){
9785             //Roo.log("set scrolltop to cbot-ch");
9786             c.scrollTop = cbot-ch;
9787         }
9788
9789         if(hscroll !== false){
9790             if(cleft < sleft){
9791                 c.scrollLeft = cleft;
9792             }else if(cright > sright){
9793                 c.scrollLeft = cright-c.clientWidth;
9794             }
9795         }
9796
9797         return el;
9798     },
9799     
9800     
9801     insertRow : function(dm, rowIndex, isUpdate){
9802         
9803         if(!isUpdate){
9804             this.fireEvent("beforerowsinserted", this, rowIndex);
9805         }
9806             //var s = this.getScrollState();
9807         var row = this.renderRow(this.cm, this.store, rowIndex);
9808         // insert before rowIndex..
9809         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9810         
9811         var _this = this;
9812                 
9813         if(row.cellObjects.length){
9814             Roo.each(row.cellObjects, function(r){
9815                 _this.renderCellObject(r);
9816             })
9817         }
9818             
9819         if(!isUpdate){
9820             this.fireEvent("rowsinserted", this, rowIndex);
9821             //this.syncRowHeights(firstRow, lastRow);
9822             //this.stripeRows(firstRow);
9823             //this.layout();
9824         }
9825         
9826     },
9827     
9828     
9829     getRowDom : function(rowIndex)
9830     {
9831         var rows = this.el.select('tbody > tr', true).elements;
9832         
9833         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9834         
9835     },
9836     getCellDom : function(rowIndex, colIndex)
9837     {
9838         var row = this.getRowDom(rowIndex);
9839         if (row === false) {
9840             return false;
9841         }
9842         var cols = row.select('td', true).elements;
9843         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9844         
9845     },
9846     
9847     // returns the object tree for a tr..
9848   
9849     
9850     renderRow : function(cm, ds, rowIndex) 
9851     {
9852         var d = ds.getAt(rowIndex);
9853         
9854         var row = {
9855             tag : 'tr',
9856             cls : 'x-row-' + rowIndex,
9857             cn : []
9858         };
9859             
9860         var cellObjects = [];
9861         
9862         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9863             var config = cm.config[i];
9864             
9865             var renderer = cm.getRenderer(i);
9866             var value = '';
9867             var id = false;
9868             
9869             if(typeof(renderer) !== 'undefined'){
9870                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9871             }
9872             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9873             // and are rendered into the cells after the row is rendered - using the id for the element.
9874             
9875             if(typeof(value) === 'object'){
9876                 id = Roo.id();
9877                 cellObjects.push({
9878                     container : id,
9879                     cfg : value 
9880                 })
9881             }
9882             
9883             var rowcfg = {
9884                 record: d,
9885                 rowIndex : rowIndex,
9886                 colIndex : i,
9887                 rowClass : ''
9888             };
9889
9890             this.fireEvent('rowclass', this, rowcfg);
9891             
9892             var td = {
9893                 tag: 'td',
9894                 // this might end up displaying HTML?
9895                 // this is too messy... - better to only do it on columsn you know are going to be too long
9896                 //tooltip : (typeof(value) === 'object') ? '' : value,
9897                 cls : rowcfg.rowClass + ' x-col-' + i,
9898                 style: '',
9899                 html: (typeof(value) === 'object') ? '' : value
9900             };
9901             
9902             if (id) {
9903                 td.id = id;
9904             }
9905             
9906             if(typeof(config.colspan) != 'undefined'){
9907                 td.colspan = config.colspan;
9908             }
9909             
9910             
9911             
9912             if(typeof(config.align) != 'undefined' && config.align.length){
9913                 td.style += ' text-align:' + config.align + ';';
9914             }
9915             if(typeof(config.valign) != 'undefined' && config.valign.length){
9916                 td.style += ' vertical-align:' + config.valign + ';';
9917             }
9918             /*
9919             if(typeof(config.width) != 'undefined'){
9920                 td.style += ' width:' +  config.width + 'px;';
9921             }
9922             */
9923             
9924             if(typeof(config.cursor) != 'undefined'){
9925                 td.style += ' cursor:' +  config.cursor + ';';
9926             }
9927             
9928             if(typeof(config.cls) != 'undefined'){
9929                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9930             }
9931             if (this.responsive) {
9932                 ['xs','sm','md','lg'].map(function(size){
9933                     
9934                     if(typeof(config[size]) == 'undefined'){
9935                         return;
9936                     }
9937                     
9938                     
9939                       
9940                     if (!config[size]) { // 0 = hidden
9941                         // BS 4 '0' is treated as hide that column and below.
9942                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9943                         return;
9944                     }
9945                     
9946                     td.cls += ' col-' + size + '-' + config[size] + (
9947                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9948                     );
9949                      
9950     
9951                 });
9952             }
9953             row.cn.push(td);
9954            
9955         }
9956         
9957         row.cellObjects = cellObjects;
9958         
9959         return row;
9960           
9961     },
9962     
9963     
9964     
9965     onBeforeLoad : function()
9966     {
9967         
9968     },
9969      /**
9970      * Remove all rows
9971      */
9972     clear : function()
9973     {
9974         this.el.select('tbody', true).first().dom.innerHTML = '';
9975     },
9976     /**
9977      * Show or hide a row.
9978      * @param {Number} rowIndex to show or hide
9979      * @param {Boolean} state hide
9980      */
9981     setRowVisibility : function(rowIndex, state)
9982     {
9983         var bt = this.bodyEl.dom;
9984         
9985         var rows = this.el.select('tbody > tr', true).elements;
9986         
9987         if(typeof(rows[rowIndex]) == 'undefined'){
9988             return;
9989         }
9990         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9991         
9992     },
9993     
9994     
9995     getSelectionModel : function(){
9996         if(!this.selModel){
9997             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9998         }
9999         return this.selModel;
10000     },
10001     /*
10002      * Render the Roo.bootstrap object from renderder
10003      */
10004     renderCellObject : function(r)
10005     {
10006         var _this = this;
10007         
10008         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10009         
10010         var t = r.cfg.render(r.container);
10011         
10012         if(r.cfg.cn){
10013             Roo.each(r.cfg.cn, function(c){
10014                 var child = {
10015                     container: t.getChildContainer(),
10016                     cfg: c
10017                 };
10018                 _this.renderCellObject(child);
10019             })
10020         }
10021     },
10022     /**
10023      * get the Row Index from a dom element.
10024      * @param {Roo.Element} row The row to look for
10025      * @returns {Number} the row
10026      */
10027     getRowIndex : function(row)
10028     {
10029         var rowIndex = -1;
10030         
10031         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10032             if(el != row){
10033                 return;
10034             }
10035             
10036             rowIndex = index;
10037         });
10038         
10039         return rowIndex;
10040     },
10041     /**
10042      * get the header TH element for columnIndex
10043      * @param {Number} columnIndex
10044      * @returns {Roo.Element}
10045      */
10046     getHeaderIndex: function(colIndex)
10047     {
10048         var cols = this.headEl.select('th', true).elements;
10049         return cols[colIndex]; 
10050     },
10051     /**
10052      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10053      * @param {domElement} cell to look for
10054      * @returns {Number} the column
10055      */
10056     getCellIndex : function(cell)
10057     {
10058         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10059         if(id){
10060             return parseInt(id[1], 10);
10061         }
10062         return 0;
10063     },
10064      /**
10065      * Returns the grid's underlying element = used by panel.Grid
10066      * @return {Element} The element
10067      */
10068     getGridEl : function(){
10069         return this.el;
10070     },
10071      /**
10072      * Forces a resize - used by panel.Grid
10073      * @return {Element} The element
10074      */
10075     autoSize : function()
10076     {
10077         //var ctr = Roo.get(this.container.dom.parentElement);
10078         var ctr = Roo.get(this.el.dom);
10079         
10080         var thd = this.getGridEl().select('thead',true).first();
10081         var tbd = this.getGridEl().select('tbody', true).first();
10082         var tfd = this.getGridEl().select('tfoot', true).first();
10083         
10084         var cw = ctr.getWidth();
10085         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10086         
10087         if (tbd) {
10088             
10089             tbd.setWidth(ctr.getWidth());
10090             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10091             // this needs fixing for various usage - currently only hydra job advers I think..
10092             //tdb.setHeight(
10093             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10094             //); 
10095             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10096             cw -= barsize;
10097         }
10098         cw = Math.max(cw, this.totalWidth);
10099         this.getGridEl().select('tbody tr',true).setWidth(cw);
10100         this.initCSS();
10101         
10102         // resize 'expandable coloumn?
10103         
10104         return; // we doe not have a view in this design..
10105         
10106     },
10107     onBodyScroll: function()
10108     {
10109         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10110         if(this.headEl){
10111             this.headEl.setStyle({
10112                 'position' : 'relative',
10113                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10114             });
10115         }
10116         
10117         if(this.lazyLoad){
10118             
10119             var scrollHeight = this.bodyEl.dom.scrollHeight;
10120             
10121             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10122             
10123             var height = this.bodyEl.getHeight();
10124             
10125             if(scrollHeight - height == scrollTop) {
10126                 
10127                 var total = this.ds.getTotalCount();
10128                 
10129                 if(this.footer.cursor + this.footer.pageSize < total){
10130                     
10131                     this.footer.ds.load({
10132                         params : {
10133                             start : this.footer.cursor + this.footer.pageSize,
10134                             limit : this.footer.pageSize
10135                         },
10136                         add : true
10137                     });
10138                 }
10139             }
10140             
10141         }
10142     },
10143     onColumnSplitterMoved : function(i, diff)
10144     {
10145         this.userResized = true;
10146         
10147         var cm = this.colModel;
10148         
10149         var w = this.getHeaderIndex(i).getWidth() + diff;
10150         
10151         
10152         cm.setColumnWidth(i, w, true);
10153         this.initCSS();
10154         //var cid = cm.getColumnId(i); << not used in this version?
10155        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10156         
10157         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10158         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10159         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10160 */
10161         //this.updateSplitters();
10162         //this.layout(); << ??
10163         this.fireEvent("columnresize", i, w);
10164     },
10165     onHeaderChange : function()
10166     {
10167         var header = this.renderHeader();
10168         var table = this.el.select('table', true).first();
10169         
10170         this.headEl.remove();
10171         this.headEl = table.createChild(header, this.bodyEl, false);
10172         
10173         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10174             e.on('click', this.sort, this);
10175         }, this);
10176         
10177         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10178             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10179         }
10180         
10181     },
10182     
10183     onHiddenChange : function(colModel, colIndex, hidden)
10184     {
10185         /*
10186         this.cm.setHidden()
10187         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10188         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10189         
10190         this.CSS.updateRule(thSelector, "display", "");
10191         this.CSS.updateRule(tdSelector, "display", "");
10192         
10193         if(hidden){
10194             this.CSS.updateRule(thSelector, "display", "none");
10195             this.CSS.updateRule(tdSelector, "display", "none");
10196         }
10197         */
10198         // onload calls initCSS()
10199         this.onHeaderChange();
10200         this.onLoad();
10201     },
10202     
10203     setColumnWidth: function(col_index, width)
10204     {
10205         // width = "md-2 xs-2..."
10206         if(!this.colModel.config[col_index]) {
10207             return;
10208         }
10209         
10210         var w = width.split(" ");
10211         
10212         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10213         
10214         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10215         
10216         
10217         for(var j = 0; j < w.length; j++) {
10218             
10219             if(!w[j]) {
10220                 continue;
10221             }
10222             
10223             var size_cls = w[j].split("-");
10224             
10225             if(!Number.isInteger(size_cls[1] * 1)) {
10226                 continue;
10227             }
10228             
10229             if(!this.colModel.config[col_index][size_cls[0]]) {
10230                 continue;
10231             }
10232             
10233             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10234                 continue;
10235             }
10236             
10237             h_row[0].classList.replace(
10238                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10239                 "col-"+size_cls[0]+"-"+size_cls[1]
10240             );
10241             
10242             for(var i = 0; i < rows.length; i++) {
10243                 
10244                 var size_cls = w[j].split("-");
10245                 
10246                 if(!Number.isInteger(size_cls[1] * 1)) {
10247                     continue;
10248                 }
10249                 
10250                 if(!this.colModel.config[col_index][size_cls[0]]) {
10251                     continue;
10252                 }
10253                 
10254                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10255                     continue;
10256                 }
10257                 
10258                 rows[i].classList.replace(
10259                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10260                     "col-"+size_cls[0]+"-"+size_cls[1]
10261                 );
10262             }
10263             
10264             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10265         }
10266     }
10267 });
10268
10269 // currently only used to find the split on drag.. 
10270 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10271
10272 /**
10273  * @depricated
10274 */
10275 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10276 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10277 /*
10278  * - LGPL
10279  *
10280  * table cell
10281  * 
10282  */
10283
10284 /**
10285  * @class Roo.bootstrap.TableCell
10286  * @extends Roo.bootstrap.Component
10287  * Bootstrap TableCell class
10288  * @cfg {String} html cell contain text
10289  * @cfg {String} cls cell class
10290  * @cfg {String} tag cell tag (td|th) default td
10291  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10292  * @cfg {String} align Aligns the content in a cell
10293  * @cfg {String} axis Categorizes cells
10294  * @cfg {String} bgcolor Specifies the background color of a cell
10295  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10296  * @cfg {Number} colspan Specifies the number of columns a cell should span
10297  * @cfg {String} headers Specifies one or more header cells a cell is related to
10298  * @cfg {Number} height Sets the height of a cell
10299  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10300  * @cfg {Number} rowspan Sets the number of rows a cell should span
10301  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10302  * @cfg {String} valign Vertical aligns the content in a cell
10303  * @cfg {Number} width Specifies the width of a cell
10304  * 
10305  * @constructor
10306  * Create a new TableCell
10307  * @param {Object} config The config object
10308  */
10309
10310 Roo.bootstrap.TableCell = function(config){
10311     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10312 };
10313
10314 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10315     
10316     html: false,
10317     cls: false,
10318     tag: false,
10319     abbr: false,
10320     align: false,
10321     axis: false,
10322     bgcolor: false,
10323     charoff: false,
10324     colspan: false,
10325     headers: false,
10326     height: false,
10327     nowrap: false,
10328     rowspan: false,
10329     scope: false,
10330     valign: false,
10331     width: false,
10332     
10333     
10334     getAutoCreate : function(){
10335         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10336         
10337         cfg = {
10338             tag: 'td'
10339         };
10340         
10341         if(this.tag){
10342             cfg.tag = this.tag;
10343         }
10344         
10345         if (this.html) {
10346             cfg.html=this.html
10347         }
10348         if (this.cls) {
10349             cfg.cls=this.cls
10350         }
10351         if (this.abbr) {
10352             cfg.abbr=this.abbr
10353         }
10354         if (this.align) {
10355             cfg.align=this.align
10356         }
10357         if (this.axis) {
10358             cfg.axis=this.axis
10359         }
10360         if (this.bgcolor) {
10361             cfg.bgcolor=this.bgcolor
10362         }
10363         if (this.charoff) {
10364             cfg.charoff=this.charoff
10365         }
10366         if (this.colspan) {
10367             cfg.colspan=this.colspan
10368         }
10369         if (this.headers) {
10370             cfg.headers=this.headers
10371         }
10372         if (this.height) {
10373             cfg.height=this.height
10374         }
10375         if (this.nowrap) {
10376             cfg.nowrap=this.nowrap
10377         }
10378         if (this.rowspan) {
10379             cfg.rowspan=this.rowspan
10380         }
10381         if (this.scope) {
10382             cfg.scope=this.scope
10383         }
10384         if (this.valign) {
10385             cfg.valign=this.valign
10386         }
10387         if (this.width) {
10388             cfg.width=this.width
10389         }
10390         
10391         
10392         return cfg;
10393     }
10394    
10395 });
10396
10397  
10398
10399  /*
10400  * - LGPL
10401  *
10402  * table row
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.TableRow
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap TableRow class
10410  * @cfg {String} cls row class
10411  * @cfg {String} align Aligns the content in a table row
10412  * @cfg {String} bgcolor Specifies a background color for a table row
10413  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10414  * @cfg {String} valign Vertical aligns the content in a table row
10415  * 
10416  * @constructor
10417  * Create a new TableRow
10418  * @param {Object} config The config object
10419  */
10420
10421 Roo.bootstrap.TableRow = function(config){
10422     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10423 };
10424
10425 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10426     
10427     cls: false,
10428     align: false,
10429     bgcolor: false,
10430     charoff: false,
10431     valign: false,
10432     
10433     getAutoCreate : function(){
10434         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10435         
10436         cfg = {
10437             tag: 'tr'
10438         };
10439             
10440         if(this.cls){
10441             cfg.cls = this.cls;
10442         }
10443         if(this.align){
10444             cfg.align = this.align;
10445         }
10446         if(this.bgcolor){
10447             cfg.bgcolor = this.bgcolor;
10448         }
10449         if(this.charoff){
10450             cfg.charoff = this.charoff;
10451         }
10452         if(this.valign){
10453             cfg.valign = this.valign;
10454         }
10455         
10456         return cfg;
10457     }
10458    
10459 });
10460
10461  
10462
10463  /*
10464  * - LGPL
10465  *
10466  * table body
10467  * 
10468  */
10469
10470 /**
10471  * @class Roo.bootstrap.TableBody
10472  * @extends Roo.bootstrap.Component
10473  * Bootstrap TableBody class
10474  * @cfg {String} cls element class
10475  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10476  * @cfg {String} align Aligns the content inside the element
10477  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10478  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10479  * 
10480  * @constructor
10481  * Create a new TableBody
10482  * @param {Object} config The config object
10483  */
10484
10485 Roo.bootstrap.TableBody = function(config){
10486     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10487 };
10488
10489 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10490     
10491     cls: false,
10492     tag: false,
10493     align: false,
10494     charoff: false,
10495     valign: false,
10496     
10497     getAutoCreate : function(){
10498         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10499         
10500         cfg = {
10501             tag: 'tbody'
10502         };
10503             
10504         if (this.cls) {
10505             cfg.cls=this.cls
10506         }
10507         if(this.tag){
10508             cfg.tag = this.tag;
10509         }
10510         
10511         if(this.align){
10512             cfg.align = this.align;
10513         }
10514         if(this.charoff){
10515             cfg.charoff = this.charoff;
10516         }
10517         if(this.valign){
10518             cfg.valign = this.valign;
10519         }
10520         
10521         return cfg;
10522     }
10523     
10524     
10525 //    initEvents : function()
10526 //    {
10527 //        
10528 //        if(!this.store){
10529 //            return;
10530 //        }
10531 //        
10532 //        this.store = Roo.factory(this.store, Roo.data);
10533 //        this.store.on('load', this.onLoad, this);
10534 //        
10535 //        this.store.load();
10536 //        
10537 //    },
10538 //    
10539 //    onLoad: function () 
10540 //    {   
10541 //        this.fireEvent('load', this);
10542 //    }
10543 //    
10544 //   
10545 });
10546
10547  
10548
10549  /*
10550  * Based on:
10551  * Ext JS Library 1.1.1
10552  * Copyright(c) 2006-2007, Ext JS, LLC.
10553  *
10554  * Originally Released Under LGPL - original licence link has changed is not relivant.
10555  *
10556  * Fork - LGPL
10557  * <script type="text/javascript">
10558  */
10559
10560 // as we use this in bootstrap.
10561 Roo.namespace('Roo.form');
10562  /**
10563  * @class Roo.form.Action
10564  * Internal Class used to handle form actions
10565  * @constructor
10566  * @param {Roo.form.BasicForm} el The form element or its id
10567  * @param {Object} config Configuration options
10568  */
10569
10570  
10571  
10572 // define the action interface
10573 Roo.form.Action = function(form, options){
10574     this.form = form;
10575     this.options = options || {};
10576 };
10577 /**
10578  * Client Validation Failed
10579  * @const 
10580  */
10581 Roo.form.Action.CLIENT_INVALID = 'client';
10582 /**
10583  * Server Validation Failed
10584  * @const 
10585  */
10586 Roo.form.Action.SERVER_INVALID = 'server';
10587  /**
10588  * Connect to Server Failed
10589  * @const 
10590  */
10591 Roo.form.Action.CONNECT_FAILURE = 'connect';
10592 /**
10593  * Reading Data from Server Failed
10594  * @const 
10595  */
10596 Roo.form.Action.LOAD_FAILURE = 'load';
10597
10598 Roo.form.Action.prototype = {
10599     type : 'default',
10600     failureType : undefined,
10601     response : undefined,
10602     result : undefined,
10603
10604     // interface method
10605     run : function(options){
10606
10607     },
10608
10609     // interface method
10610     success : function(response){
10611
10612     },
10613
10614     // interface method
10615     handleResponse : function(response){
10616
10617     },
10618
10619     // default connection failure
10620     failure : function(response){
10621         
10622         this.response = response;
10623         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10624         this.form.afterAction(this, false);
10625     },
10626
10627     processResponse : function(response){
10628         this.response = response;
10629         if(!response.responseText){
10630             return true;
10631         }
10632         this.result = this.handleResponse(response);
10633         return this.result;
10634     },
10635
10636     // utility functions used internally
10637     getUrl : function(appendParams){
10638         var url = this.options.url || this.form.url || this.form.el.dom.action;
10639         if(appendParams){
10640             var p = this.getParams();
10641             if(p){
10642                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10643             }
10644         }
10645         return url;
10646     },
10647
10648     getMethod : function(){
10649         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10650     },
10651
10652     getParams : function(){
10653         var bp = this.form.baseParams;
10654         var p = this.options.params;
10655         if(p){
10656             if(typeof p == "object"){
10657                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10658             }else if(typeof p == 'string' && bp){
10659                 p += '&' + Roo.urlEncode(bp);
10660             }
10661         }else if(bp){
10662             p = Roo.urlEncode(bp);
10663         }
10664         return p;
10665     },
10666
10667     createCallback : function(){
10668         return {
10669             success: this.success,
10670             failure: this.failure,
10671             scope: this,
10672             timeout: (this.form.timeout*1000),
10673             upload: this.form.fileUpload ? this.success : undefined
10674         };
10675     }
10676 };
10677
10678 Roo.form.Action.Submit = function(form, options){
10679     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10680 };
10681
10682 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10683     type : 'submit',
10684
10685     haveProgress : false,
10686     uploadComplete : false,
10687     
10688     // uploadProgress indicator.
10689     uploadProgress : function()
10690     {
10691         if (!this.form.progressUrl) {
10692             return;
10693         }
10694         
10695         if (!this.haveProgress) {
10696             Roo.MessageBox.progress("Uploading", "Uploading");
10697         }
10698         if (this.uploadComplete) {
10699            Roo.MessageBox.hide();
10700            return;
10701         }
10702         
10703         this.haveProgress = true;
10704    
10705         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10706         
10707         var c = new Roo.data.Connection();
10708         c.request({
10709             url : this.form.progressUrl,
10710             params: {
10711                 id : uid
10712             },
10713             method: 'GET',
10714             success : function(req){
10715                //console.log(data);
10716                 var rdata = false;
10717                 var edata;
10718                 try  {
10719                    rdata = Roo.decode(req.responseText)
10720                 } catch (e) {
10721                     Roo.log("Invalid data from server..");
10722                     Roo.log(edata);
10723                     return;
10724                 }
10725                 if (!rdata || !rdata.success) {
10726                     Roo.log(rdata);
10727                     Roo.MessageBox.alert(Roo.encode(rdata));
10728                     return;
10729                 }
10730                 var data = rdata.data;
10731                 
10732                 if (this.uploadComplete) {
10733                    Roo.MessageBox.hide();
10734                    return;
10735                 }
10736                    
10737                 if (data){
10738                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10739                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10740                     );
10741                 }
10742                 this.uploadProgress.defer(2000,this);
10743             },
10744        
10745             failure: function(data) {
10746                 Roo.log('progress url failed ');
10747                 Roo.log(data);
10748             },
10749             scope : this
10750         });
10751            
10752     },
10753     
10754     
10755     run : function()
10756     {
10757         // run get Values on the form, so it syncs any secondary forms.
10758         this.form.getValues();
10759         
10760         var o = this.options;
10761         var method = this.getMethod();
10762         var isPost = method == 'POST';
10763         if(o.clientValidation === false || this.form.isValid()){
10764             
10765             if (this.form.progressUrl) {
10766                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10767                     (new Date() * 1) + '' + Math.random());
10768                     
10769             } 
10770             
10771             
10772             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10773                 form:this.form.el.dom,
10774                 url:this.getUrl(!isPost),
10775                 method: method,
10776                 params:isPost ? this.getParams() : null,
10777                 isUpload: this.form.fileUpload,
10778                 formData : this.form.formData
10779             }));
10780             
10781             this.uploadProgress();
10782
10783         }else if (o.clientValidation !== false){ // client validation failed
10784             this.failureType = Roo.form.Action.CLIENT_INVALID;
10785             this.form.afterAction(this, false);
10786         }
10787     },
10788
10789     success : function(response)
10790     {
10791         this.uploadComplete= true;
10792         if (this.haveProgress) {
10793             Roo.MessageBox.hide();
10794         }
10795         
10796         
10797         var result = this.processResponse(response);
10798         if(result === true || result.success){
10799             this.form.afterAction(this, true);
10800             return;
10801         }
10802         if(result.errors){
10803             this.form.markInvalid(result.errors);
10804             this.failureType = Roo.form.Action.SERVER_INVALID;
10805         }
10806         this.form.afterAction(this, false);
10807     },
10808     failure : function(response)
10809     {
10810         this.uploadComplete= true;
10811         if (this.haveProgress) {
10812             Roo.MessageBox.hide();
10813         }
10814         
10815         this.response = response;
10816         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10817         this.form.afterAction(this, false);
10818     },
10819     
10820     handleResponse : function(response){
10821         if(this.form.errorReader){
10822             var rs = this.form.errorReader.read(response);
10823             var errors = [];
10824             if(rs.records){
10825                 for(var i = 0, len = rs.records.length; i < len; i++) {
10826                     var r = rs.records[i];
10827                     errors[i] = r.data;
10828                 }
10829             }
10830             if(errors.length < 1){
10831                 errors = null;
10832             }
10833             return {
10834                 success : rs.success,
10835                 errors : errors
10836             };
10837         }
10838         var ret = false;
10839         try {
10840             ret = Roo.decode(response.responseText);
10841         } catch (e) {
10842             ret = {
10843                 success: false,
10844                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10845                 errors : []
10846             };
10847         }
10848         return ret;
10849         
10850     }
10851 });
10852
10853
10854 Roo.form.Action.Load = function(form, options){
10855     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10856     this.reader = this.form.reader;
10857 };
10858
10859 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10860     type : 'load',
10861
10862     run : function(){
10863         
10864         Roo.Ajax.request(Roo.apply(
10865                 this.createCallback(), {
10866                     method:this.getMethod(),
10867                     url:this.getUrl(false),
10868                     params:this.getParams()
10869         }));
10870     },
10871
10872     success : function(response){
10873         
10874         var result = this.processResponse(response);
10875         if(result === true || !result.success || !result.data){
10876             this.failureType = Roo.form.Action.LOAD_FAILURE;
10877             this.form.afterAction(this, false);
10878             return;
10879         }
10880         this.form.clearInvalid();
10881         this.form.setValues(result.data);
10882         this.form.afterAction(this, true);
10883     },
10884
10885     handleResponse : function(response){
10886         if(this.form.reader){
10887             var rs = this.form.reader.read(response);
10888             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10889             return {
10890                 success : rs.success,
10891                 data : data
10892             };
10893         }
10894         return Roo.decode(response.responseText);
10895     }
10896 });
10897
10898 Roo.form.Action.ACTION_TYPES = {
10899     'load' : Roo.form.Action.Load,
10900     'submit' : Roo.form.Action.Submit
10901 };/*
10902  * - LGPL
10903  *
10904  * form
10905  *
10906  */
10907
10908 /**
10909  * @class Roo.bootstrap.Form
10910  * @extends Roo.bootstrap.Component
10911  * Bootstrap Form class
10912  * @cfg {String} method  GET | POST (default POST)
10913  * @cfg {String} labelAlign top | left (default top)
10914  * @cfg {String} align left  | right - for navbars
10915  * @cfg {Boolean} loadMask load mask when submit (default true)
10916
10917  *
10918  * @constructor
10919  * Create a new Form
10920  * @param {Object} config The config object
10921  */
10922
10923
10924 Roo.bootstrap.Form = function(config){
10925     
10926     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10927     
10928     Roo.bootstrap.Form.popover.apply();
10929     
10930     this.addEvents({
10931         /**
10932          * @event clientvalidation
10933          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10934          * @param {Form} this
10935          * @param {Boolean} valid true if the form has passed client-side validation
10936          */
10937         clientvalidation: true,
10938         /**
10939          * @event beforeaction
10940          * Fires before any action is performed. Return false to cancel the action.
10941          * @param {Form} this
10942          * @param {Action} action The action to be performed
10943          */
10944         beforeaction: true,
10945         /**
10946          * @event actionfailed
10947          * Fires when an action fails.
10948          * @param {Form} this
10949          * @param {Action} action The action that failed
10950          */
10951         actionfailed : true,
10952         /**
10953          * @event actioncomplete
10954          * Fires when an action is completed.
10955          * @param {Form} this
10956          * @param {Action} action The action that completed
10957          */
10958         actioncomplete : true
10959     });
10960 };
10961
10962 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10963
10964      /**
10965      * @cfg {String} method
10966      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10967      */
10968     method : 'POST',
10969     /**
10970      * @cfg {String} url
10971      * The URL to use for form actions if one isn't supplied in the action options.
10972      */
10973     /**
10974      * @cfg {Boolean} fileUpload
10975      * Set to true if this form is a file upload.
10976      */
10977
10978     /**
10979      * @cfg {Object} baseParams
10980      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10981      */
10982
10983     /**
10984      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10985      */
10986     timeout: 30,
10987     /**
10988      * @cfg {Sting} align (left|right) for navbar forms
10989      */
10990     align : 'left',
10991
10992     // private
10993     activeAction : null,
10994
10995     /**
10996      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10997      * element by passing it or its id or mask the form itself by passing in true.
10998      * @type Mixed
10999      */
11000     waitMsgTarget : false,
11001
11002     loadMask : true,
11003     
11004     /**
11005      * @cfg {Boolean} errorMask (true|false) default false
11006      */
11007     errorMask : false,
11008     
11009     /**
11010      * @cfg {Number} maskOffset Default 100
11011      */
11012     maskOffset : 100,
11013     
11014     /**
11015      * @cfg {Boolean} maskBody
11016      */
11017     maskBody : false,
11018
11019     getAutoCreate : function(){
11020
11021         var cfg = {
11022             tag: 'form',
11023             method : this.method || 'POST',
11024             id : this.id || Roo.id(),
11025             cls : ''
11026         };
11027         if (this.parent().xtype.match(/^Nav/)) {
11028             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11029
11030         }
11031
11032         if (this.labelAlign == 'left' ) {
11033             cfg.cls += ' form-horizontal';
11034         }
11035
11036
11037         return cfg;
11038     },
11039     initEvents : function()
11040     {
11041         this.el.on('submit', this.onSubmit, this);
11042         // this was added as random key presses on the form where triggering form submit.
11043         this.el.on('keypress', function(e) {
11044             if (e.getCharCode() != 13) {
11045                 return true;
11046             }
11047             // we might need to allow it for textareas.. and some other items.
11048             // check e.getTarget().
11049
11050             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11051                 return true;
11052             }
11053
11054             Roo.log("keypress blocked");
11055
11056             e.preventDefault();
11057             return false;
11058         });
11059         
11060     },
11061     // private
11062     onSubmit : function(e){
11063         e.stopEvent();
11064     },
11065
11066      /**
11067      * Returns true if client-side validation on the form is successful.
11068      * @return Boolean
11069      */
11070     isValid : function(){
11071         var items = this.getItems();
11072         var valid = true;
11073         var target = false;
11074         
11075         items.each(function(f){
11076             
11077             if(f.validate()){
11078                 return;
11079             }
11080             
11081             Roo.log('invalid field: ' + f.name);
11082             
11083             valid = false;
11084
11085             if(!target && f.el.isVisible(true)){
11086                 target = f;
11087             }
11088            
11089         });
11090         
11091         if(this.errorMask && !valid){
11092             Roo.bootstrap.Form.popover.mask(this, target);
11093         }
11094         
11095         return valid;
11096     },
11097     
11098     /**
11099      * Returns true if any fields in this form have changed since their original load.
11100      * @return Boolean
11101      */
11102     isDirty : function(){
11103         var dirty = false;
11104         var items = this.getItems();
11105         items.each(function(f){
11106            if(f.isDirty()){
11107                dirty = true;
11108                return false;
11109            }
11110            return true;
11111         });
11112         return dirty;
11113     },
11114      /**
11115      * Performs a predefined action (submit or load) or custom actions you define on this form.
11116      * @param {String} actionName The name of the action type
11117      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11118      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11119      * accept other config options):
11120      * <pre>
11121 Property          Type             Description
11122 ----------------  ---------------  ----------------------------------------------------------------------------------
11123 url               String           The url for the action (defaults to the form's url)
11124 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11125 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11126 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11127                                    validate the form on the client (defaults to false)
11128      * </pre>
11129      * @return {BasicForm} this
11130      */
11131     doAction : function(action, options){
11132         if(typeof action == 'string'){
11133             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11134         }
11135         if(this.fireEvent('beforeaction', this, action) !== false){
11136             this.beforeAction(action);
11137             action.run.defer(100, action);
11138         }
11139         return this;
11140     },
11141
11142     // private
11143     beforeAction : function(action){
11144         var o = action.options;
11145         
11146         if(this.loadMask){
11147             
11148             if(this.maskBody){
11149                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11150             } else {
11151                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152             }
11153         }
11154         // not really supported yet.. ??
11155
11156         //if(this.waitMsgTarget === true){
11157         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11158         //}else if(this.waitMsgTarget){
11159         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11160         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11161         //}else {
11162         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11163        // }
11164
11165     },
11166
11167     // private
11168     afterAction : function(action, success){
11169         this.activeAction = null;
11170         var o = action.options;
11171
11172         if(this.loadMask){
11173             
11174             if(this.maskBody){
11175                 Roo.get(document.body).unmask();
11176             } else {
11177                 this.el.unmask();
11178             }
11179         }
11180         
11181         //if(this.waitMsgTarget === true){
11182 //            this.el.unmask();
11183         //}else if(this.waitMsgTarget){
11184         //    this.waitMsgTarget.unmask();
11185         //}else{
11186         //    Roo.MessageBox.updateProgress(1);
11187         //    Roo.MessageBox.hide();
11188        // }
11189         //
11190         if(success){
11191             if(o.reset){
11192                 this.reset();
11193             }
11194             Roo.callback(o.success, o.scope, [this, action]);
11195             this.fireEvent('actioncomplete', this, action);
11196
11197         }else{
11198
11199             // failure condition..
11200             // we have a scenario where updates need confirming.
11201             // eg. if a locking scenario exists..
11202             // we look for { errors : { needs_confirm : true }} in the response.
11203             if (
11204                 (typeof(action.result) != 'undefined')  &&
11205                 (typeof(action.result.errors) != 'undefined')  &&
11206                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11207            ){
11208                 var _t = this;
11209                 Roo.log("not supported yet");
11210                  /*
11211
11212                 Roo.MessageBox.confirm(
11213                     "Change requires confirmation",
11214                     action.result.errorMsg,
11215                     function(r) {
11216                         if (r != 'yes') {
11217                             return;
11218                         }
11219                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11220                     }
11221
11222                 );
11223                 */
11224
11225
11226                 return;
11227             }
11228
11229             Roo.callback(o.failure, o.scope, [this, action]);
11230             // show an error message if no failed handler is set..
11231             if (!this.hasListener('actionfailed')) {
11232                 Roo.log("need to add dialog support");
11233                 /*
11234                 Roo.MessageBox.alert("Error",
11235                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11236                         action.result.errorMsg :
11237                         "Saving Failed, please check your entries or try again"
11238                 );
11239                 */
11240             }
11241
11242             this.fireEvent('actionfailed', this, action);
11243         }
11244
11245     },
11246     /**
11247      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11248      * @param {String} id The value to search for
11249      * @return Field
11250      */
11251     findField : function(id){
11252         var items = this.getItems();
11253         var field = items.get(id);
11254         if(!field){
11255              items.each(function(f){
11256                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11257                     field = f;
11258                     return false;
11259                 }
11260                 return true;
11261             });
11262         }
11263         return field || null;
11264     },
11265      /**
11266      * Mark fields in this form invalid in bulk.
11267      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11268      * @return {BasicForm} this
11269      */
11270     markInvalid : function(errors){
11271         if(errors instanceof Array){
11272             for(var i = 0, len = errors.length; i < len; i++){
11273                 var fieldError = errors[i];
11274                 var f = this.findField(fieldError.id);
11275                 if(f){
11276                     f.markInvalid(fieldError.msg);
11277                 }
11278             }
11279         }else{
11280             var field, id;
11281             for(id in errors){
11282                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11283                     field.markInvalid(errors[id]);
11284                 }
11285             }
11286         }
11287         //Roo.each(this.childForms || [], function (f) {
11288         //    f.markInvalid(errors);
11289         //});
11290
11291         return this;
11292     },
11293
11294     /**
11295      * Set values for fields in this form in bulk.
11296      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11297      * @return {BasicForm} this
11298      */
11299     setValues : function(values){
11300         if(values instanceof Array){ // array of objects
11301             for(var i = 0, len = values.length; i < len; i++){
11302                 var v = values[i];
11303                 var f = this.findField(v.id);
11304                 if(f){
11305                     f.setValue(v.value);
11306                     if(this.trackResetOnLoad){
11307                         f.originalValue = f.getValue();
11308                     }
11309                 }
11310             }
11311         }else{ // object hash
11312             var field, id;
11313             for(id in values){
11314                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11315
11316                     if (field.setFromData &&
11317                         field.valueField &&
11318                         field.displayField &&
11319                         // combos' with local stores can
11320                         // be queried via setValue()
11321                         // to set their value..
11322                         (field.store && !field.store.isLocal)
11323                         ) {
11324                         // it's a combo
11325                         var sd = { };
11326                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11327                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11328                         field.setFromData(sd);
11329
11330                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11331                         
11332                         field.setFromData(values);
11333                         
11334                     } else {
11335                         field.setValue(values[id]);
11336                     }
11337
11338
11339                     if(this.trackResetOnLoad){
11340                         field.originalValue = field.getValue();
11341                     }
11342                 }
11343             }
11344         }
11345
11346         //Roo.each(this.childForms || [], function (f) {
11347         //    f.setValues(values);
11348         //});
11349
11350         return this;
11351     },
11352
11353     /**
11354      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11355      * they are returned as an array.
11356      * @param {Boolean} asString
11357      * @return {Object}
11358      */
11359     getValues : function(asString){
11360         //if (this.childForms) {
11361             // copy values from the child forms
11362         //    Roo.each(this.childForms, function (f) {
11363         //        this.setValues(f.getValues());
11364         //    }, this);
11365         //}
11366
11367
11368
11369         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11370         if(asString === true){
11371             return fs;
11372         }
11373         return Roo.urlDecode(fs);
11374     },
11375
11376     /**
11377      * Returns the fields in this form as an object with key/value pairs.
11378      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11379      * @return {Object}
11380      */
11381     getFieldValues : function(with_hidden)
11382     {
11383         var items = this.getItems();
11384         var ret = {};
11385         items.each(function(f){
11386             
11387             if (!f.getName()) {
11388                 return;
11389             }
11390             
11391             var v = f.getValue();
11392             
11393             if (f.inputType =='radio') {
11394                 if (typeof(ret[f.getName()]) == 'undefined') {
11395                     ret[f.getName()] = ''; // empty..
11396                 }
11397
11398                 if (!f.el.dom.checked) {
11399                     return;
11400
11401                 }
11402                 v = f.el.dom.value;
11403
11404             }
11405             
11406             if(f.xtype == 'MoneyField'){
11407                 ret[f.currencyName] = f.getCurrency();
11408             }
11409
11410             // not sure if this supported any more..
11411             if ((typeof(v) == 'object') && f.getRawValue) {
11412                 v = f.getRawValue() ; // dates..
11413             }
11414             // combo boxes where name != hiddenName...
11415             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11416                 ret[f.name] = f.getRawValue();
11417             }
11418             ret[f.getName()] = v;
11419         });
11420
11421         return ret;
11422     },
11423
11424     /**
11425      * Clears all invalid messages in this form.
11426      * @return {BasicForm} this
11427      */
11428     clearInvalid : function(){
11429         var items = this.getItems();
11430
11431         items.each(function(f){
11432            f.clearInvalid();
11433         });
11434
11435         return this;
11436     },
11437
11438     /**
11439      * Resets this form.
11440      * @return {BasicForm} this
11441      */
11442     reset : function(){
11443         var items = this.getItems();
11444         items.each(function(f){
11445             f.reset();
11446         });
11447
11448         Roo.each(this.childForms || [], function (f) {
11449             f.reset();
11450         });
11451
11452
11453         return this;
11454     },
11455     
11456     getItems : function()
11457     {
11458         var r=new Roo.util.MixedCollection(false, function(o){
11459             return o.id || (o.id = Roo.id());
11460         });
11461         var iter = function(el) {
11462             if (el.inputEl) {
11463                 r.add(el);
11464             }
11465             if (!el.items) {
11466                 return;
11467             }
11468             Roo.each(el.items,function(e) {
11469                 iter(e);
11470             });
11471         };
11472
11473         iter(this);
11474         return r;
11475     },
11476     
11477     hideFields : function(items)
11478     {
11479         Roo.each(items, function(i){
11480             
11481             var f = this.findField(i);
11482             
11483             if(!f){
11484                 return;
11485             }
11486             
11487             f.hide();
11488             
11489         }, this);
11490     },
11491     
11492     showFields : function(items)
11493     {
11494         Roo.each(items, function(i){
11495             
11496             var f = this.findField(i);
11497             
11498             if(!f){
11499                 return;
11500             }
11501             
11502             f.show();
11503             
11504         }, this);
11505     }
11506
11507 });
11508
11509 Roo.apply(Roo.bootstrap.Form, {
11510     
11511     popover : {
11512         
11513         padding : 5,
11514         
11515         isApplied : false,
11516         
11517         isMasked : false,
11518         
11519         form : false,
11520         
11521         target : false,
11522         
11523         toolTip : false,
11524         
11525         intervalID : false,
11526         
11527         maskEl : false,
11528         
11529         apply : function()
11530         {
11531             if(this.isApplied){
11532                 return;
11533             }
11534             
11535             this.maskEl = {
11536                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11537                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11538                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11539                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11540             };
11541             
11542             this.maskEl.top.enableDisplayMode("block");
11543             this.maskEl.left.enableDisplayMode("block");
11544             this.maskEl.bottom.enableDisplayMode("block");
11545             this.maskEl.right.enableDisplayMode("block");
11546             
11547             this.toolTip = new Roo.bootstrap.Tooltip({
11548                 cls : 'roo-form-error-popover',
11549                 alignment : {
11550                     'left' : ['r-l', [-2,0], 'right'],
11551                     'right' : ['l-r', [2,0], 'left'],
11552                     'bottom' : ['tl-bl', [0,2], 'top'],
11553                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11554                 }
11555             });
11556             
11557             this.toolTip.render(Roo.get(document.body));
11558
11559             this.toolTip.el.enableDisplayMode("block");
11560             
11561             Roo.get(document.body).on('click', function(){
11562                 this.unmask();
11563             }, this);
11564             
11565             Roo.get(document.body).on('touchstart', function(){
11566                 this.unmask();
11567             }, this);
11568             
11569             this.isApplied = true
11570         },
11571         
11572         mask : function(form, target)
11573         {
11574             this.form = form;
11575             
11576             this.target = target;
11577             
11578             if(!this.form.errorMask || !target.el){
11579                 return;
11580             }
11581             
11582             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11583             
11584             Roo.log(scrollable);
11585             
11586             var ot = this.target.el.calcOffsetsTo(scrollable);
11587             
11588             var scrollTo = ot[1] - this.form.maskOffset;
11589             
11590             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11591             
11592             scrollable.scrollTo('top', scrollTo);
11593             
11594             var box = this.target.el.getBox();
11595             Roo.log(box);
11596             var zIndex = Roo.bootstrap.Modal.zIndex++;
11597
11598             
11599             this.maskEl.top.setStyle('position', 'absolute');
11600             this.maskEl.top.setStyle('z-index', zIndex);
11601             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11602             this.maskEl.top.setLeft(0);
11603             this.maskEl.top.setTop(0);
11604             this.maskEl.top.show();
11605             
11606             this.maskEl.left.setStyle('position', 'absolute');
11607             this.maskEl.left.setStyle('z-index', zIndex);
11608             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11609             this.maskEl.left.setLeft(0);
11610             this.maskEl.left.setTop(box.y - this.padding);
11611             this.maskEl.left.show();
11612
11613             this.maskEl.bottom.setStyle('position', 'absolute');
11614             this.maskEl.bottom.setStyle('z-index', zIndex);
11615             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11616             this.maskEl.bottom.setLeft(0);
11617             this.maskEl.bottom.setTop(box.bottom + this.padding);
11618             this.maskEl.bottom.show();
11619
11620             this.maskEl.right.setStyle('position', 'absolute');
11621             this.maskEl.right.setStyle('z-index', zIndex);
11622             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11623             this.maskEl.right.setLeft(box.right + this.padding);
11624             this.maskEl.right.setTop(box.y - this.padding);
11625             this.maskEl.right.show();
11626
11627             this.toolTip.bindEl = this.target.el;
11628
11629             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11630
11631             var tip = this.target.blankText;
11632
11633             if(this.target.getValue() !== '' ) {
11634                 
11635                 if (this.target.invalidText.length) {
11636                     tip = this.target.invalidText;
11637                 } else if (this.target.regexText.length){
11638                     tip = this.target.regexText;
11639                 }
11640             }
11641
11642             this.toolTip.show(tip);
11643
11644             this.intervalID = window.setInterval(function() {
11645                 Roo.bootstrap.Form.popover.unmask();
11646             }, 10000);
11647
11648             window.onwheel = function(){ return false;};
11649             
11650             (function(){ this.isMasked = true; }).defer(500, this);
11651             
11652         },
11653         
11654         unmask : function()
11655         {
11656             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11657                 return;
11658             }
11659             
11660             this.maskEl.top.setStyle('position', 'absolute');
11661             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11662             this.maskEl.top.hide();
11663
11664             this.maskEl.left.setStyle('position', 'absolute');
11665             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11666             this.maskEl.left.hide();
11667
11668             this.maskEl.bottom.setStyle('position', 'absolute');
11669             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11670             this.maskEl.bottom.hide();
11671
11672             this.maskEl.right.setStyle('position', 'absolute');
11673             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11674             this.maskEl.right.hide();
11675             
11676             this.toolTip.hide();
11677             
11678             this.toolTip.el.hide();
11679             
11680             window.onwheel = function(){ return true;};
11681             
11682             if(this.intervalID){
11683                 window.clearInterval(this.intervalID);
11684                 this.intervalID = false;
11685             }
11686             
11687             this.isMasked = false;
11688             
11689         }
11690         
11691     }
11692     
11693 });
11694
11695 /*
11696  * Based on:
11697  * Ext JS Library 1.1.1
11698  * Copyright(c) 2006-2007, Ext JS, LLC.
11699  *
11700  * Originally Released Under LGPL - original licence link has changed is not relivant.
11701  *
11702  * Fork - LGPL
11703  * <script type="text/javascript">
11704  */
11705 /**
11706  * @class Roo.form.VTypes
11707  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11708  * @singleton
11709  */
11710 Roo.form.VTypes = function(){
11711     // closure these in so they are only created once.
11712     var alpha = /^[a-zA-Z_]+$/;
11713     var alphanum = /^[a-zA-Z0-9_]+$/;
11714     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11715     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11716
11717     // All these messages and functions are configurable
11718     return {
11719         /**
11720          * The function used to validate email addresses
11721          * @param {String} value The email address
11722          */
11723         'email' : function(v){
11724             return email.test(v);
11725         },
11726         /**
11727          * The error text to display when the email validation function returns false
11728          * @type String
11729          */
11730         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11731         /**
11732          * The keystroke filter mask to be applied on email input
11733          * @type RegExp
11734          */
11735         'emailMask' : /[a-z0-9_\.\-@]/i,
11736
11737         /**
11738          * The function used to validate URLs
11739          * @param {String} value The URL
11740          */
11741         'url' : function(v){
11742             return url.test(v);
11743         },
11744         /**
11745          * The error text to display when the url validation function returns false
11746          * @type String
11747          */
11748         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11749         
11750         /**
11751          * The function used to validate alpha values
11752          * @param {String} value The value
11753          */
11754         'alpha' : function(v){
11755             return alpha.test(v);
11756         },
11757         /**
11758          * The error text to display when the alpha validation function returns false
11759          * @type String
11760          */
11761         'alphaText' : 'This field should only contain letters and _',
11762         /**
11763          * The keystroke filter mask to be applied on alpha input
11764          * @type RegExp
11765          */
11766         'alphaMask' : /[a-z_]/i,
11767
11768         /**
11769          * The function used to validate alphanumeric values
11770          * @param {String} value The value
11771          */
11772         'alphanum' : function(v){
11773             return alphanum.test(v);
11774         },
11775         /**
11776          * The error text to display when the alphanumeric validation function returns false
11777          * @type String
11778          */
11779         'alphanumText' : 'This field should only contain letters, numbers and _',
11780         /**
11781          * The keystroke filter mask to be applied on alphanumeric input
11782          * @type RegExp
11783          */
11784         'alphanumMask' : /[a-z0-9_]/i
11785     };
11786 }();/*
11787  * - LGPL
11788  *
11789  * Input
11790  * 
11791  */
11792
11793 /**
11794  * @class Roo.bootstrap.Input
11795  * @extends Roo.bootstrap.Component
11796  * Bootstrap Input class
11797  * @cfg {Boolean} disabled is it disabled
11798  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11799  * @cfg {String} name name of the input
11800  * @cfg {string} fieldLabel - the label associated
11801  * @cfg {string} placeholder - placeholder to put in text.
11802  * @cfg {string}  before - input group add on before
11803  * @cfg {string} after - input group add on after
11804  * @cfg {string} size - (lg|sm) or leave empty..
11805  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11806  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11807  * @cfg {Number} md colspan out of 12 for computer-sized screens
11808  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11809  * @cfg {string} value default value of the input
11810  * @cfg {Number} labelWidth set the width of label 
11811  * @cfg {Number} labellg set the width of label (1-12)
11812  * @cfg {Number} labelmd set the width of label (1-12)
11813  * @cfg {Number} labelsm set the width of label (1-12)
11814  * @cfg {Number} labelxs set the width of label (1-12)
11815  * @cfg {String} labelAlign (top|left)
11816  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11817  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11818  * @cfg {String} indicatorpos (left|right) default left
11819  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11820  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11821  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11822
11823  * @cfg {String} align (left|center|right) Default left
11824  * @cfg {Boolean} forceFeedback (true|false) Default false
11825  * 
11826  * @constructor
11827  * Create a new Input
11828  * @param {Object} config The config object
11829  */
11830
11831 Roo.bootstrap.Input = function(config){
11832     
11833     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11834     
11835     this.addEvents({
11836         /**
11837          * @event focus
11838          * Fires when this field receives input focus.
11839          * @param {Roo.form.Field} this
11840          */
11841         focus : true,
11842         /**
11843          * @event blur
11844          * Fires when this field loses input focus.
11845          * @param {Roo.form.Field} this
11846          */
11847         blur : true,
11848         /**
11849          * @event specialkey
11850          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11851          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11852          * @param {Roo.form.Field} this
11853          * @param {Roo.EventObject} e The event object
11854          */
11855         specialkey : true,
11856         /**
11857          * @event change
11858          * Fires just before the field blurs if the field value has changed.
11859          * @param {Roo.form.Field} this
11860          * @param {Mixed} newValue The new value
11861          * @param {Mixed} oldValue The original value
11862          */
11863         change : true,
11864         /**
11865          * @event invalid
11866          * Fires after the field has been marked as invalid.
11867          * @param {Roo.form.Field} this
11868          * @param {String} msg The validation message
11869          */
11870         invalid : true,
11871         /**
11872          * @event valid
11873          * Fires after the field has been validated with no errors.
11874          * @param {Roo.form.Field} this
11875          */
11876         valid : true,
11877          /**
11878          * @event keyup
11879          * Fires after the key up
11880          * @param {Roo.form.Field} this
11881          * @param {Roo.EventObject}  e The event Object
11882          */
11883         keyup : true,
11884         /**
11885          * @event paste
11886          * Fires after the user pastes into input
11887          * @param {Roo.form.Field} this
11888          * @param {Roo.EventObject}  e The event Object
11889          */
11890         paste : true
11891     });
11892 };
11893
11894 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11895      /**
11896      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11897       automatic validation (defaults to "keyup").
11898      */
11899     validationEvent : "keyup",
11900      /**
11901      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11902      */
11903     validateOnBlur : true,
11904     /**
11905      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11906      */
11907     validationDelay : 250,
11908      /**
11909      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11910      */
11911     focusClass : "x-form-focus",  // not needed???
11912     
11913        
11914     /**
11915      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11916      */
11917     invalidClass : "has-warning",
11918     
11919     /**
11920      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11921      */
11922     validClass : "has-success",
11923     
11924     /**
11925      * @cfg {Boolean} hasFeedback (true|false) default true
11926      */
11927     hasFeedback : true,
11928     
11929     /**
11930      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11931      */
11932     invalidFeedbackClass : "glyphicon-warning-sign",
11933     
11934     /**
11935      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11936      */
11937     validFeedbackClass : "glyphicon-ok",
11938     
11939     /**
11940      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11941      */
11942     selectOnFocus : false,
11943     
11944      /**
11945      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11946      */
11947     maskRe : null,
11948        /**
11949      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11950      */
11951     vtype : null,
11952     
11953       /**
11954      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11955      */
11956     disableKeyFilter : false,
11957     
11958        /**
11959      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11960      */
11961     disabled : false,
11962      /**
11963      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11964      */
11965     allowBlank : true,
11966     /**
11967      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11968      */
11969     blankText : "Please complete this mandatory field",
11970     
11971      /**
11972      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11973      */
11974     minLength : 0,
11975     /**
11976      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11977      */
11978     maxLength : Number.MAX_VALUE,
11979     /**
11980      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11981      */
11982     minLengthText : "The minimum length for this field is {0}",
11983     /**
11984      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11985      */
11986     maxLengthText : "The maximum length for this field is {0}",
11987   
11988     
11989     /**
11990      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11991      * If available, this function will be called only after the basic validators all return true, and will be passed the
11992      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11993      */
11994     validator : null,
11995     /**
11996      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11997      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11998      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11999      */
12000     regex : null,
12001     /**
12002      * @cfg {String} regexText -- Depricated - use Invalid Text
12003      */
12004     regexText : "",
12005     
12006     /**
12007      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12008      */
12009     invalidText : "",
12010     
12011     
12012     
12013     autocomplete: false,
12014     
12015     
12016     fieldLabel : '',
12017     inputType : 'text',
12018     
12019     name : false,
12020     placeholder: false,
12021     before : false,
12022     after : false,
12023     size : false,
12024     hasFocus : false,
12025     preventMark: false,
12026     isFormField : true,
12027     value : '',
12028     labelWidth : 2,
12029     labelAlign : false,
12030     readOnly : false,
12031     align : false,
12032     formatedValue : false,
12033     forceFeedback : false,
12034     
12035     indicatorpos : 'left',
12036     
12037     labellg : 0,
12038     labelmd : 0,
12039     labelsm : 0,
12040     labelxs : 0,
12041     
12042     capture : '',
12043     accept : '',
12044     
12045     parentLabelAlign : function()
12046     {
12047         var parent = this;
12048         while (parent.parent()) {
12049             parent = parent.parent();
12050             if (typeof(parent.labelAlign) !='undefined') {
12051                 return parent.labelAlign;
12052             }
12053         }
12054         return 'left';
12055         
12056     },
12057     
12058     getAutoCreate : function()
12059     {
12060         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12061         
12062         var id = Roo.id();
12063         
12064         var cfg = {};
12065         
12066         if(this.inputType != 'hidden'){
12067             cfg.cls = 'form-group' //input-group
12068         }
12069         
12070         var input =  {
12071             tag: 'input',
12072             id : id,
12073             type : this.inputType,
12074             value : this.value,
12075             cls : 'form-control',
12076             placeholder : this.placeholder || '',
12077             autocomplete : this.autocomplete || 'new-password'
12078         };
12079         if (this.inputType == 'file') {
12080             input.style = 'overflow:hidden'; // why not in CSS?
12081         }
12082         
12083         if(this.capture.length){
12084             input.capture = this.capture;
12085         }
12086         
12087         if(this.accept.length){
12088             input.accept = this.accept + "/*";
12089         }
12090         
12091         if(this.align){
12092             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12093         }
12094         
12095         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12096             input.maxLength = this.maxLength;
12097         }
12098         
12099         if (this.disabled) {
12100             input.disabled=true;
12101         }
12102         
12103         if (this.readOnly) {
12104             input.readonly=true;
12105         }
12106         
12107         if (this.name) {
12108             input.name = this.name;
12109         }
12110         
12111         if (this.size) {
12112             input.cls += ' input-' + this.size;
12113         }
12114         
12115         var settings=this;
12116         ['xs','sm','md','lg'].map(function(size){
12117             if (settings[size]) {
12118                 cfg.cls += ' col-' + size + '-' + settings[size];
12119             }
12120         });
12121         
12122         var inputblock = input;
12123         
12124         var feedback = {
12125             tag: 'span',
12126             cls: 'glyphicon form-control-feedback'
12127         };
12128             
12129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12130             
12131             inputblock = {
12132                 cls : 'has-feedback',
12133                 cn :  [
12134                     input,
12135                     feedback
12136                 ] 
12137             };  
12138         }
12139         
12140         if (this.before || this.after) {
12141             
12142             inputblock = {
12143                 cls : 'input-group',
12144                 cn :  [] 
12145             };
12146             
12147             if (this.before && typeof(this.before) == 'string') {
12148                 
12149                 inputblock.cn.push({
12150                     tag :'span',
12151                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12152                     html : this.before
12153                 });
12154             }
12155             if (this.before && typeof(this.before) == 'object') {
12156                 this.before = Roo.factory(this.before);
12157                 
12158                 inputblock.cn.push({
12159                     tag :'span',
12160                     cls : 'roo-input-before input-group-prepend   input-group-' +
12161                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12162                 });
12163             }
12164             
12165             inputblock.cn.push(input);
12166             
12167             if (this.after && typeof(this.after) == 'string') {
12168                 inputblock.cn.push({
12169                     tag :'span',
12170                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12171                     html : this.after
12172                 });
12173             }
12174             if (this.after && typeof(this.after) == 'object') {
12175                 this.after = Roo.factory(this.after);
12176                 
12177                 inputblock.cn.push({
12178                     tag :'span',
12179                     cls : 'roo-input-after input-group-append  input-group-' +
12180                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12181                 });
12182             }
12183             
12184             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12185                 inputblock.cls += ' has-feedback';
12186                 inputblock.cn.push(feedback);
12187             }
12188         };
12189         var indicator = {
12190             tag : 'i',
12191             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12192             tooltip : 'This field is required'
12193         };
12194         if (this.allowBlank ) {
12195             indicator.style = this.allowBlank ? ' display:none' : '';
12196         }
12197         if (align ==='left' && this.fieldLabel.length) {
12198             
12199             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12200             
12201             cfg.cn = [
12202                 indicator,
12203                 {
12204                     tag: 'label',
12205                     'for' :  id,
12206                     cls : 'control-label col-form-label',
12207                     html : this.fieldLabel
12208
12209                 },
12210                 {
12211                     cls : "", 
12212                     cn: [
12213                         inputblock
12214                     ]
12215                 }
12216             ];
12217             
12218             var labelCfg = cfg.cn[1];
12219             var contentCfg = cfg.cn[2];
12220             
12221             if(this.indicatorpos == 'right'){
12222                 cfg.cn = [
12223                     {
12224                         tag: 'label',
12225                         'for' :  id,
12226                         cls : 'control-label col-form-label',
12227                         cn : [
12228                             {
12229                                 tag : 'span',
12230                                 html : this.fieldLabel
12231                             },
12232                             indicator
12233                         ]
12234                     },
12235                     {
12236                         cls : "",
12237                         cn: [
12238                             inputblock
12239                         ]
12240                     }
12241
12242                 ];
12243                 
12244                 labelCfg = cfg.cn[0];
12245                 contentCfg = cfg.cn[1];
12246             
12247             }
12248             
12249             if(this.labelWidth > 12){
12250                 labelCfg.style = "width: " + this.labelWidth + 'px';
12251             }
12252             
12253             if(this.labelWidth < 13 && this.labelmd == 0){
12254                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12255             }
12256             
12257             if(this.labellg > 0){
12258                 labelCfg.cls += ' col-lg-' + this.labellg;
12259                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12260             }
12261             
12262             if(this.labelmd > 0){
12263                 labelCfg.cls += ' col-md-' + this.labelmd;
12264                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12265             }
12266             
12267             if(this.labelsm > 0){
12268                 labelCfg.cls += ' col-sm-' + this.labelsm;
12269                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12270             }
12271             
12272             if(this.labelxs > 0){
12273                 labelCfg.cls += ' col-xs-' + this.labelxs;
12274                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12275             }
12276             
12277             
12278         } else if ( this.fieldLabel.length) {
12279                 
12280             
12281             
12282             cfg.cn = [
12283                 {
12284                     tag : 'i',
12285                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12286                     tooltip : 'This field is required',
12287                     style : this.allowBlank ? ' display:none' : '' 
12288                 },
12289                 {
12290                     tag: 'label',
12291                    //cls : 'input-group-addon',
12292                     html : this.fieldLabel
12293
12294                 },
12295
12296                inputblock
12297
12298            ];
12299            
12300            if(this.indicatorpos == 'right'){
12301        
12302                 cfg.cn = [
12303                     {
12304                         tag: 'label',
12305                        //cls : 'input-group-addon',
12306                         html : this.fieldLabel
12307
12308                     },
12309                     {
12310                         tag : 'i',
12311                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12312                         tooltip : 'This field is required',
12313                         style : this.allowBlank ? ' display:none' : '' 
12314                     },
12315
12316                    inputblock
12317
12318                ];
12319
12320             }
12321
12322         } else {
12323             
12324             cfg.cn = [
12325
12326                     inputblock
12327
12328             ];
12329                 
12330                 
12331         };
12332         
12333         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12334            cfg.cls += ' navbar-form';
12335         }
12336         
12337         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12338             // on BS4 we do this only if not form 
12339             cfg.cls += ' navbar-form';
12340             cfg.tag = 'li';
12341         }
12342         
12343         return cfg;
12344         
12345     },
12346     /**
12347      * return the real input element.
12348      */
12349     inputEl: function ()
12350     {
12351         return this.el.select('input.form-control',true).first();
12352     },
12353     
12354     tooltipEl : function()
12355     {
12356         return this.inputEl();
12357     },
12358     
12359     indicatorEl : function()
12360     {
12361         if (Roo.bootstrap.version == 4) {
12362             return false; // not enabled in v4 yet.
12363         }
12364         
12365         var indicator = this.el.select('i.roo-required-indicator',true).first();
12366         
12367         if(!indicator){
12368             return false;
12369         }
12370         
12371         return indicator;
12372         
12373     },
12374     
12375     setDisabled : function(v)
12376     {
12377         var i  = this.inputEl().dom;
12378         if (!v) {
12379             i.removeAttribute('disabled');
12380             return;
12381             
12382         }
12383         i.setAttribute('disabled','true');
12384     },
12385     initEvents : function()
12386     {
12387           
12388         this.inputEl().on("keydown" , this.fireKey,  this);
12389         this.inputEl().on("focus", this.onFocus,  this);
12390         this.inputEl().on("blur", this.onBlur,  this);
12391         
12392         this.inputEl().relayEvent('keyup', this);
12393         this.inputEl().relayEvent('paste', this);
12394         
12395         this.indicator = this.indicatorEl();
12396         
12397         if(this.indicator){
12398             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12399         }
12400  
12401         // reference to original value for reset
12402         this.originalValue = this.getValue();
12403         //Roo.form.TextField.superclass.initEvents.call(this);
12404         if(this.validationEvent == 'keyup'){
12405             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12406             this.inputEl().on('keyup', this.filterValidation, this);
12407         }
12408         else if(this.validationEvent !== false){
12409             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12410         }
12411         
12412         if(this.selectOnFocus){
12413             this.on("focus", this.preFocus, this);
12414             
12415         }
12416         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12417             this.inputEl().on("keypress", this.filterKeys, this);
12418         } else {
12419             this.inputEl().relayEvent('keypress', this);
12420         }
12421        /* if(this.grow){
12422             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12423             this.el.on("click", this.autoSize,  this);
12424         }
12425         */
12426         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12427             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12428         }
12429         
12430         if (typeof(this.before) == 'object') {
12431             this.before.render(this.el.select('.roo-input-before',true).first());
12432         }
12433         if (typeof(this.after) == 'object') {
12434             this.after.render(this.el.select('.roo-input-after',true).first());
12435         }
12436         
12437         this.inputEl().on('change', this.onChange, this);
12438         
12439     },
12440     filterValidation : function(e){
12441         if(!e.isNavKeyPress()){
12442             this.validationTask.delay(this.validationDelay);
12443         }
12444     },
12445      /**
12446      * Validates the field value
12447      * @return {Boolean} True if the value is valid, else false
12448      */
12449     validate : function(){
12450         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12451         if(this.disabled || this.validateValue(this.getRawValue())){
12452             this.markValid();
12453             return true;
12454         }
12455         
12456         this.markInvalid();
12457         return false;
12458     },
12459     
12460     
12461     /**
12462      * Validates a value according to the field's validation rules and marks the field as invalid
12463      * if the validation fails
12464      * @param {Mixed} value The value to validate
12465      * @return {Boolean} True if the value is valid, else false
12466      */
12467     validateValue : function(value)
12468     {
12469         if(this.getVisibilityEl().hasClass('hidden')){
12470             return true;
12471         }
12472         
12473         if(value.length < 1)  { // if it's blank
12474             if(this.allowBlank){
12475                 return true;
12476             }
12477             return false;
12478         }
12479         
12480         if(value.length < this.minLength){
12481             return false;
12482         }
12483         if(value.length > this.maxLength){
12484             return false;
12485         }
12486         if(this.vtype){
12487             var vt = Roo.form.VTypes;
12488             if(!vt[this.vtype](value, this)){
12489                 return false;
12490             }
12491         }
12492         if(typeof this.validator == "function"){
12493             var msg = this.validator(value);
12494             if(msg !== true){
12495                 return false;
12496             }
12497             if (typeof(msg) == 'string') {
12498                 this.invalidText = msg;
12499             }
12500         }
12501         
12502         if(this.regex && !this.regex.test(value)){
12503             return false;
12504         }
12505         
12506         return true;
12507     },
12508     
12509      // private
12510     fireKey : function(e){
12511         //Roo.log('field ' + e.getKey());
12512         if(e.isNavKeyPress()){
12513             this.fireEvent("specialkey", this, e);
12514         }
12515     },
12516     focus : function (selectText){
12517         if(this.rendered){
12518             this.inputEl().focus();
12519             if(selectText === true){
12520                 this.inputEl().dom.select();
12521             }
12522         }
12523         return this;
12524     } ,
12525     
12526     onFocus : function(){
12527         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12528            // this.el.addClass(this.focusClass);
12529         }
12530         if(!this.hasFocus){
12531             this.hasFocus = true;
12532             this.startValue = this.getValue();
12533             this.fireEvent("focus", this);
12534         }
12535     },
12536     
12537     beforeBlur : Roo.emptyFn,
12538
12539     
12540     // private
12541     onBlur : function(){
12542         this.beforeBlur();
12543         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12544             //this.el.removeClass(this.focusClass);
12545         }
12546         this.hasFocus = false;
12547         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12548             this.validate();
12549         }
12550         var v = this.getValue();
12551         if(String(v) !== String(this.startValue)){
12552             this.fireEvent('change', this, v, this.startValue);
12553         }
12554         this.fireEvent("blur", this);
12555     },
12556     
12557     onChange : function(e)
12558     {
12559         var v = this.getValue();
12560         if(String(v) !== String(this.startValue)){
12561             this.fireEvent('change', this, v, this.startValue);
12562         }
12563         
12564     },
12565     
12566     /**
12567      * Resets the current field value to the originally loaded value and clears any validation messages
12568      */
12569     reset : function(){
12570         this.setValue(this.originalValue);
12571         this.validate();
12572     },
12573      /**
12574      * Returns the name of the field
12575      * @return {Mixed} name The name field
12576      */
12577     getName: function(){
12578         return this.name;
12579     },
12580      /**
12581      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12582      * @return {Mixed} value The field value
12583      */
12584     getValue : function(){
12585         
12586         var v = this.inputEl().getValue();
12587         
12588         return v;
12589     },
12590     /**
12591      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12592      * @return {Mixed} value The field value
12593      */
12594     getRawValue : function(){
12595         var v = this.inputEl().getValue();
12596         
12597         return v;
12598     },
12599     
12600     /**
12601      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12602      * @param {Mixed} value The value to set
12603      */
12604     setRawValue : function(v){
12605         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12606     },
12607     
12608     selectText : function(start, end){
12609         var v = this.getRawValue();
12610         if(v.length > 0){
12611             start = start === undefined ? 0 : start;
12612             end = end === undefined ? v.length : end;
12613             var d = this.inputEl().dom;
12614             if(d.setSelectionRange){
12615                 d.setSelectionRange(start, end);
12616             }else if(d.createTextRange){
12617                 var range = d.createTextRange();
12618                 range.moveStart("character", start);
12619                 range.moveEnd("character", v.length-end);
12620                 range.select();
12621             }
12622         }
12623     },
12624     
12625     /**
12626      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12627      * @param {Mixed} value The value to set
12628      */
12629     setValue : function(v){
12630         this.value = v;
12631         if(this.rendered){
12632             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12633             this.validate();
12634         }
12635     },
12636     
12637     /*
12638     processValue : function(value){
12639         if(this.stripCharsRe){
12640             var newValue = value.replace(this.stripCharsRe, '');
12641             if(newValue !== value){
12642                 this.setRawValue(newValue);
12643                 return newValue;
12644             }
12645         }
12646         return value;
12647     },
12648   */
12649     preFocus : function(){
12650         
12651         if(this.selectOnFocus){
12652             this.inputEl().dom.select();
12653         }
12654     },
12655     filterKeys : function(e){
12656         var k = e.getKey();
12657         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12658             return;
12659         }
12660         var c = e.getCharCode(), cc = String.fromCharCode(c);
12661         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12662             return;
12663         }
12664         if(!this.maskRe.test(cc)){
12665             e.stopEvent();
12666         }
12667     },
12668      /**
12669      * Clear any invalid styles/messages for this field
12670      */
12671     clearInvalid : function(){
12672         
12673         if(!this.el || this.preventMark){ // not rendered
12674             return;
12675         }
12676         
12677         
12678         this.el.removeClass([this.invalidClass, 'is-invalid']);
12679         
12680         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12681             
12682             var feedback = this.el.select('.form-control-feedback', true).first();
12683             
12684             if(feedback){
12685                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12686             }
12687             
12688         }
12689         
12690         if(this.indicator){
12691             this.indicator.removeClass('visible');
12692             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12693         }
12694         
12695         this.fireEvent('valid', this);
12696     },
12697     
12698      /**
12699      * Mark this field as valid
12700      */
12701     markValid : function()
12702     {
12703         if(!this.el  || this.preventMark){ // not rendered...
12704             return;
12705         }
12706         
12707         this.el.removeClass([this.invalidClass, this.validClass]);
12708         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12709
12710         var feedback = this.el.select('.form-control-feedback', true).first();
12711             
12712         if(feedback){
12713             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12714         }
12715         
12716         if(this.indicator){
12717             this.indicator.removeClass('visible');
12718             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12719         }
12720         
12721         if(this.disabled){
12722             return;
12723         }
12724         
12725            
12726         if(this.allowBlank && !this.getRawValue().length){
12727             return;
12728         }
12729         if (Roo.bootstrap.version == 3) {
12730             this.el.addClass(this.validClass);
12731         } else {
12732             this.inputEl().addClass('is-valid');
12733         }
12734
12735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12736             
12737             var feedback = this.el.select('.form-control-feedback', true).first();
12738             
12739             if(feedback){
12740                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12741                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12742             }
12743             
12744         }
12745         
12746         this.fireEvent('valid', this);
12747     },
12748     
12749      /**
12750      * Mark this field as invalid
12751      * @param {String} msg The validation message
12752      */
12753     markInvalid : function(msg)
12754     {
12755         if(!this.el  || this.preventMark){ // not rendered
12756             return;
12757         }
12758         
12759         this.el.removeClass([this.invalidClass, this.validClass]);
12760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12761         
12762         var feedback = this.el.select('.form-control-feedback', true).first();
12763             
12764         if(feedback){
12765             this.el.select('.form-control-feedback', true).first().removeClass(
12766                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12767         }
12768
12769         if(this.disabled){
12770             return;
12771         }
12772         
12773         if(this.allowBlank && !this.getRawValue().length){
12774             return;
12775         }
12776         
12777         if(this.indicator){
12778             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12779             this.indicator.addClass('visible');
12780         }
12781         if (Roo.bootstrap.version == 3) {
12782             this.el.addClass(this.invalidClass);
12783         } else {
12784             this.inputEl().addClass('is-invalid');
12785         }
12786         
12787         
12788         
12789         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12790             
12791             var feedback = this.el.select('.form-control-feedback', true).first();
12792             
12793             if(feedback){
12794                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12795                 
12796                 if(this.getValue().length || this.forceFeedback){
12797                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12798                 }
12799                 
12800             }
12801             
12802         }
12803         
12804         this.fireEvent('invalid', this, msg);
12805     },
12806     // private
12807     SafariOnKeyDown : function(event)
12808     {
12809         // this is a workaround for a password hang bug on chrome/ webkit.
12810         if (this.inputEl().dom.type != 'password') {
12811             return;
12812         }
12813         
12814         var isSelectAll = false;
12815         
12816         if(this.inputEl().dom.selectionEnd > 0){
12817             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12818         }
12819         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12820             event.preventDefault();
12821             this.setValue('');
12822             return;
12823         }
12824         
12825         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12826             
12827             event.preventDefault();
12828             // this is very hacky as keydown always get's upper case.
12829             //
12830             var cc = String.fromCharCode(event.getCharCode());
12831             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12832             
12833         }
12834     },
12835     adjustWidth : function(tag, w){
12836         tag = tag.toLowerCase();
12837         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12838             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12839                 if(tag == 'input'){
12840                     return w + 2;
12841                 }
12842                 if(tag == 'textarea'){
12843                     return w-2;
12844                 }
12845             }else if(Roo.isOpera){
12846                 if(tag == 'input'){
12847                     return w + 2;
12848                 }
12849                 if(tag == 'textarea'){
12850                     return w-2;
12851                 }
12852             }
12853         }
12854         return w;
12855     },
12856     
12857     setFieldLabel : function(v)
12858     {
12859         if(!this.rendered){
12860             return;
12861         }
12862         
12863         if(this.indicatorEl()){
12864             var ar = this.el.select('label > span',true);
12865             
12866             if (ar.elements.length) {
12867                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12868                 this.fieldLabel = v;
12869                 return;
12870             }
12871             
12872             var br = this.el.select('label',true);
12873             
12874             if(br.elements.length) {
12875                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12876                 this.fieldLabel = v;
12877                 return;
12878             }
12879             
12880             Roo.log('Cannot Found any of label > span || label in input');
12881             return;
12882         }
12883         
12884         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12885         this.fieldLabel = v;
12886         
12887         
12888     }
12889 });
12890
12891  
12892 /*
12893  * - LGPL
12894  *
12895  * Input
12896  * 
12897  */
12898
12899 /**
12900  * @class Roo.bootstrap.TextArea
12901  * @extends Roo.bootstrap.Input
12902  * Bootstrap TextArea class
12903  * @cfg {Number} cols Specifies the visible width of a text area
12904  * @cfg {Number} rows Specifies the visible number of lines in a text area
12905  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12906  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12907  * @cfg {string} html text
12908  * 
12909  * @constructor
12910  * Create a new TextArea
12911  * @param {Object} config The config object
12912  */
12913
12914 Roo.bootstrap.TextArea = function(config){
12915     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12916    
12917 };
12918
12919 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12920      
12921     cols : false,
12922     rows : 5,
12923     readOnly : false,
12924     warp : 'soft',
12925     resize : false,
12926     value: false,
12927     html: false,
12928     
12929     getAutoCreate : function(){
12930         
12931         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12932         
12933         var id = Roo.id();
12934         
12935         var cfg = {};
12936         
12937         if(this.inputType != 'hidden'){
12938             cfg.cls = 'form-group' //input-group
12939         }
12940         
12941         var input =  {
12942             tag: 'textarea',
12943             id : id,
12944             warp : this.warp,
12945             rows : this.rows,
12946             value : this.value || '',
12947             html: this.html || '',
12948             cls : 'form-control',
12949             placeholder : this.placeholder || '' 
12950             
12951         };
12952         
12953         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12954             input.maxLength = this.maxLength;
12955         }
12956         
12957         if(this.resize){
12958             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12959         }
12960         
12961         if(this.cols){
12962             input.cols = this.cols;
12963         }
12964         
12965         if (this.readOnly) {
12966             input.readonly = true;
12967         }
12968         
12969         if (this.name) {
12970             input.name = this.name;
12971         }
12972         
12973         if (this.size) {
12974             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12975         }
12976         
12977         var settings=this;
12978         ['xs','sm','md','lg'].map(function(size){
12979             if (settings[size]) {
12980                 cfg.cls += ' col-' + size + '-' + settings[size];
12981             }
12982         });
12983         
12984         var inputblock = input;
12985         
12986         if(this.hasFeedback && !this.allowBlank){
12987             
12988             var feedback = {
12989                 tag: 'span',
12990                 cls: 'glyphicon form-control-feedback'
12991             };
12992
12993             inputblock = {
12994                 cls : 'has-feedback',
12995                 cn :  [
12996                     input,
12997                     feedback
12998                 ] 
12999             };  
13000         }
13001         
13002         
13003         if (this.before || this.after) {
13004             
13005             inputblock = {
13006                 cls : 'input-group',
13007                 cn :  [] 
13008             };
13009             if (this.before) {
13010                 inputblock.cn.push({
13011                     tag :'span',
13012                     cls : 'input-group-addon',
13013                     html : this.before
13014                 });
13015             }
13016             
13017             inputblock.cn.push(input);
13018             
13019             if(this.hasFeedback && !this.allowBlank){
13020                 inputblock.cls += ' has-feedback';
13021                 inputblock.cn.push(feedback);
13022             }
13023             
13024             if (this.after) {
13025                 inputblock.cn.push({
13026                     tag :'span',
13027                     cls : 'input-group-addon',
13028                     html : this.after
13029                 });
13030             }
13031             
13032         }
13033         
13034         if (align ==='left' && this.fieldLabel.length) {
13035             cfg.cn = [
13036                 {
13037                     tag: 'label',
13038                     'for' :  id,
13039                     cls : 'control-label',
13040                     html : this.fieldLabel
13041                 },
13042                 {
13043                     cls : "",
13044                     cn: [
13045                         inputblock
13046                     ]
13047                 }
13048
13049             ];
13050             
13051             if(this.labelWidth > 12){
13052                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13053             }
13054
13055             if(this.labelWidth < 13 && this.labelmd == 0){
13056                 this.labelmd = this.labelWidth;
13057             }
13058
13059             if(this.labellg > 0){
13060                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13061                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13062             }
13063
13064             if(this.labelmd > 0){
13065                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13066                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13067             }
13068
13069             if(this.labelsm > 0){
13070                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13071                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13072             }
13073
13074             if(this.labelxs > 0){
13075                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13076                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13077             }
13078             
13079         } else if ( this.fieldLabel.length) {
13080             cfg.cn = [
13081
13082                {
13083                    tag: 'label',
13084                    //cls : 'input-group-addon',
13085                    html : this.fieldLabel
13086
13087                },
13088
13089                inputblock
13090
13091            ];
13092
13093         } else {
13094
13095             cfg.cn = [
13096
13097                 inputblock
13098
13099             ];
13100                 
13101         }
13102         
13103         if (this.disabled) {
13104             input.disabled=true;
13105         }
13106         
13107         return cfg;
13108         
13109     },
13110     /**
13111      * return the real textarea element.
13112      */
13113     inputEl: function ()
13114     {
13115         return this.el.select('textarea.form-control',true).first();
13116     },
13117     
13118     /**
13119      * Clear any invalid styles/messages for this field
13120      */
13121     clearInvalid : function()
13122     {
13123         
13124         if(!this.el || this.preventMark){ // not rendered
13125             return;
13126         }
13127         
13128         var label = this.el.select('label', true).first();
13129         var icon = this.el.select('i.fa-star', true).first();
13130         
13131         if(label && icon){
13132             icon.remove();
13133         }
13134         this.el.removeClass( this.validClass);
13135         this.inputEl().removeClass('is-invalid');
13136          
13137         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13138             
13139             var feedback = this.el.select('.form-control-feedback', true).first();
13140             
13141             if(feedback){
13142                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13143             }
13144             
13145         }
13146         
13147         this.fireEvent('valid', this);
13148     },
13149     
13150      /**
13151      * Mark this field as valid
13152      */
13153     markValid : function()
13154     {
13155         if(!this.el  || this.preventMark){ // not rendered
13156             return;
13157         }
13158         
13159         this.el.removeClass([this.invalidClass, this.validClass]);
13160         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13161         
13162         var feedback = this.el.select('.form-control-feedback', true).first();
13163             
13164         if(feedback){
13165             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13166         }
13167
13168         if(this.disabled || this.allowBlank){
13169             return;
13170         }
13171         
13172         var label = this.el.select('label', true).first();
13173         var icon = this.el.select('i.fa-star', true).first();
13174         
13175         if(label && icon){
13176             icon.remove();
13177         }
13178         if (Roo.bootstrap.version == 3) {
13179             this.el.addClass(this.validClass);
13180         } else {
13181             this.inputEl().addClass('is-valid');
13182         }
13183         
13184         
13185         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13186             
13187             var feedback = this.el.select('.form-control-feedback', true).first();
13188             
13189             if(feedback){
13190                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13191                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13192             }
13193             
13194         }
13195         
13196         this.fireEvent('valid', this);
13197     },
13198     
13199      /**
13200      * Mark this field as invalid
13201      * @param {String} msg The validation message
13202      */
13203     markInvalid : function(msg)
13204     {
13205         if(!this.el  || this.preventMark){ // not rendered
13206             return;
13207         }
13208         
13209         this.el.removeClass([this.invalidClass, this.validClass]);
13210         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13211         
13212         var feedback = this.el.select('.form-control-feedback', true).first();
13213             
13214         if(feedback){
13215             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13216         }
13217
13218         if(this.disabled || this.allowBlank){
13219             return;
13220         }
13221         
13222         var label = this.el.select('label', true).first();
13223         var icon = this.el.select('i.fa-star', true).first();
13224         
13225         if(!this.getValue().length && label && !icon){
13226             this.el.createChild({
13227                 tag : 'i',
13228                 cls : 'text-danger fa fa-lg fa-star',
13229                 tooltip : 'This field is required',
13230                 style : 'margin-right:5px;'
13231             }, label, true);
13232         }
13233         
13234         if (Roo.bootstrap.version == 3) {
13235             this.el.addClass(this.invalidClass);
13236         } else {
13237             this.inputEl().addClass('is-invalid');
13238         }
13239         
13240         // fixme ... this may be depricated need to test..
13241         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13242             
13243             var feedback = this.el.select('.form-control-feedback', true).first();
13244             
13245             if(feedback){
13246                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13247                 
13248                 if(this.getValue().length || this.forceFeedback){
13249                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13250                 }
13251                 
13252             }
13253             
13254         }
13255         
13256         this.fireEvent('invalid', this, msg);
13257     }
13258 });
13259
13260  
13261 /*
13262  * - LGPL
13263  *
13264  * trigger field - base class for combo..
13265  * 
13266  */
13267  
13268 /**
13269  * @class Roo.bootstrap.TriggerField
13270  * @extends Roo.bootstrap.Input
13271  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13272  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13273  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13274  * for which you can provide a custom implementation.  For example:
13275  * <pre><code>
13276 var trigger = new Roo.bootstrap.TriggerField();
13277 trigger.onTriggerClick = myTriggerFn;
13278 trigger.applyTo('my-field');
13279 </code></pre>
13280  *
13281  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13282  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13283  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13284  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13285  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13286
13287  * @constructor
13288  * Create a new TriggerField.
13289  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13290  * to the base TextField)
13291  */
13292 Roo.bootstrap.TriggerField = function(config){
13293     this.mimicing = false;
13294     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13295 };
13296
13297 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13298     /**
13299      * @cfg {String} triggerClass A CSS class to apply to the trigger
13300      */
13301      /**
13302      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13303      */
13304     hideTrigger:false,
13305
13306     /**
13307      * @cfg {Boolean} removable (true|false) special filter default false
13308      */
13309     removable : false,
13310     
13311     /** @cfg {Boolean} grow @hide */
13312     /** @cfg {Number} growMin @hide */
13313     /** @cfg {Number} growMax @hide */
13314
13315     /**
13316      * @hide 
13317      * @method
13318      */
13319     autoSize: Roo.emptyFn,
13320     // private
13321     monitorTab : true,
13322     // private
13323     deferHeight : true,
13324
13325     
13326     actionMode : 'wrap',
13327     
13328     caret : false,
13329     
13330     
13331     getAutoCreate : function(){
13332        
13333         var align = this.labelAlign || this.parentLabelAlign();
13334         
13335         var id = Roo.id();
13336         
13337         var cfg = {
13338             cls: 'form-group' //input-group
13339         };
13340         
13341         
13342         var input =  {
13343             tag: 'input',
13344             id : id,
13345             type : this.inputType,
13346             cls : 'form-control',
13347             autocomplete: 'new-password',
13348             placeholder : this.placeholder || '' 
13349             
13350         };
13351         if (this.name) {
13352             input.name = this.name;
13353         }
13354         if (this.size) {
13355             input.cls += ' input-' + this.size;
13356         }
13357         
13358         if (this.disabled) {
13359             input.disabled=true;
13360         }
13361         
13362         var inputblock = input;
13363         
13364         if(this.hasFeedback && !this.allowBlank){
13365             
13366             var feedback = {
13367                 tag: 'span',
13368                 cls: 'glyphicon form-control-feedback'
13369             };
13370             
13371             if(this.removable && !this.editable  ){
13372                 inputblock = {
13373                     cls : 'has-feedback',
13374                     cn :  [
13375                         inputblock,
13376                         {
13377                             tag: 'button',
13378                             html : 'x',
13379                             cls : 'roo-combo-removable-btn close'
13380                         },
13381                         feedback
13382                     ] 
13383                 };
13384             } else {
13385                 inputblock = {
13386                     cls : 'has-feedback',
13387                     cn :  [
13388                         inputblock,
13389                         feedback
13390                     ] 
13391                 };
13392             }
13393
13394         } else {
13395             if(this.removable && !this.editable ){
13396                 inputblock = {
13397                     cls : 'roo-removable',
13398                     cn :  [
13399                         inputblock,
13400                         {
13401                             tag: 'button',
13402                             html : 'x',
13403                             cls : 'roo-combo-removable-btn close'
13404                         }
13405                     ] 
13406                 };
13407             }
13408         }
13409         
13410         if (this.before || this.after) {
13411             
13412             inputblock = {
13413                 cls : 'input-group',
13414                 cn :  [] 
13415             };
13416             if (this.before) {
13417                 inputblock.cn.push({
13418                     tag :'span',
13419                     cls : 'input-group-addon input-group-prepend input-group-text',
13420                     html : this.before
13421                 });
13422             }
13423             
13424             inputblock.cn.push(input);
13425             
13426             if(this.hasFeedback && !this.allowBlank){
13427                 inputblock.cls += ' has-feedback';
13428                 inputblock.cn.push(feedback);
13429             }
13430             
13431             if (this.after) {
13432                 inputblock.cn.push({
13433                     tag :'span',
13434                     cls : 'input-group-addon input-group-append input-group-text',
13435                     html : this.after
13436                 });
13437             }
13438             
13439         };
13440         
13441       
13442         
13443         var ibwrap = inputblock;
13444         
13445         if(this.multiple){
13446             ibwrap = {
13447                 tag: 'ul',
13448                 cls: 'roo-select2-choices',
13449                 cn:[
13450                     {
13451                         tag: 'li',
13452                         cls: 'roo-select2-search-field',
13453                         cn: [
13454
13455                             inputblock
13456                         ]
13457                     }
13458                 ]
13459             };
13460                 
13461         }
13462         
13463         var combobox = {
13464             cls: 'roo-select2-container input-group',
13465             cn: [
13466                  {
13467                     tag: 'input',
13468                     type : 'hidden',
13469                     cls: 'form-hidden-field'
13470                 },
13471                 ibwrap
13472             ]
13473         };
13474         
13475         if(!this.multiple && this.showToggleBtn){
13476             
13477             var caret = {
13478                         tag: 'span',
13479                         cls: 'caret'
13480              };
13481             if (this.caret != false) {
13482                 caret = {
13483                      tag: 'i',
13484                      cls: 'fa fa-' + this.caret
13485                 };
13486                 
13487             }
13488             
13489             combobox.cn.push({
13490                 tag :'span',
13491                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13492                 cn : [
13493                     Roo.bootstrap.version == 3 ? caret : '',
13494                     {
13495                         tag: 'span',
13496                         cls: 'combobox-clear',
13497                         cn  : [
13498                             {
13499                                 tag : 'i',
13500                                 cls: 'icon-remove'
13501                             }
13502                         ]
13503                     }
13504                 ]
13505
13506             })
13507         }
13508         
13509         if(this.multiple){
13510             combobox.cls += ' roo-select2-container-multi';
13511         }
13512          var indicator = {
13513             tag : 'i',
13514             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13515             tooltip : 'This field is required'
13516         };
13517         if (Roo.bootstrap.version == 4) {
13518             indicator = {
13519                 tag : 'i',
13520                 style : 'display:none'
13521             };
13522         }
13523         
13524         
13525         if (align ==='left' && this.fieldLabel.length) {
13526             
13527             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13528
13529             cfg.cn = [
13530                 indicator,
13531                 {
13532                     tag: 'label',
13533                     'for' :  id,
13534                     cls : 'control-label',
13535                     html : this.fieldLabel
13536
13537                 },
13538                 {
13539                     cls : "", 
13540                     cn: [
13541                         combobox
13542                     ]
13543                 }
13544
13545             ];
13546             
13547             var labelCfg = cfg.cn[1];
13548             var contentCfg = cfg.cn[2];
13549             
13550             if(this.indicatorpos == 'right'){
13551                 cfg.cn = [
13552                     {
13553                         tag: 'label',
13554                         'for' :  id,
13555                         cls : 'control-label',
13556                         cn : [
13557                             {
13558                                 tag : 'span',
13559                                 html : this.fieldLabel
13560                             },
13561                             indicator
13562                         ]
13563                     },
13564                     {
13565                         cls : "", 
13566                         cn: [
13567                             combobox
13568                         ]
13569                     }
13570
13571                 ];
13572                 
13573                 labelCfg = cfg.cn[0];
13574                 contentCfg = cfg.cn[1];
13575             }
13576             
13577             if(this.labelWidth > 12){
13578                 labelCfg.style = "width: " + this.labelWidth + 'px';
13579             }
13580             
13581             if(this.labelWidth < 13 && this.labelmd == 0){
13582                 this.labelmd = this.labelWidth;
13583             }
13584             
13585             if(this.labellg > 0){
13586                 labelCfg.cls += ' col-lg-' + this.labellg;
13587                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13588             }
13589             
13590             if(this.labelmd > 0){
13591                 labelCfg.cls += ' col-md-' + this.labelmd;
13592                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13593             }
13594             
13595             if(this.labelsm > 0){
13596                 labelCfg.cls += ' col-sm-' + this.labelsm;
13597                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13598             }
13599             
13600             if(this.labelxs > 0){
13601                 labelCfg.cls += ' col-xs-' + this.labelxs;
13602                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13603             }
13604             
13605         } else if ( this.fieldLabel.length) {
13606 //                Roo.log(" label");
13607             cfg.cn = [
13608                 indicator,
13609                {
13610                    tag: 'label',
13611                    //cls : 'input-group-addon',
13612                    html : this.fieldLabel
13613
13614                },
13615
13616                combobox
13617
13618             ];
13619             
13620             if(this.indicatorpos == 'right'){
13621                 
13622                 cfg.cn = [
13623                     {
13624                        tag: 'label',
13625                        cn : [
13626                            {
13627                                tag : 'span',
13628                                html : this.fieldLabel
13629                            },
13630                            indicator
13631                        ]
13632
13633                     },
13634                     combobox
13635
13636                 ];
13637
13638             }
13639
13640         } else {
13641             
13642 //                Roo.log(" no label && no align");
13643                 cfg = combobox
13644                      
13645                 
13646         }
13647         
13648         var settings=this;
13649         ['xs','sm','md','lg'].map(function(size){
13650             if (settings[size]) {
13651                 cfg.cls += ' col-' + size + '-' + settings[size];
13652             }
13653         });
13654         
13655         return cfg;
13656         
13657     },
13658     
13659     
13660     
13661     // private
13662     onResize : function(w, h){
13663 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13664 //        if(typeof w == 'number'){
13665 //            var x = w - this.trigger.getWidth();
13666 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13667 //            this.trigger.setStyle('left', x+'px');
13668 //        }
13669     },
13670
13671     // private
13672     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13673
13674     // private
13675     getResizeEl : function(){
13676         return this.inputEl();
13677     },
13678
13679     // private
13680     getPositionEl : function(){
13681         return this.inputEl();
13682     },
13683
13684     // private
13685     alignErrorIcon : function(){
13686         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13687     },
13688
13689     // private
13690     initEvents : function(){
13691         
13692         this.createList();
13693         
13694         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13695         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13696         if(!this.multiple && this.showToggleBtn){
13697             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13698             if(this.hideTrigger){
13699                 this.trigger.setDisplayed(false);
13700             }
13701             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13702         }
13703         
13704         if(this.multiple){
13705             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13706         }
13707         
13708         if(this.removable && !this.editable && !this.tickable){
13709             var close = this.closeTriggerEl();
13710             
13711             if(close){
13712                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13713                 close.on('click', this.removeBtnClick, this, close);
13714             }
13715         }
13716         
13717         //this.trigger.addClassOnOver('x-form-trigger-over');
13718         //this.trigger.addClassOnClick('x-form-trigger-click');
13719         
13720         //if(!this.width){
13721         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13722         //}
13723     },
13724     
13725     closeTriggerEl : function()
13726     {
13727         var close = this.el.select('.roo-combo-removable-btn', true).first();
13728         return close ? close : false;
13729     },
13730     
13731     removeBtnClick : function(e, h, el)
13732     {
13733         e.preventDefault();
13734         
13735         if(this.fireEvent("remove", this) !== false){
13736             this.reset();
13737             this.fireEvent("afterremove", this)
13738         }
13739     },
13740     
13741     createList : function()
13742     {
13743         this.list = Roo.get(document.body).createChild({
13744             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13745             cls: 'typeahead typeahead-long dropdown-menu shadow',
13746             style: 'display:none'
13747         });
13748         
13749         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13750         
13751     },
13752
13753     // private
13754     initTrigger : function(){
13755        
13756     },
13757
13758     // private
13759     onDestroy : function(){
13760         if(this.trigger){
13761             this.trigger.removeAllListeners();
13762           //  this.trigger.remove();
13763         }
13764         //if(this.wrap){
13765         //    this.wrap.remove();
13766         //}
13767         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13768     },
13769
13770     // private
13771     onFocus : function(){
13772         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13773         /*
13774         if(!this.mimicing){
13775             this.wrap.addClass('x-trigger-wrap-focus');
13776             this.mimicing = true;
13777             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13778             if(this.monitorTab){
13779                 this.el.on("keydown", this.checkTab, this);
13780             }
13781         }
13782         */
13783     },
13784
13785     // private
13786     checkTab : function(e){
13787         if(e.getKey() == e.TAB){
13788             this.triggerBlur();
13789         }
13790     },
13791
13792     // private
13793     onBlur : function(){
13794         // do nothing
13795     },
13796
13797     // private
13798     mimicBlur : function(e, t){
13799         /*
13800         if(!this.wrap.contains(t) && this.validateBlur()){
13801             this.triggerBlur();
13802         }
13803         */
13804     },
13805
13806     // private
13807     triggerBlur : function(){
13808         this.mimicing = false;
13809         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13810         if(this.monitorTab){
13811             this.el.un("keydown", this.checkTab, this);
13812         }
13813         //this.wrap.removeClass('x-trigger-wrap-focus');
13814         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13815     },
13816
13817     // private
13818     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13819     validateBlur : function(e, t){
13820         return true;
13821     },
13822
13823     // private
13824     onDisable : function(){
13825         this.inputEl().dom.disabled = true;
13826         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13827         //if(this.wrap){
13828         //    this.wrap.addClass('x-item-disabled');
13829         //}
13830     },
13831
13832     // private
13833     onEnable : function(){
13834         this.inputEl().dom.disabled = false;
13835         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13836         //if(this.wrap){
13837         //    this.el.removeClass('x-item-disabled');
13838         //}
13839     },
13840
13841     // private
13842     onShow : function(){
13843         var ae = this.getActionEl();
13844         
13845         if(ae){
13846             ae.dom.style.display = '';
13847             ae.dom.style.visibility = 'visible';
13848         }
13849     },
13850
13851     // private
13852     
13853     onHide : function(){
13854         var ae = this.getActionEl();
13855         ae.dom.style.display = 'none';
13856     },
13857
13858     /**
13859      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13860      * by an implementing function.
13861      * @method
13862      * @param {EventObject} e
13863      */
13864     onTriggerClick : Roo.emptyFn
13865 });
13866  
13867 /*
13868 * Licence: LGPL
13869 */
13870
13871 /**
13872  * @class Roo.bootstrap.CardUploader
13873  * @extends Roo.bootstrap.Button
13874  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13875  * @cfg {Number} errorTimeout default 3000
13876  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13877  * @cfg {Array}  html The button text.
13878
13879  *
13880  * @constructor
13881  * Create a new CardUploader
13882  * @param {Object} config The config object
13883  */
13884
13885 Roo.bootstrap.CardUploader = function(config){
13886     
13887  
13888     
13889     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13890     
13891     
13892     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13893         return r.data.id
13894      });
13895     
13896      this.addEvents({
13897          // raw events
13898         /**
13899          * @event preview
13900          * When a image is clicked on - and needs to display a slideshow or similar..
13901          * @param {Roo.bootstrap.Card} this
13902          * @param {Object} The image information data 
13903          *
13904          */
13905         'preview' : true,
13906          /**
13907          * @event download
13908          * When a the download link is clicked
13909          * @param {Roo.bootstrap.Card} this
13910          * @param {Object} The image information data  contains 
13911          */
13912         'download' : true
13913         
13914     });
13915 };
13916  
13917 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13918     
13919      
13920     errorTimeout : 3000,
13921      
13922     images : false,
13923    
13924     fileCollection : false,
13925     allowBlank : true,
13926     
13927     getAutoCreate : function()
13928     {
13929         
13930         var cfg =  {
13931             cls :'form-group' ,
13932             cn : [
13933                
13934                 {
13935                     tag: 'label',
13936                    //cls : 'input-group-addon',
13937                     html : this.fieldLabel
13938
13939                 },
13940
13941                 {
13942                     tag: 'input',
13943                     type : 'hidden',
13944                     name : this.name,
13945                     value : this.value,
13946                     cls : 'd-none  form-control'
13947                 },
13948                 
13949                 {
13950                     tag: 'input',
13951                     multiple : 'multiple',
13952                     type : 'file',
13953                     cls : 'd-none  roo-card-upload-selector'
13954                 },
13955                 
13956                 {
13957                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13958                 },
13959                 {
13960                     cls : 'card-columns roo-card-uploader-container'
13961                 }
13962
13963             ]
13964         };
13965            
13966          
13967         return cfg;
13968     },
13969     
13970     getChildContainer : function() /// what children are added to.
13971     {
13972         return this.containerEl;
13973     },
13974    
13975     getButtonContainer : function() /// what children are added to.
13976     {
13977         return this.el.select(".roo-card-uploader-button-container").first();
13978     },
13979    
13980     initEvents : function()
13981     {
13982         
13983         Roo.bootstrap.Input.prototype.initEvents.call(this);
13984         
13985         var t = this;
13986         this.addxtype({
13987             xns: Roo.bootstrap,
13988
13989             xtype : 'Button',
13990             container_method : 'getButtonContainer' ,            
13991             html :  this.html, // fix changable?
13992             cls : 'w-100 ',
13993             listeners : {
13994                 'click' : function(btn, e) {
13995                     t.onClick(e);
13996                 }
13997             }
13998         });
13999         
14000         
14001         
14002         
14003         this.urlAPI = (window.createObjectURL && window) || 
14004                                 (window.URL && URL.revokeObjectURL && URL) || 
14005                                 (window.webkitURL && webkitURL);
14006                         
14007          
14008          
14009          
14010         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14011         
14012         this.selectorEl.on('change', this.onFileSelected, this);
14013         if (this.images) {
14014             var t = this;
14015             this.images.forEach(function(img) {
14016                 t.addCard(img)
14017             });
14018             this.images = false;
14019         }
14020         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14021          
14022        
14023     },
14024     
14025    
14026     onClick : function(e)
14027     {
14028         e.preventDefault();
14029          
14030         this.selectorEl.dom.click();
14031          
14032     },
14033     
14034     onFileSelected : function(e)
14035     {
14036         e.preventDefault();
14037         
14038         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14039             return;
14040         }
14041         
14042         Roo.each(this.selectorEl.dom.files, function(file){    
14043             this.addFile(file);
14044         }, this);
14045          
14046     },
14047     
14048       
14049     
14050       
14051     
14052     addFile : function(file)
14053     {
14054            
14055         if(typeof(file) === 'string'){
14056             throw "Add file by name?"; // should not happen
14057             return;
14058         }
14059         
14060         if(!file || !this.urlAPI){
14061             return;
14062         }
14063         
14064         // file;
14065         // file.type;
14066         
14067         var _this = this;
14068         
14069         
14070         var url = _this.urlAPI.createObjectURL( file);
14071            
14072         this.addCard({
14073             id : Roo.bootstrap.CardUploader.ID--,
14074             is_uploaded : false,
14075             src : url,
14076             srcfile : file,
14077             title : file.name,
14078             mimetype : file.type,
14079             preview : false,
14080             is_deleted : 0
14081         });
14082         
14083     },
14084     
14085     /**
14086      * addCard - add an Attachment to the uploader
14087      * @param data - the data about the image to upload
14088      *
14089      * {
14090           id : 123
14091           title : "Title of file",
14092           is_uploaded : false,
14093           src : "http://.....",
14094           srcfile : { the File upload object },
14095           mimetype : file.type,
14096           preview : false,
14097           is_deleted : 0
14098           .. any other data...
14099         }
14100      *
14101      * 
14102     */
14103     
14104     addCard : function (data)
14105     {
14106         // hidden input element?
14107         // if the file is not an image...
14108         //then we need to use something other that and header_image
14109         var t = this;
14110         //   remove.....
14111         var footer = [
14112             {
14113                 xns : Roo.bootstrap,
14114                 xtype : 'CardFooter',
14115                  items: [
14116                     {
14117                         xns : Roo.bootstrap,
14118                         xtype : 'Element',
14119                         cls : 'd-flex',
14120                         items : [
14121                             
14122                             {
14123                                 xns : Roo.bootstrap,
14124                                 xtype : 'Button',
14125                                 html : String.format("<small>{0}</small>", data.title),
14126                                 cls : 'col-10 text-left',
14127                                 size: 'sm',
14128                                 weight: 'link',
14129                                 fa : 'download',
14130                                 listeners : {
14131                                     click : function() {
14132                                      
14133                                         t.fireEvent( "download", t, data );
14134                                     }
14135                                 }
14136                             },
14137                           
14138                             {
14139                                 xns : Roo.bootstrap,
14140                                 xtype : 'Button',
14141                                 style: 'max-height: 28px; ',
14142                                 size : 'sm',
14143                                 weight: 'danger',
14144                                 cls : 'col-2',
14145                                 fa : 'times',
14146                                 listeners : {
14147                                     click : function() {
14148                                         t.removeCard(data.id)
14149                                     }
14150                                 }
14151                             }
14152                         ]
14153                     }
14154                     
14155                 ] 
14156             }
14157             
14158         ];
14159         
14160         var cn = this.addxtype(
14161             {
14162                  
14163                 xns : Roo.bootstrap,
14164                 xtype : 'Card',
14165                 closeable : true,
14166                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14167                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14168                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14169                 data : data,
14170                 html : false,
14171                  
14172                 items : footer,
14173                 initEvents : function() {
14174                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14175                     var card = this;
14176                     this.imgEl = this.el.select('.card-img-top').first();
14177                     if (this.imgEl) {
14178                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14179                         this.imgEl.set({ 'pointer' : 'cursor' });
14180                                   
14181                     }
14182                     this.getCardFooter().addClass('p-1');
14183                     
14184                   
14185                 }
14186                 
14187             }
14188         );
14189         // dont' really need ot update items.
14190         // this.items.push(cn);
14191         this.fileCollection.add(cn);
14192         
14193         if (!data.srcfile) {
14194             this.updateInput();
14195             return;
14196         }
14197             
14198         var _t = this;
14199         var reader = new FileReader();
14200         reader.addEventListener("load", function() {  
14201             data.srcdata =  reader.result;
14202             _t.updateInput();
14203         });
14204         reader.readAsDataURL(data.srcfile);
14205         
14206         
14207         
14208     },
14209     removeCard : function(id)
14210     {
14211         
14212         var card  = this.fileCollection.get(id);
14213         card.data.is_deleted = 1;
14214         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14215         //this.fileCollection.remove(card);
14216         //this.items = this.items.filter(function(e) { return e != card });
14217         // dont' really need ot update items.
14218         card.el.dom.parentNode.removeChild(card.el.dom);
14219         this.updateInput();
14220
14221         
14222     },
14223     reset: function()
14224     {
14225         this.fileCollection.each(function(card) {
14226             if (card.el.dom && card.el.dom.parentNode) {
14227                 card.el.dom.parentNode.removeChild(card.el.dom);
14228             }
14229         });
14230         this.fileCollection.clear();
14231         this.updateInput();
14232     },
14233     
14234     updateInput : function()
14235     {
14236          var data = [];
14237         this.fileCollection.each(function(e) {
14238             data.push(e.data);
14239             
14240         });
14241         this.inputEl().dom.value = JSON.stringify(data);
14242         
14243         
14244         
14245     }
14246     
14247     
14248 });
14249
14250
14251 Roo.bootstrap.CardUploader.ID = -1;/*
14252  * Based on:
14253  * Ext JS Library 1.1.1
14254  * Copyright(c) 2006-2007, Ext JS, LLC.
14255  *
14256  * Originally Released Under LGPL - original licence link has changed is not relivant.
14257  *
14258  * Fork - LGPL
14259  * <script type="text/javascript">
14260  */
14261
14262
14263 /**
14264  * @class Roo.data.SortTypes
14265  * @singleton
14266  * Defines the default sorting (casting?) comparison functions used when sorting data.
14267  */
14268 Roo.data.SortTypes = {
14269     /**
14270      * Default sort that does nothing
14271      * @param {Mixed} s The value being converted
14272      * @return {Mixed} The comparison value
14273      */
14274     none : function(s){
14275         return s;
14276     },
14277     
14278     /**
14279      * The regular expression used to strip tags
14280      * @type {RegExp}
14281      * @property
14282      */
14283     stripTagsRE : /<\/?[^>]+>/gi,
14284     
14285     /**
14286      * Strips all HTML tags to sort on text only
14287      * @param {Mixed} s The value being converted
14288      * @return {String} The comparison value
14289      */
14290     asText : function(s){
14291         return String(s).replace(this.stripTagsRE, "");
14292     },
14293     
14294     /**
14295      * Strips all HTML tags to sort on text only - Case insensitive
14296      * @param {Mixed} s The value being converted
14297      * @return {String} The comparison value
14298      */
14299     asUCText : function(s){
14300         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14301     },
14302     
14303     /**
14304      * Case insensitive string
14305      * @param {Mixed} s The value being converted
14306      * @return {String} The comparison value
14307      */
14308     asUCString : function(s) {
14309         return String(s).toUpperCase();
14310     },
14311     
14312     /**
14313      * Date sorting
14314      * @param {Mixed} s The value being converted
14315      * @return {Number} The comparison value
14316      */
14317     asDate : function(s) {
14318         if(!s){
14319             return 0;
14320         }
14321         if(s instanceof Date){
14322             return s.getTime();
14323         }
14324         return Date.parse(String(s));
14325     },
14326     
14327     /**
14328      * Float sorting
14329      * @param {Mixed} s The value being converted
14330      * @return {Float} The comparison value
14331      */
14332     asFloat : function(s) {
14333         var val = parseFloat(String(s).replace(/,/g, ""));
14334         if(isNaN(val)) {
14335             val = 0;
14336         }
14337         return val;
14338     },
14339     
14340     /**
14341      * Integer sorting
14342      * @param {Mixed} s The value being converted
14343      * @return {Number} The comparison value
14344      */
14345     asInt : function(s) {
14346         var val = parseInt(String(s).replace(/,/g, ""));
14347         if(isNaN(val)) {
14348             val = 0;
14349         }
14350         return val;
14351     }
14352 };/*
14353  * Based on:
14354  * Ext JS Library 1.1.1
14355  * Copyright(c) 2006-2007, Ext JS, LLC.
14356  *
14357  * Originally Released Under LGPL - original licence link has changed is not relivant.
14358  *
14359  * Fork - LGPL
14360  * <script type="text/javascript">
14361  */
14362
14363 /**
14364 * @class Roo.data.Record
14365  * Instances of this class encapsulate both record <em>definition</em> information, and record
14366  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14367  * to access Records cached in an {@link Roo.data.Store} object.<br>
14368  * <p>
14369  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14370  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14371  * objects.<br>
14372  * <p>
14373  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14374  * @constructor
14375  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14376  * {@link #create}. The parameters are the same.
14377  * @param {Array} data An associative Array of data values keyed by the field name.
14378  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14379  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14380  * not specified an integer id is generated.
14381  */
14382 Roo.data.Record = function(data, id){
14383     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14384     this.data = data;
14385 };
14386
14387 /**
14388  * Generate a constructor for a specific record layout.
14389  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14390  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14391  * Each field definition object may contain the following properties: <ul>
14392  * <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,
14393  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14394  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14395  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14396  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14397  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14398  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14399  * this may be omitted.</p></li>
14400  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14401  * <ul><li>auto (Default, implies no conversion)</li>
14402  * <li>string</li>
14403  * <li>int</li>
14404  * <li>float</li>
14405  * <li>boolean</li>
14406  * <li>date</li></ul></p></li>
14407  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14408  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14409  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14410  * by the Reader into an object that will be stored in the Record. It is passed the
14411  * following parameters:<ul>
14412  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14413  * </ul></p></li>
14414  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14415  * </ul>
14416  * <br>usage:<br><pre><code>
14417 var TopicRecord = Roo.data.Record.create(
14418     {name: 'title', mapping: 'topic_title'},
14419     {name: 'author', mapping: 'username'},
14420     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14421     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14422     {name: 'lastPoster', mapping: 'user2'},
14423     {name: 'excerpt', mapping: 'post_text'}
14424 );
14425
14426 var myNewRecord = new TopicRecord({
14427     title: 'Do my job please',
14428     author: 'noobie',
14429     totalPosts: 1,
14430     lastPost: new Date(),
14431     lastPoster: 'Animal',
14432     excerpt: 'No way dude!'
14433 });
14434 myStore.add(myNewRecord);
14435 </code></pre>
14436  * @method create
14437  * @static
14438  */
14439 Roo.data.Record.create = function(o){
14440     var f = function(){
14441         f.superclass.constructor.apply(this, arguments);
14442     };
14443     Roo.extend(f, Roo.data.Record);
14444     var p = f.prototype;
14445     p.fields = new Roo.util.MixedCollection(false, function(field){
14446         return field.name;
14447     });
14448     for(var i = 0, len = o.length; i < len; i++){
14449         p.fields.add(new Roo.data.Field(o[i]));
14450     }
14451     f.getField = function(name){
14452         return p.fields.get(name);  
14453     };
14454     return f;
14455 };
14456
14457 Roo.data.Record.AUTO_ID = 1000;
14458 Roo.data.Record.EDIT = 'edit';
14459 Roo.data.Record.REJECT = 'reject';
14460 Roo.data.Record.COMMIT = 'commit';
14461
14462 Roo.data.Record.prototype = {
14463     /**
14464      * Readonly flag - true if this record has been modified.
14465      * @type Boolean
14466      */
14467     dirty : false,
14468     editing : false,
14469     error: null,
14470     modified: null,
14471
14472     // private
14473     join : function(store){
14474         this.store = store;
14475     },
14476
14477     /**
14478      * Set the named field to the specified value.
14479      * @param {String} name The name of the field to set.
14480      * @param {Object} value The value to set the field to.
14481      */
14482     set : function(name, value){
14483         if(this.data[name] == value){
14484             return;
14485         }
14486         this.dirty = true;
14487         if(!this.modified){
14488             this.modified = {};
14489         }
14490         if(typeof this.modified[name] == 'undefined'){
14491             this.modified[name] = this.data[name];
14492         }
14493         this.data[name] = value;
14494         if(!this.editing && this.store){
14495             this.store.afterEdit(this);
14496         }       
14497     },
14498
14499     /**
14500      * Get the value of the named field.
14501      * @param {String} name The name of the field to get the value of.
14502      * @return {Object} The value of the field.
14503      */
14504     get : function(name){
14505         return this.data[name]; 
14506     },
14507
14508     // private
14509     beginEdit : function(){
14510         this.editing = true;
14511         this.modified = {}; 
14512     },
14513
14514     // private
14515     cancelEdit : function(){
14516         this.editing = false;
14517         delete this.modified;
14518     },
14519
14520     // private
14521     endEdit : function(){
14522         this.editing = false;
14523         if(this.dirty && this.store){
14524             this.store.afterEdit(this);
14525         }
14526     },
14527
14528     /**
14529      * Usually called by the {@link Roo.data.Store} which owns the Record.
14530      * Rejects all changes made to the Record since either creation, or the last commit operation.
14531      * Modified fields are reverted to their original values.
14532      * <p>
14533      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14534      * of reject operations.
14535      */
14536     reject : function(){
14537         var m = this.modified;
14538         for(var n in m){
14539             if(typeof m[n] != "function"){
14540                 this.data[n] = m[n];
14541             }
14542         }
14543         this.dirty = false;
14544         delete this.modified;
14545         this.editing = false;
14546         if(this.store){
14547             this.store.afterReject(this);
14548         }
14549     },
14550
14551     /**
14552      * Usually called by the {@link Roo.data.Store} which owns the Record.
14553      * Commits all changes made to the Record since either creation, or the last commit operation.
14554      * <p>
14555      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14556      * of commit operations.
14557      */
14558     commit : function(){
14559         this.dirty = false;
14560         delete this.modified;
14561         this.editing = false;
14562         if(this.store){
14563             this.store.afterCommit(this);
14564         }
14565     },
14566
14567     // private
14568     hasError : function(){
14569         return this.error != null;
14570     },
14571
14572     // private
14573     clearError : function(){
14574         this.error = null;
14575     },
14576
14577     /**
14578      * Creates a copy of this record.
14579      * @param {String} id (optional) A new record id if you don't want to use this record's id
14580      * @return {Record}
14581      */
14582     copy : function(newId) {
14583         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14584     }
14585 };/*
14586  * Based on:
14587  * Ext JS Library 1.1.1
14588  * Copyright(c) 2006-2007, Ext JS, LLC.
14589  *
14590  * Originally Released Under LGPL - original licence link has changed is not relivant.
14591  *
14592  * Fork - LGPL
14593  * <script type="text/javascript">
14594  */
14595
14596
14597
14598 /**
14599  * @class Roo.data.Store
14600  * @extends Roo.util.Observable
14601  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14602  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14603  * <p>
14604  * 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
14605  * has no knowledge of the format of the data returned by the Proxy.<br>
14606  * <p>
14607  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14608  * instances from the data object. These records are cached and made available through accessor functions.
14609  * @constructor
14610  * Creates a new Store.
14611  * @param {Object} config A config object containing the objects needed for the Store to access data,
14612  * and read the data into Records.
14613  */
14614 Roo.data.Store = function(config){
14615     this.data = new Roo.util.MixedCollection(false);
14616     this.data.getKey = function(o){
14617         return o.id;
14618     };
14619     this.baseParams = {};
14620     // private
14621     this.paramNames = {
14622         "start" : "start",
14623         "limit" : "limit",
14624         "sort" : "sort",
14625         "dir" : "dir",
14626         "multisort" : "_multisort"
14627     };
14628
14629     if(config && config.data){
14630         this.inlineData = config.data;
14631         delete config.data;
14632     }
14633
14634     Roo.apply(this, config);
14635     
14636     if(this.reader){ // reader passed
14637         this.reader = Roo.factory(this.reader, Roo.data);
14638         this.reader.xmodule = this.xmodule || false;
14639         if(!this.recordType){
14640             this.recordType = this.reader.recordType;
14641         }
14642         if(this.reader.onMetaChange){
14643             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14644         }
14645     }
14646
14647     if(this.recordType){
14648         this.fields = this.recordType.prototype.fields;
14649     }
14650     this.modified = [];
14651
14652     this.addEvents({
14653         /**
14654          * @event datachanged
14655          * Fires when the data cache has changed, and a widget which is using this Store
14656          * as a Record cache should refresh its view.
14657          * @param {Store} this
14658          */
14659         datachanged : true,
14660         /**
14661          * @event metachange
14662          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14663          * @param {Store} this
14664          * @param {Object} meta The JSON metadata
14665          */
14666         metachange : true,
14667         /**
14668          * @event add
14669          * Fires when Records have been added to the Store
14670          * @param {Store} this
14671          * @param {Roo.data.Record[]} records The array of Records added
14672          * @param {Number} index The index at which the record(s) were added
14673          */
14674         add : true,
14675         /**
14676          * @event remove
14677          * Fires when a Record has been removed from the Store
14678          * @param {Store} this
14679          * @param {Roo.data.Record} record The Record that was removed
14680          * @param {Number} index The index at which the record was removed
14681          */
14682         remove : true,
14683         /**
14684          * @event update
14685          * Fires when a Record has been updated
14686          * @param {Store} this
14687          * @param {Roo.data.Record} record The Record that was updated
14688          * @param {String} operation The update operation being performed.  Value may be one of:
14689          * <pre><code>
14690  Roo.data.Record.EDIT
14691  Roo.data.Record.REJECT
14692  Roo.data.Record.COMMIT
14693          * </code></pre>
14694          */
14695         update : true,
14696         /**
14697          * @event clear
14698          * Fires when the data cache has been cleared.
14699          * @param {Store} this
14700          */
14701         clear : true,
14702         /**
14703          * @event beforeload
14704          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14705          * the load action will be canceled.
14706          * @param {Store} this
14707          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14708          */
14709         beforeload : true,
14710         /**
14711          * @event beforeloadadd
14712          * Fires after a new set of Records has been loaded.
14713          * @param {Store} this
14714          * @param {Roo.data.Record[]} records The Records that were loaded
14715          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14716          */
14717         beforeloadadd : true,
14718         /**
14719          * @event load
14720          * Fires after a new set of Records has been loaded, before they are added to the store.
14721          * @param {Store} this
14722          * @param {Roo.data.Record[]} records The Records that were loaded
14723          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14724          * @params {Object} return from reader
14725          */
14726         load : true,
14727         /**
14728          * @event loadexception
14729          * Fires if an exception occurs in the Proxy during loading.
14730          * Called with the signature of the Proxy's "loadexception" event.
14731          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14732          * 
14733          * @param {Proxy} 
14734          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14735          * @param {Object} load options 
14736          * @param {Object} jsonData from your request (normally this contains the Exception)
14737          */
14738         loadexception : true
14739     });
14740     
14741     if(this.proxy){
14742         this.proxy = Roo.factory(this.proxy, Roo.data);
14743         this.proxy.xmodule = this.xmodule || false;
14744         this.relayEvents(this.proxy,  ["loadexception"]);
14745     }
14746     this.sortToggle = {};
14747     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14748
14749     Roo.data.Store.superclass.constructor.call(this);
14750
14751     if(this.inlineData){
14752         this.loadData(this.inlineData);
14753         delete this.inlineData;
14754     }
14755 };
14756
14757 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14758      /**
14759     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14760     * without a remote query - used by combo/forms at present.
14761     */
14762     
14763     /**
14764     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14765     */
14766     /**
14767     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14768     */
14769     /**
14770     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14771     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14772     */
14773     /**
14774     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14775     * on any HTTP request
14776     */
14777     /**
14778     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14779     */
14780     /**
14781     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14782     */
14783     multiSort: false,
14784     /**
14785     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14786     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14787     */
14788     remoteSort : false,
14789
14790     /**
14791     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14792      * loaded or when a record is removed. (defaults to false).
14793     */
14794     pruneModifiedRecords : false,
14795
14796     // private
14797     lastOptions : null,
14798
14799     /**
14800      * Add Records to the Store and fires the add event.
14801      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14802      */
14803     add : function(records){
14804         records = [].concat(records);
14805         for(var i = 0, len = records.length; i < len; i++){
14806             records[i].join(this);
14807         }
14808         var index = this.data.length;
14809         this.data.addAll(records);
14810         this.fireEvent("add", this, records, index);
14811     },
14812
14813     /**
14814      * Remove a Record from the Store and fires the remove event.
14815      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14816      */
14817     remove : function(record){
14818         var index = this.data.indexOf(record);
14819         this.data.removeAt(index);
14820  
14821         if(this.pruneModifiedRecords){
14822             this.modified.remove(record);
14823         }
14824         this.fireEvent("remove", this, record, index);
14825     },
14826
14827     /**
14828      * Remove all Records from the Store and fires the clear event.
14829      */
14830     removeAll : function(){
14831         this.data.clear();
14832         if(this.pruneModifiedRecords){
14833             this.modified = [];
14834         }
14835         this.fireEvent("clear", this);
14836     },
14837
14838     /**
14839      * Inserts Records to the Store at the given index and fires the add event.
14840      * @param {Number} index The start index at which to insert the passed Records.
14841      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14842      */
14843     insert : function(index, records){
14844         records = [].concat(records);
14845         for(var i = 0, len = records.length; i < len; i++){
14846             this.data.insert(index, records[i]);
14847             records[i].join(this);
14848         }
14849         this.fireEvent("add", this, records, index);
14850     },
14851
14852     /**
14853      * Get the index within the cache of the passed Record.
14854      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14855      * @return {Number} The index of the passed Record. Returns -1 if not found.
14856      */
14857     indexOf : function(record){
14858         return this.data.indexOf(record);
14859     },
14860
14861     /**
14862      * Get the index within the cache of the Record with the passed id.
14863      * @param {String} id The id of the Record to find.
14864      * @return {Number} The index of the Record. Returns -1 if not found.
14865      */
14866     indexOfId : function(id){
14867         return this.data.indexOfKey(id);
14868     },
14869
14870     /**
14871      * Get the Record with the specified id.
14872      * @param {String} id The id of the Record to find.
14873      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14874      */
14875     getById : function(id){
14876         return this.data.key(id);
14877     },
14878
14879     /**
14880      * Get the Record at the specified index.
14881      * @param {Number} index The index of the Record to find.
14882      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14883      */
14884     getAt : function(index){
14885         return this.data.itemAt(index);
14886     },
14887
14888     /**
14889      * Returns a range of Records between specified indices.
14890      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14891      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14892      * @return {Roo.data.Record[]} An array of Records
14893      */
14894     getRange : function(start, end){
14895         return this.data.getRange(start, end);
14896     },
14897
14898     // private
14899     storeOptions : function(o){
14900         o = Roo.apply({}, o);
14901         delete o.callback;
14902         delete o.scope;
14903         this.lastOptions = o;
14904     },
14905
14906     /**
14907      * Loads the Record cache from the configured Proxy using the configured Reader.
14908      * <p>
14909      * If using remote paging, then the first load call must specify the <em>start</em>
14910      * and <em>limit</em> properties in the options.params property to establish the initial
14911      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14912      * <p>
14913      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14914      * and this call will return before the new data has been loaded. Perform any post-processing
14915      * in a callback function, or in a "load" event handler.</strong>
14916      * <p>
14917      * @param {Object} options An object containing properties which control loading options:<ul>
14918      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14919      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14920      * passed the following arguments:<ul>
14921      * <li>r : Roo.data.Record[]</li>
14922      * <li>options: Options object from the load call</li>
14923      * <li>success: Boolean success indicator</li></ul></li>
14924      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14925      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14926      * </ul>
14927      */
14928     load : function(options){
14929         options = options || {};
14930         if(this.fireEvent("beforeload", this, options) !== false){
14931             this.storeOptions(options);
14932             var p = Roo.apply(options.params || {}, this.baseParams);
14933             // if meta was not loaded from remote source.. try requesting it.
14934             if (!this.reader.metaFromRemote) {
14935                 p._requestMeta = 1;
14936             }
14937             if(this.sortInfo && this.remoteSort){
14938                 var pn = this.paramNames;
14939                 p[pn["sort"]] = this.sortInfo.field;
14940                 p[pn["dir"]] = this.sortInfo.direction;
14941             }
14942             if (this.multiSort) {
14943                 var pn = this.paramNames;
14944                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14945             }
14946             
14947             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14948         }
14949     },
14950
14951     /**
14952      * Reloads the Record cache from the configured Proxy using the configured Reader and
14953      * the options from the last load operation performed.
14954      * @param {Object} options (optional) An object containing properties which may override the options
14955      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14956      * the most recently used options are reused).
14957      */
14958     reload : function(options){
14959         this.load(Roo.applyIf(options||{}, this.lastOptions));
14960     },
14961
14962     // private
14963     // Called as a callback by the Reader during a load operation.
14964     loadRecords : function(o, options, success){
14965         if(!o || success === false){
14966             if(success !== false){
14967                 this.fireEvent("load", this, [], options, o);
14968             }
14969             if(options.callback){
14970                 options.callback.call(options.scope || this, [], options, false);
14971             }
14972             return;
14973         }
14974         // if data returned failure - throw an exception.
14975         if (o.success === false) {
14976             // show a message if no listener is registered.
14977             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14978                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14979             }
14980             // loadmask wil be hooked into this..
14981             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14982             return;
14983         }
14984         var r = o.records, t = o.totalRecords || r.length;
14985         
14986         this.fireEvent("beforeloadadd", this, r, options, o);
14987         
14988         if(!options || options.add !== true){
14989             if(this.pruneModifiedRecords){
14990                 this.modified = [];
14991             }
14992             for(var i = 0, len = r.length; i < len; i++){
14993                 r[i].join(this);
14994             }
14995             if(this.snapshot){
14996                 this.data = this.snapshot;
14997                 delete this.snapshot;
14998             }
14999             this.data.clear();
15000             this.data.addAll(r);
15001             this.totalLength = t;
15002             this.applySort();
15003             this.fireEvent("datachanged", this);
15004         }else{
15005             this.totalLength = Math.max(t, this.data.length+r.length);
15006             this.add(r);
15007         }
15008         
15009         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15010                 
15011             var e = new Roo.data.Record({});
15012
15013             e.set(this.parent.displayField, this.parent.emptyTitle);
15014             e.set(this.parent.valueField, '');
15015
15016             this.insert(0, e);
15017         }
15018             
15019         this.fireEvent("load", this, r, options, o);
15020         if(options.callback){
15021             options.callback.call(options.scope || this, r, options, true);
15022         }
15023     },
15024
15025
15026     /**
15027      * Loads data from a passed data block. A Reader which understands the format of the data
15028      * must have been configured in the constructor.
15029      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15030      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15031      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15032      */
15033     loadData : function(o, append){
15034         var r = this.reader.readRecords(o);
15035         this.loadRecords(r, {add: append}, true);
15036     },
15037     
15038      /**
15039      * using 'cn' the nested child reader read the child array into it's child stores.
15040      * @param {Object} rec The record with a 'children array
15041      */
15042     loadDataFromChildren : function(rec)
15043     {
15044         this.loadData(this.reader.toLoadData(rec));
15045     },
15046     
15047
15048     /**
15049      * Gets the number of cached records.
15050      * <p>
15051      * <em>If using paging, this may not be the total size of the dataset. If the data object
15052      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15053      * the data set size</em>
15054      */
15055     getCount : function(){
15056         return this.data.length || 0;
15057     },
15058
15059     /**
15060      * Gets the total number of records in the dataset as returned by the server.
15061      * <p>
15062      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15063      * the dataset size</em>
15064      */
15065     getTotalCount : function(){
15066         return this.totalLength || 0;
15067     },
15068
15069     /**
15070      * Returns the sort state of the Store as an object with two properties:
15071      * <pre><code>
15072  field {String} The name of the field by which the Records are sorted
15073  direction {String} The sort order, "ASC" or "DESC"
15074      * </code></pre>
15075      */
15076     getSortState : function(){
15077         return this.sortInfo;
15078     },
15079
15080     // private
15081     applySort : function(){
15082         if(this.sortInfo && !this.remoteSort){
15083             var s = this.sortInfo, f = s.field;
15084             var st = this.fields.get(f).sortType;
15085             var fn = function(r1, r2){
15086                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15087                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15088             };
15089             this.data.sort(s.direction, fn);
15090             if(this.snapshot && this.snapshot != this.data){
15091                 this.snapshot.sort(s.direction, fn);
15092             }
15093         }
15094     },
15095
15096     /**
15097      * Sets the default sort column and order to be used by the next load operation.
15098      * @param {String} fieldName The name of the field to sort by.
15099      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15100      */
15101     setDefaultSort : function(field, dir){
15102         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15103     },
15104
15105     /**
15106      * Sort the Records.
15107      * If remote sorting is used, the sort is performed on the server, and the cache is
15108      * reloaded. If local sorting is used, the cache is sorted internally.
15109      * @param {String} fieldName The name of the field to sort by.
15110      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15111      */
15112     sort : function(fieldName, dir){
15113         var f = this.fields.get(fieldName);
15114         if(!dir){
15115             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15116             
15117             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15118                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15119             }else{
15120                 dir = f.sortDir;
15121             }
15122         }
15123         this.sortToggle[f.name] = dir;
15124         this.sortInfo = {field: f.name, direction: dir};
15125         if(!this.remoteSort){
15126             this.applySort();
15127             this.fireEvent("datachanged", this);
15128         }else{
15129             this.load(this.lastOptions);
15130         }
15131     },
15132
15133     /**
15134      * Calls the specified function for each of the Records in the cache.
15135      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15136      * Returning <em>false</em> aborts and exits the iteration.
15137      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15138      */
15139     each : function(fn, scope){
15140         this.data.each(fn, scope);
15141     },
15142
15143     /**
15144      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15145      * (e.g., during paging).
15146      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15147      */
15148     getModifiedRecords : function(){
15149         return this.modified;
15150     },
15151
15152     // private
15153     createFilterFn : function(property, value, anyMatch){
15154         if(!value.exec){ // not a regex
15155             value = String(value);
15156             if(value.length == 0){
15157                 return false;
15158             }
15159             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15160         }
15161         return function(r){
15162             return value.test(r.data[property]);
15163         };
15164     },
15165
15166     /**
15167      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15168      * @param {String} property A field on your records
15169      * @param {Number} start The record index to start at (defaults to 0)
15170      * @param {Number} end The last record index to include (defaults to length - 1)
15171      * @return {Number} The sum
15172      */
15173     sum : function(property, start, end){
15174         var rs = this.data.items, v = 0;
15175         start = start || 0;
15176         end = (end || end === 0) ? end : rs.length-1;
15177
15178         for(var i = start; i <= end; i++){
15179             v += (rs[i].data[property] || 0);
15180         }
15181         return v;
15182     },
15183
15184     /**
15185      * Filter the records by a specified property.
15186      * @param {String} field A field on your records
15187      * @param {String/RegExp} value Either a string that the field
15188      * should start with or a RegExp to test against the field
15189      * @param {Boolean} anyMatch True to match any part not just the beginning
15190      */
15191     filter : function(property, value, anyMatch){
15192         var fn = this.createFilterFn(property, value, anyMatch);
15193         return fn ? this.filterBy(fn) : this.clearFilter();
15194     },
15195
15196     /**
15197      * Filter by a function. The specified function will be called with each
15198      * record in this data source. If the function returns true the record is included,
15199      * otherwise it is filtered.
15200      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15201      * @param {Object} scope (optional) The scope of the function (defaults to this)
15202      */
15203     filterBy : function(fn, scope){
15204         this.snapshot = this.snapshot || this.data;
15205         this.data = this.queryBy(fn, scope||this);
15206         this.fireEvent("datachanged", this);
15207     },
15208
15209     /**
15210      * Query the records by a specified property.
15211      * @param {String} field A field on your records
15212      * @param {String/RegExp} value Either a string that the field
15213      * should start with or a RegExp to test against the field
15214      * @param {Boolean} anyMatch True to match any part not just the beginning
15215      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15216      */
15217     query : function(property, value, anyMatch){
15218         var fn = this.createFilterFn(property, value, anyMatch);
15219         return fn ? this.queryBy(fn) : this.data.clone();
15220     },
15221
15222     /**
15223      * Query by a function. The specified function will be called with each
15224      * record in this data source. If the function returns true the record is included
15225      * in the results.
15226      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15227      * @param {Object} scope (optional) The scope of the function (defaults to this)
15228       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15229      **/
15230     queryBy : function(fn, scope){
15231         var data = this.snapshot || this.data;
15232         return data.filterBy(fn, scope||this);
15233     },
15234
15235     /**
15236      * Collects unique values for a particular dataIndex from this store.
15237      * @param {String} dataIndex The property to collect
15238      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15239      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15240      * @return {Array} An array of the unique values
15241      **/
15242     collect : function(dataIndex, allowNull, bypassFilter){
15243         var d = (bypassFilter === true && this.snapshot) ?
15244                 this.snapshot.items : this.data.items;
15245         var v, sv, r = [], l = {};
15246         for(var i = 0, len = d.length; i < len; i++){
15247             v = d[i].data[dataIndex];
15248             sv = String(v);
15249             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15250                 l[sv] = true;
15251                 r[r.length] = v;
15252             }
15253         }
15254         return r;
15255     },
15256
15257     /**
15258      * Revert to a view of the Record cache with no filtering applied.
15259      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15260      */
15261     clearFilter : function(suppressEvent){
15262         if(this.snapshot && this.snapshot != this.data){
15263             this.data = this.snapshot;
15264             delete this.snapshot;
15265             if(suppressEvent !== true){
15266                 this.fireEvent("datachanged", this);
15267             }
15268         }
15269     },
15270
15271     // private
15272     afterEdit : function(record){
15273         if(this.modified.indexOf(record) == -1){
15274             this.modified.push(record);
15275         }
15276         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15277     },
15278     
15279     // private
15280     afterReject : function(record){
15281         this.modified.remove(record);
15282         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15283     },
15284
15285     // private
15286     afterCommit : function(record){
15287         this.modified.remove(record);
15288         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15289     },
15290
15291     /**
15292      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15293      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15294      */
15295     commitChanges : function(){
15296         var m = this.modified.slice(0);
15297         this.modified = [];
15298         for(var i = 0, len = m.length; i < len; i++){
15299             m[i].commit();
15300         }
15301     },
15302
15303     /**
15304      * Cancel outstanding changes on all changed records.
15305      */
15306     rejectChanges : function(){
15307         var m = this.modified.slice(0);
15308         this.modified = [];
15309         for(var i = 0, len = m.length; i < len; i++){
15310             m[i].reject();
15311         }
15312     },
15313
15314     onMetaChange : function(meta, rtype, o){
15315         this.recordType = rtype;
15316         this.fields = rtype.prototype.fields;
15317         delete this.snapshot;
15318         this.sortInfo = meta.sortInfo || this.sortInfo;
15319         this.modified = [];
15320         this.fireEvent('metachange', this, this.reader.meta);
15321     },
15322     
15323     moveIndex : function(data, type)
15324     {
15325         var index = this.indexOf(data);
15326         
15327         var newIndex = index + type;
15328         
15329         this.remove(data);
15330         
15331         this.insert(newIndex, data);
15332         
15333     }
15334 });/*
15335  * Based on:
15336  * Ext JS Library 1.1.1
15337  * Copyright(c) 2006-2007, Ext JS, LLC.
15338  *
15339  * Originally Released Under LGPL - original licence link has changed is not relivant.
15340  *
15341  * Fork - LGPL
15342  * <script type="text/javascript">
15343  */
15344
15345 /**
15346  * @class Roo.data.SimpleStore
15347  * @extends Roo.data.Store
15348  * Small helper class to make creating Stores from Array data easier.
15349  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15350  * @cfg {Array} fields An array of field definition objects, or field name strings.
15351  * @cfg {Object} an existing reader (eg. copied from another store)
15352  * @cfg {Array} data The multi-dimensional array of data
15353  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15354  * @cfg {Roo.data.Reader} reader  [not-required] 
15355  * @constructor
15356  * @param {Object} config
15357  */
15358 Roo.data.SimpleStore = function(config)
15359 {
15360     Roo.data.SimpleStore.superclass.constructor.call(this, {
15361         isLocal : true,
15362         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15363                 id: config.id
15364             },
15365             Roo.data.Record.create(config.fields)
15366         ),
15367         proxy : new Roo.data.MemoryProxy(config.data)
15368     });
15369     this.load();
15370 };
15371 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15372  * Based on:
15373  * Ext JS Library 1.1.1
15374  * Copyright(c) 2006-2007, Ext JS, LLC.
15375  *
15376  * Originally Released Under LGPL - original licence link has changed is not relivant.
15377  *
15378  * Fork - LGPL
15379  * <script type="text/javascript">
15380  */
15381
15382 /**
15383 /**
15384  * @extends Roo.data.Store
15385  * @class Roo.data.JsonStore
15386  * Small helper class to make creating Stores for JSON data easier. <br/>
15387 <pre><code>
15388 var store = new Roo.data.JsonStore({
15389     url: 'get-images.php',
15390     root: 'images',
15391     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15392 });
15393 </code></pre>
15394  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15395  * JsonReader and HttpProxy (unless inline data is provided).</b>
15396  * @cfg {Array} fields An array of field definition objects, or field name strings.
15397  * @constructor
15398  * @param {Object} config
15399  */
15400 Roo.data.JsonStore = function(c){
15401     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15402         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15403         reader: new Roo.data.JsonReader(c, c.fields)
15404     }));
15405 };
15406 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15407  * Based on:
15408  * Ext JS Library 1.1.1
15409  * Copyright(c) 2006-2007, Ext JS, LLC.
15410  *
15411  * Originally Released Under LGPL - original licence link has changed is not relivant.
15412  *
15413  * Fork - LGPL
15414  * <script type="text/javascript">
15415  */
15416
15417  
15418 Roo.data.Field = function(config){
15419     if(typeof config == "string"){
15420         config = {name: config};
15421     }
15422     Roo.apply(this, config);
15423     
15424     if(!this.type){
15425         this.type = "auto";
15426     }
15427     
15428     var st = Roo.data.SortTypes;
15429     // named sortTypes are supported, here we look them up
15430     if(typeof this.sortType == "string"){
15431         this.sortType = st[this.sortType];
15432     }
15433     
15434     // set default sortType for strings and dates
15435     if(!this.sortType){
15436         switch(this.type){
15437             case "string":
15438                 this.sortType = st.asUCString;
15439                 break;
15440             case "date":
15441                 this.sortType = st.asDate;
15442                 break;
15443             default:
15444                 this.sortType = st.none;
15445         }
15446     }
15447
15448     // define once
15449     var stripRe = /[\$,%]/g;
15450
15451     // prebuilt conversion function for this field, instead of
15452     // switching every time we're reading a value
15453     if(!this.convert){
15454         var cv, dateFormat = this.dateFormat;
15455         switch(this.type){
15456             case "":
15457             case "auto":
15458             case undefined:
15459                 cv = function(v){ return v; };
15460                 break;
15461             case "string":
15462                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15463                 break;
15464             case "int":
15465                 cv = function(v){
15466                     return v !== undefined && v !== null && v !== '' ?
15467                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15468                     };
15469                 break;
15470             case "float":
15471                 cv = function(v){
15472                     return v !== undefined && v !== null && v !== '' ?
15473                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15474                     };
15475                 break;
15476             case "bool":
15477             case "boolean":
15478                 cv = function(v){ return v === true || v === "true" || v == 1; };
15479                 break;
15480             case "date":
15481                 cv = function(v){
15482                     if(!v){
15483                         return '';
15484                     }
15485                     if(v instanceof Date){
15486                         return v;
15487                     }
15488                     if(dateFormat){
15489                         if(dateFormat == "timestamp"){
15490                             return new Date(v*1000);
15491                         }
15492                         return Date.parseDate(v, dateFormat);
15493                     }
15494                     var parsed = Date.parse(v);
15495                     return parsed ? new Date(parsed) : null;
15496                 };
15497              break;
15498             
15499         }
15500         this.convert = cv;
15501     }
15502 };
15503
15504 Roo.data.Field.prototype = {
15505     dateFormat: null,
15506     defaultValue: "",
15507     mapping: null,
15508     sortType : null,
15509     sortDir : "ASC"
15510 };/*
15511  * Based on:
15512  * Ext JS Library 1.1.1
15513  * Copyright(c) 2006-2007, Ext JS, LLC.
15514  *
15515  * Originally Released Under LGPL - original licence link has changed is not relivant.
15516  *
15517  * Fork - LGPL
15518  * <script type="text/javascript">
15519  */
15520  
15521 // Base class for reading structured data from a data source.  This class is intended to be
15522 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15523
15524 /**
15525  * @class Roo.data.DataReader
15526  * @abstract
15527  * Base class for reading structured data from a data source.  This class is intended to be
15528  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15529  */
15530
15531 Roo.data.DataReader = function(meta, recordType){
15532     
15533     this.meta = meta;
15534     
15535     this.recordType = recordType instanceof Array ? 
15536         Roo.data.Record.create(recordType) : recordType;
15537 };
15538
15539 Roo.data.DataReader.prototype = {
15540     
15541     
15542     readerType : 'Data',
15543      /**
15544      * Create an empty record
15545      * @param {Object} data (optional) - overlay some values
15546      * @return {Roo.data.Record} record created.
15547      */
15548     newRow :  function(d) {
15549         var da =  {};
15550         this.recordType.prototype.fields.each(function(c) {
15551             switch( c.type) {
15552                 case 'int' : da[c.name] = 0; break;
15553                 case 'date' : da[c.name] = new Date(); break;
15554                 case 'float' : da[c.name] = 0.0; break;
15555                 case 'boolean' : da[c.name] = false; break;
15556                 default : da[c.name] = ""; break;
15557             }
15558             
15559         });
15560         return new this.recordType(Roo.apply(da, d));
15561     }
15562     
15563     
15564 };/*
15565  * Based on:
15566  * Ext JS Library 1.1.1
15567  * Copyright(c) 2006-2007, Ext JS, LLC.
15568  *
15569  * Originally Released Under LGPL - original licence link has changed is not relivant.
15570  *
15571  * Fork - LGPL
15572  * <script type="text/javascript">
15573  */
15574
15575 /**
15576  * @class Roo.data.DataProxy
15577  * @extends Roo.data.Observable
15578  * @abstract
15579  * This class is an abstract base class for implementations which provide retrieval of
15580  * unformatted data objects.<br>
15581  * <p>
15582  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15583  * (of the appropriate type which knows how to parse the data object) to provide a block of
15584  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15585  * <p>
15586  * Custom implementations must implement the load method as described in
15587  * {@link Roo.data.HttpProxy#load}.
15588  */
15589 Roo.data.DataProxy = function(){
15590     this.addEvents({
15591         /**
15592          * @event beforeload
15593          * Fires before a network request is made to retrieve a data object.
15594          * @param {Object} This DataProxy object.
15595          * @param {Object} params The params parameter to the load function.
15596          */
15597         beforeload : true,
15598         /**
15599          * @event load
15600          * Fires before the load method's callback is called.
15601          * @param {Object} This DataProxy object.
15602          * @param {Object} o The data object.
15603          * @param {Object} arg The callback argument object passed to the load function.
15604          */
15605         load : true,
15606         /**
15607          * @event loadexception
15608          * Fires if an Exception occurs during data retrieval.
15609          * @param {Object} This DataProxy object.
15610          * @param {Object} o The data object.
15611          * @param {Object} arg The callback argument object passed to the load function.
15612          * @param {Object} e The Exception.
15613          */
15614         loadexception : true
15615     });
15616     Roo.data.DataProxy.superclass.constructor.call(this);
15617 };
15618
15619 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15620
15621     /**
15622      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15623      */
15624 /*
15625  * Based on:
15626  * Ext JS Library 1.1.1
15627  * Copyright(c) 2006-2007, Ext JS, LLC.
15628  *
15629  * Originally Released Under LGPL - original licence link has changed is not relivant.
15630  *
15631  * Fork - LGPL
15632  * <script type="text/javascript">
15633  */
15634 /**
15635  * @class Roo.data.MemoryProxy
15636  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15637  * to the Reader when its load method is called.
15638  * @constructor
15639  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15640  */
15641 Roo.data.MemoryProxy = function(data){
15642     if (data.data) {
15643         data = data.data;
15644     }
15645     Roo.data.MemoryProxy.superclass.constructor.call(this);
15646     this.data = data;
15647 };
15648
15649 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15650     
15651     /**
15652      * Load data from the requested source (in this case an in-memory
15653      * data object passed to the constructor), read the data object into
15654      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15655      * process that block using the passed callback.
15656      * @param {Object} params This parameter is not used by the MemoryProxy class.
15657      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15658      * object into a block of Roo.data.Records.
15659      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15660      * The function must be passed <ul>
15661      * <li>The Record block object</li>
15662      * <li>The "arg" argument from the load function</li>
15663      * <li>A boolean success indicator</li>
15664      * </ul>
15665      * @param {Object} scope The scope in which to call the callback
15666      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15667      */
15668     load : function(params, reader, callback, scope, arg){
15669         params = params || {};
15670         var result;
15671         try {
15672             result = reader.readRecords(params.data ? params.data :this.data);
15673         }catch(e){
15674             this.fireEvent("loadexception", this, arg, null, e);
15675             callback.call(scope, null, arg, false);
15676             return;
15677         }
15678         callback.call(scope, result, arg, true);
15679     },
15680     
15681     // private
15682     update : function(params, records){
15683         
15684     }
15685 });/*
15686  * Based on:
15687  * Ext JS Library 1.1.1
15688  * Copyright(c) 2006-2007, Ext JS, LLC.
15689  *
15690  * Originally Released Under LGPL - original licence link has changed is not relivant.
15691  *
15692  * Fork - LGPL
15693  * <script type="text/javascript">
15694  */
15695 /**
15696  * @class Roo.data.HttpProxy
15697  * @extends Roo.data.DataProxy
15698  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15699  * configured to reference a certain URL.<br><br>
15700  * <p>
15701  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15702  * from which the running page was served.<br><br>
15703  * <p>
15704  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15705  * <p>
15706  * Be aware that to enable the browser to parse an XML document, the server must set
15707  * the Content-Type header in the HTTP response to "text/xml".
15708  * @constructor
15709  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15710  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15711  * will be used to make the request.
15712  */
15713 Roo.data.HttpProxy = function(conn){
15714     Roo.data.HttpProxy.superclass.constructor.call(this);
15715     // is conn a conn config or a real conn?
15716     this.conn = conn;
15717     this.useAjax = !conn || !conn.events;
15718   
15719 };
15720
15721 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15722     // thse are take from connection...
15723     
15724     /**
15725      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15726      */
15727     /**
15728      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15729      * extra parameters to each request made by this object. (defaults to undefined)
15730      */
15731     /**
15732      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15733      *  to each request made by this object. (defaults to undefined)
15734      */
15735     /**
15736      * @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)
15737      */
15738     /**
15739      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15740      */
15741      /**
15742      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15743      * @type Boolean
15744      */
15745   
15746
15747     /**
15748      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15749      * @type Boolean
15750      */
15751     /**
15752      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15753      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15754      * a finer-grained basis than the DataProxy events.
15755      */
15756     getConnection : function(){
15757         return this.useAjax ? Roo.Ajax : this.conn;
15758     },
15759
15760     /**
15761      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15762      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15763      * process that block using the passed callback.
15764      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15765      * for the request to the remote server.
15766      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15767      * object into a block of Roo.data.Records.
15768      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15769      * The function must be passed <ul>
15770      * <li>The Record block object</li>
15771      * <li>The "arg" argument from the load function</li>
15772      * <li>A boolean success indicator</li>
15773      * </ul>
15774      * @param {Object} scope The scope in which to call the callback
15775      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15776      */
15777     load : function(params, reader, callback, scope, arg){
15778         if(this.fireEvent("beforeload", this, params) !== false){
15779             var  o = {
15780                 params : params || {},
15781                 request: {
15782                     callback : callback,
15783                     scope : scope,
15784                     arg : arg
15785                 },
15786                 reader: reader,
15787                 callback : this.loadResponse,
15788                 scope: this
15789             };
15790             if(this.useAjax){
15791                 Roo.applyIf(o, this.conn);
15792                 if(this.activeRequest){
15793                     Roo.Ajax.abort(this.activeRequest);
15794                 }
15795                 this.activeRequest = Roo.Ajax.request(o);
15796             }else{
15797                 this.conn.request(o);
15798             }
15799         }else{
15800             callback.call(scope||this, null, arg, false);
15801         }
15802     },
15803
15804     // private
15805     loadResponse : function(o, success, response){
15806         delete this.activeRequest;
15807         if(!success){
15808             this.fireEvent("loadexception", this, o, response);
15809             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15810             return;
15811         }
15812         var result;
15813         try {
15814             result = o.reader.read(response);
15815         }catch(e){
15816             this.fireEvent("loadexception", this, o, response, e);
15817             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15818             return;
15819         }
15820         
15821         this.fireEvent("load", this, o, o.request.arg);
15822         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15823     },
15824
15825     // private
15826     update : function(dataSet){
15827
15828     },
15829
15830     // private
15831     updateResponse : function(dataSet){
15832
15833     }
15834 });/*
15835  * Based on:
15836  * Ext JS Library 1.1.1
15837  * Copyright(c) 2006-2007, Ext JS, LLC.
15838  *
15839  * Originally Released Under LGPL - original licence link has changed is not relivant.
15840  *
15841  * Fork - LGPL
15842  * <script type="text/javascript">
15843  */
15844
15845 /**
15846  * @class Roo.data.ScriptTagProxy
15847  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15848  * other than the originating domain of the running page.<br><br>
15849  * <p>
15850  * <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
15851  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15852  * <p>
15853  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15854  * source code that is used as the source inside a &lt;script> tag.<br><br>
15855  * <p>
15856  * In order for the browser to process the returned data, the server must wrap the data object
15857  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15858  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15859  * depending on whether the callback name was passed:
15860  * <p>
15861  * <pre><code>
15862 boolean scriptTag = false;
15863 String cb = request.getParameter("callback");
15864 if (cb != null) {
15865     scriptTag = true;
15866     response.setContentType("text/javascript");
15867 } else {
15868     response.setContentType("application/x-json");
15869 }
15870 Writer out = response.getWriter();
15871 if (scriptTag) {
15872     out.write(cb + "(");
15873 }
15874 out.print(dataBlock.toJsonString());
15875 if (scriptTag) {
15876     out.write(");");
15877 }
15878 </pre></code>
15879  *
15880  * @constructor
15881  * @param {Object} config A configuration object.
15882  */
15883 Roo.data.ScriptTagProxy = function(config){
15884     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15885     Roo.apply(this, config);
15886     this.head = document.getElementsByTagName("head")[0];
15887 };
15888
15889 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15890
15891 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15892     /**
15893      * @cfg {String} url The URL from which to request the data object.
15894      */
15895     /**
15896      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15897      */
15898     timeout : 30000,
15899     /**
15900      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15901      * the server the name of the callback function set up by the load call to process the returned data object.
15902      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15903      * javascript output which calls this named function passing the data object as its only parameter.
15904      */
15905     callbackParam : "callback",
15906     /**
15907      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15908      * name to the request.
15909      */
15910     nocache : true,
15911
15912     /**
15913      * Load data from the configured URL, read the data object into
15914      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15915      * process that block using the passed callback.
15916      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15917      * for the request to the remote server.
15918      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15919      * object into a block of Roo.data.Records.
15920      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15921      * The function must be passed <ul>
15922      * <li>The Record block object</li>
15923      * <li>The "arg" argument from the load function</li>
15924      * <li>A boolean success indicator</li>
15925      * </ul>
15926      * @param {Object} scope The scope in which to call the callback
15927      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15928      */
15929     load : function(params, reader, callback, scope, arg){
15930         if(this.fireEvent("beforeload", this, params) !== false){
15931
15932             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15933
15934             var url = this.url;
15935             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15936             if(this.nocache){
15937                 url += "&_dc=" + (new Date().getTime());
15938             }
15939             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15940             var trans = {
15941                 id : transId,
15942                 cb : "stcCallback"+transId,
15943                 scriptId : "stcScript"+transId,
15944                 params : params,
15945                 arg : arg,
15946                 url : url,
15947                 callback : callback,
15948                 scope : scope,
15949                 reader : reader
15950             };
15951             var conn = this;
15952
15953             window[trans.cb] = function(o){
15954                 conn.handleResponse(o, trans);
15955             };
15956
15957             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15958
15959             if(this.autoAbort !== false){
15960                 this.abort();
15961             }
15962
15963             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15964
15965             var script = document.createElement("script");
15966             script.setAttribute("src", url);
15967             script.setAttribute("type", "text/javascript");
15968             script.setAttribute("id", trans.scriptId);
15969             this.head.appendChild(script);
15970
15971             this.trans = trans;
15972         }else{
15973             callback.call(scope||this, null, arg, false);
15974         }
15975     },
15976
15977     // private
15978     isLoading : function(){
15979         return this.trans ? true : false;
15980     },
15981
15982     /**
15983      * Abort the current server request.
15984      */
15985     abort : function(){
15986         if(this.isLoading()){
15987             this.destroyTrans(this.trans);
15988         }
15989     },
15990
15991     // private
15992     destroyTrans : function(trans, isLoaded){
15993         this.head.removeChild(document.getElementById(trans.scriptId));
15994         clearTimeout(trans.timeoutId);
15995         if(isLoaded){
15996             window[trans.cb] = undefined;
15997             try{
15998                 delete window[trans.cb];
15999             }catch(e){}
16000         }else{
16001             // if hasn't been loaded, wait for load to remove it to prevent script error
16002             window[trans.cb] = function(){
16003                 window[trans.cb] = undefined;
16004                 try{
16005                     delete window[trans.cb];
16006                 }catch(e){}
16007             };
16008         }
16009     },
16010
16011     // private
16012     handleResponse : function(o, trans){
16013         this.trans = false;
16014         this.destroyTrans(trans, true);
16015         var result;
16016         try {
16017             result = trans.reader.readRecords(o);
16018         }catch(e){
16019             this.fireEvent("loadexception", this, o, trans.arg, e);
16020             trans.callback.call(trans.scope||window, null, trans.arg, false);
16021             return;
16022         }
16023         this.fireEvent("load", this, o, trans.arg);
16024         trans.callback.call(trans.scope||window, result, trans.arg, true);
16025     },
16026
16027     // private
16028     handleFailure : function(trans){
16029         this.trans = false;
16030         this.destroyTrans(trans, false);
16031         this.fireEvent("loadexception", this, null, trans.arg);
16032         trans.callback.call(trans.scope||window, null, trans.arg, false);
16033     }
16034 });/*
16035  * Based on:
16036  * Ext JS Library 1.1.1
16037  * Copyright(c) 2006-2007, Ext JS, LLC.
16038  *
16039  * Originally Released Under LGPL - original licence link has changed is not relivant.
16040  *
16041  * Fork - LGPL
16042  * <script type="text/javascript">
16043  */
16044
16045 /**
16046  * @class Roo.data.JsonReader
16047  * @extends Roo.data.DataReader
16048  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16049  * based on mappings in a provided Roo.data.Record constructor.
16050  * 
16051  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16052  * in the reply previously. 
16053  * 
16054  * <p>
16055  * Example code:
16056  * <pre><code>
16057 var RecordDef = Roo.data.Record.create([
16058     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16059     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16060 ]);
16061 var myReader = new Roo.data.JsonReader({
16062     totalProperty: "results",    // The property which contains the total dataset size (optional)
16063     root: "rows",                // The property which contains an Array of row objects
16064     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16065 }, RecordDef);
16066 </code></pre>
16067  * <p>
16068  * This would consume a JSON file like this:
16069  * <pre><code>
16070 { 'results': 2, 'rows': [
16071     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16072     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16073 }
16074 </code></pre>
16075  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16076  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16077  * paged from the remote server.
16078  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16079  * @cfg {String} root name of the property which contains the Array of row objects.
16080  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16081  * @cfg {Array} fields Array of field definition objects
16082  * @constructor
16083  * Create a new JsonReader
16084  * @param {Object} meta Metadata configuration options
16085  * @param {Object} recordType Either an Array of field definition objects,
16086  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16087  */
16088 Roo.data.JsonReader = function(meta, recordType){
16089     
16090     meta = meta || {};
16091     // set some defaults:
16092     Roo.applyIf(meta, {
16093         totalProperty: 'total',
16094         successProperty : 'success',
16095         root : 'data',
16096         id : 'id'
16097     });
16098     
16099     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16100 };
16101 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16102     
16103     readerType : 'Json',
16104     
16105     /**
16106      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16107      * Used by Store query builder to append _requestMeta to params.
16108      * 
16109      */
16110     metaFromRemote : false,
16111     /**
16112      * This method is only used by a DataProxy which has retrieved data from a remote server.
16113      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16114      * @return {Object} data A data block which is used by an Roo.data.Store object as
16115      * a cache of Roo.data.Records.
16116      */
16117     read : function(response){
16118         var json = response.responseText;
16119        
16120         var o = /* eval:var:o */ eval("("+json+")");
16121         if(!o) {
16122             throw {message: "JsonReader.read: Json object not found"};
16123         }
16124         
16125         if(o.metaData){
16126             
16127             delete this.ef;
16128             this.metaFromRemote = true;
16129             this.meta = o.metaData;
16130             this.recordType = Roo.data.Record.create(o.metaData.fields);
16131             this.onMetaChange(this.meta, this.recordType, o);
16132         }
16133         return this.readRecords(o);
16134     },
16135
16136     // private function a store will implement
16137     onMetaChange : function(meta, recordType, o){
16138
16139     },
16140
16141     /**
16142          * @ignore
16143          */
16144     simpleAccess: function(obj, subsc) {
16145         return obj[subsc];
16146     },
16147
16148         /**
16149          * @ignore
16150          */
16151     getJsonAccessor: function(){
16152         var re = /[\[\.]/;
16153         return function(expr) {
16154             try {
16155                 return(re.test(expr))
16156                     ? new Function("obj", "return obj." + expr)
16157                     : function(obj){
16158                         return obj[expr];
16159                     };
16160             } catch(e){}
16161             return Roo.emptyFn;
16162         };
16163     }(),
16164
16165     /**
16166      * Create a data block containing Roo.data.Records from an XML document.
16167      * @param {Object} o An object which contains an Array of row objects in the property specified
16168      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16169      * which contains the total size of the dataset.
16170      * @return {Object} data A data block which is used by an Roo.data.Store object as
16171      * a cache of Roo.data.Records.
16172      */
16173     readRecords : function(o){
16174         /**
16175          * After any data loads, the raw JSON data is available for further custom processing.
16176          * @type Object
16177          */
16178         this.o = o;
16179         var s = this.meta, Record = this.recordType,
16180             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16181
16182 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16183         if (!this.ef) {
16184             if(s.totalProperty) {
16185                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16186                 }
16187                 if(s.successProperty) {
16188                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16189                 }
16190                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16191                 if (s.id) {
16192                         var g = this.getJsonAccessor(s.id);
16193                         this.getId = function(rec) {
16194                                 var r = g(rec);  
16195                                 return (r === undefined || r === "") ? null : r;
16196                         };
16197                 } else {
16198                         this.getId = function(){return null;};
16199                 }
16200             this.ef = [];
16201             for(var jj = 0; jj < fl; jj++){
16202                 f = fi[jj];
16203                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16204                 this.ef[jj] = this.getJsonAccessor(map);
16205             }
16206         }
16207
16208         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16209         if(s.totalProperty){
16210             var vt = parseInt(this.getTotal(o), 10);
16211             if(!isNaN(vt)){
16212                 totalRecords = vt;
16213             }
16214         }
16215         if(s.successProperty){
16216             var vs = this.getSuccess(o);
16217             if(vs === false || vs === 'false'){
16218                 success = false;
16219             }
16220         }
16221         var records = [];
16222         for(var i = 0; i < c; i++){
16223                 var n = root[i];
16224             var values = {};
16225             var id = this.getId(n);
16226             for(var j = 0; j < fl; j++){
16227                 f = fi[j];
16228             var v = this.ef[j](n);
16229             if (!f.convert) {
16230                 Roo.log('missing convert for ' + f.name);
16231                 Roo.log(f);
16232                 continue;
16233             }
16234             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16235             }
16236             var record = new Record(values, id);
16237             record.json = n;
16238             records[i] = record;
16239         }
16240         return {
16241             raw : o,
16242             success : success,
16243             records : records,
16244             totalRecords : totalRecords
16245         };
16246     },
16247     // used when loading children.. @see loadDataFromChildren
16248     toLoadData: function(rec)
16249     {
16250         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16251         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16252         return { data : data, total : data.length };
16253         
16254     }
16255 });/*
16256  * Based on:
16257  * Ext JS Library 1.1.1
16258  * Copyright(c) 2006-2007, Ext JS, LLC.
16259  *
16260  * Originally Released Under LGPL - original licence link has changed is not relivant.
16261  *
16262  * Fork - LGPL
16263  * <script type="text/javascript">
16264  */
16265
16266 /**
16267  * @class Roo.data.ArrayReader
16268  * @extends Roo.data.DataReader
16269  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16270  * Each element of that Array represents a row of data fields. The
16271  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16272  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16273  * <p>
16274  * Example code:.
16275  * <pre><code>
16276 var RecordDef = Roo.data.Record.create([
16277     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16278     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16279 ]);
16280 var myReader = new Roo.data.ArrayReader({
16281     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16282 }, RecordDef);
16283 </code></pre>
16284  * <p>
16285  * This would consume an Array like this:
16286  * <pre><code>
16287 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16288   </code></pre>
16289  
16290  * @constructor
16291  * Create a new JsonReader
16292  * @param {Object} meta Metadata configuration options.
16293  * @param {Object|Array} recordType Either an Array of field definition objects
16294  * 
16295  * @cfg {Array} fields Array of field definition objects
16296  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16297  * as specified to {@link Roo.data.Record#create},
16298  * or an {@link Roo.data.Record} object
16299  *
16300  * 
16301  * created using {@link Roo.data.Record#create}.
16302  */
16303 Roo.data.ArrayReader = function(meta, recordType)
16304 {    
16305     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16306 };
16307
16308 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16309     
16310       /**
16311      * Create a data block containing Roo.data.Records from an XML document.
16312      * @param {Object} o An Array of row objects which represents the dataset.
16313      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16314      * a cache of Roo.data.Records.
16315      */
16316     readRecords : function(o)
16317     {
16318         var sid = this.meta ? this.meta.id : null;
16319         var recordType = this.recordType, fields = recordType.prototype.fields;
16320         var records = [];
16321         var root = o;
16322         for(var i = 0; i < root.length; i++){
16323             var n = root[i];
16324             var values = {};
16325             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16326             for(var j = 0, jlen = fields.length; j < jlen; j++){
16327                 var f = fields.items[j];
16328                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16329                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16330                 v = f.convert(v);
16331                 values[f.name] = v;
16332             }
16333             var record = new recordType(values, id);
16334             record.json = n;
16335             records[records.length] = record;
16336         }
16337         return {
16338             records : records,
16339             totalRecords : records.length
16340         };
16341     },
16342     // used when loading children.. @see loadDataFromChildren
16343     toLoadData: function(rec)
16344     {
16345         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16346         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16347         
16348     }
16349     
16350     
16351 });/*
16352  * - LGPL
16353  * * 
16354  */
16355
16356 /**
16357  * @class Roo.bootstrap.ComboBox
16358  * @extends Roo.bootstrap.TriggerField
16359  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16360  * @cfg {Boolean} append (true|false) default false
16361  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16362  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16363  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16364  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16365  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16366  * @cfg {Boolean} animate default true
16367  * @cfg {Boolean} emptyResultText only for touch device
16368  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16369  * @cfg {String} emptyTitle default ''
16370  * @cfg {Number} width fixed with? experimental
16371  * @constructor
16372  * Create a new ComboBox.
16373  * @param {Object} config Configuration options
16374  */
16375 Roo.bootstrap.ComboBox = function(config){
16376     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16377     this.addEvents({
16378         /**
16379          * @event expand
16380          * Fires when the dropdown list is expanded
16381         * @param {Roo.bootstrap.ComboBox} combo This combo box
16382         */
16383         'expand' : true,
16384         /**
16385          * @event collapse
16386          * Fires when the dropdown list is collapsed
16387         * @param {Roo.bootstrap.ComboBox} combo This combo box
16388         */
16389         'collapse' : true,
16390         /**
16391          * @event beforeselect
16392          * Fires before a list item is selected. Return false to cancel the selection.
16393         * @param {Roo.bootstrap.ComboBox} combo This combo box
16394         * @param {Roo.data.Record} record The data record returned from the underlying store
16395         * @param {Number} index The index of the selected item in the dropdown list
16396         */
16397         'beforeselect' : true,
16398         /**
16399          * @event select
16400          * Fires when a list item is selected
16401         * @param {Roo.bootstrap.ComboBox} combo This combo box
16402         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16403         * @param {Number} index The index of the selected item in the dropdown list
16404         */
16405         'select' : true,
16406         /**
16407          * @event beforequery
16408          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16409          * The event object passed has these properties:
16410         * @param {Roo.bootstrap.ComboBox} combo This combo box
16411         * @param {String} query The query
16412         * @param {Boolean} forceAll true to force "all" query
16413         * @param {Boolean} cancel true to cancel the query
16414         * @param {Object} e The query event object
16415         */
16416         'beforequery': true,
16417          /**
16418          * @event add
16419          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16420         * @param {Roo.bootstrap.ComboBox} combo This combo box
16421         */
16422         'add' : true,
16423         /**
16424          * @event edit
16425          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16426         * @param {Roo.bootstrap.ComboBox} combo This combo box
16427         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16428         */
16429         'edit' : true,
16430         /**
16431          * @event remove
16432          * Fires when the remove value from the combobox array
16433         * @param {Roo.bootstrap.ComboBox} combo This combo box
16434         */
16435         'remove' : true,
16436         /**
16437          * @event afterremove
16438          * Fires when the remove value from the combobox array
16439         * @param {Roo.bootstrap.ComboBox} combo This combo box
16440         */
16441         'afterremove' : true,
16442         /**
16443          * @event specialfilter
16444          * Fires when specialfilter
16445             * @param {Roo.bootstrap.ComboBox} combo This combo box
16446             */
16447         'specialfilter' : true,
16448         /**
16449          * @event tick
16450          * Fires when tick the element
16451             * @param {Roo.bootstrap.ComboBox} combo This combo box
16452             */
16453         'tick' : true,
16454         /**
16455          * @event touchviewdisplay
16456          * Fires when touch view require special display (default is using displayField)
16457             * @param {Roo.bootstrap.ComboBox} combo This combo box
16458             * @param {Object} cfg set html .
16459             */
16460         'touchviewdisplay' : true
16461         
16462     });
16463     
16464     this.item = [];
16465     this.tickItems = [];
16466     
16467     this.selectedIndex = -1;
16468     if(this.mode == 'local'){
16469         if(config.queryDelay === undefined){
16470             this.queryDelay = 10;
16471         }
16472         if(config.minChars === undefined){
16473             this.minChars = 0;
16474         }
16475     }
16476 };
16477
16478 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16479      
16480     /**
16481      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16482      * rendering into an Roo.Editor, defaults to false)
16483      */
16484     /**
16485      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16486      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16487      */
16488     /**
16489      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16490      */
16491     /**
16492      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16493      * the dropdown list (defaults to undefined, with no header element)
16494      */
16495
16496      /**
16497      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16498      */
16499      
16500      /**
16501      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16502      */
16503     listWidth: undefined,
16504     /**
16505      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16506      * mode = 'remote' or 'text' if mode = 'local')
16507      */
16508     displayField: undefined,
16509     
16510     /**
16511      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16512      * mode = 'remote' or 'value' if mode = 'local'). 
16513      * Note: use of a valueField requires the user make a selection
16514      * in order for a value to be mapped.
16515      */
16516     valueField: undefined,
16517     /**
16518      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16519      */
16520     modalTitle : '',
16521     
16522     /**
16523      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16524      * field's data value (defaults to the underlying DOM element's name)
16525      */
16526     hiddenName: undefined,
16527     /**
16528      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16529      */
16530     listClass: '',
16531     /**
16532      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16533      */
16534     selectedClass: 'active',
16535     
16536     /**
16537      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16538      */
16539     shadow:'sides',
16540     /**
16541      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16542      * anchor positions (defaults to 'tl-bl')
16543      */
16544     listAlign: 'tl-bl?',
16545     /**
16546      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16547      */
16548     maxHeight: 300,
16549     /**
16550      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16551      * query specified by the allQuery config option (defaults to 'query')
16552      */
16553     triggerAction: 'query',
16554     /**
16555      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16556      * (defaults to 4, does not apply if editable = false)
16557      */
16558     minChars : 4,
16559     /**
16560      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16561      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16562      */
16563     typeAhead: false,
16564     /**
16565      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16566      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16567      */
16568     queryDelay: 500,
16569     /**
16570      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16571      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16572      */
16573     pageSize: 0,
16574     /**
16575      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16576      * when editable = true (defaults to false)
16577      */
16578     selectOnFocus:false,
16579     /**
16580      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16581      */
16582     queryParam: 'query',
16583     /**
16584      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16585      * when mode = 'remote' (defaults to 'Loading...')
16586      */
16587     loadingText: 'Loading...',
16588     /**
16589      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16590      */
16591     resizable: false,
16592     /**
16593      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16594      */
16595     handleHeight : 8,
16596     /**
16597      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16598      * traditional select (defaults to true)
16599      */
16600     editable: true,
16601     /**
16602      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16603      */
16604     allQuery: '',
16605     /**
16606      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16607      */
16608     mode: 'remote',
16609     /**
16610      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16611      * listWidth has a higher value)
16612      */
16613     minListWidth : 70,
16614     /**
16615      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16616      * allow the user to set arbitrary text into the field (defaults to false)
16617      */
16618     forceSelection:false,
16619     /**
16620      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16621      * if typeAhead = true (defaults to 250)
16622      */
16623     typeAheadDelay : 250,
16624     /**
16625      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16626      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16627      */
16628     valueNotFoundText : undefined,
16629     /**
16630      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16631      */
16632     blockFocus : false,
16633     
16634     /**
16635      * @cfg {Boolean} disableClear Disable showing of clear button.
16636      */
16637     disableClear : false,
16638     /**
16639      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16640      */
16641     alwaysQuery : false,
16642     
16643     /**
16644      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16645      */
16646     multiple : false,
16647     
16648     /**
16649      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16650      */
16651     invalidClass : "has-warning",
16652     
16653     /**
16654      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16655      */
16656     validClass : "has-success",
16657     
16658     /**
16659      * @cfg {Boolean} specialFilter (true|false) special filter default false
16660      */
16661     specialFilter : false,
16662     
16663     /**
16664      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16665      */
16666     mobileTouchView : true,
16667     
16668     /**
16669      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16670      */
16671     useNativeIOS : false,
16672     
16673     /**
16674      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16675      */
16676     mobile_restrict_height : false,
16677     
16678     ios_options : false,
16679     
16680     //private
16681     addicon : false,
16682     editicon: false,
16683     
16684     page: 0,
16685     hasQuery: false,
16686     append: false,
16687     loadNext: false,
16688     autoFocus : true,
16689     tickable : false,
16690     btnPosition : 'right',
16691     triggerList : true,
16692     showToggleBtn : true,
16693     animate : true,
16694     emptyResultText: 'Empty',
16695     triggerText : 'Select',
16696     emptyTitle : '',
16697     width : false,
16698     
16699     // element that contains real text value.. (when hidden is used..)
16700     
16701     getAutoCreate : function()
16702     {   
16703         var cfg = false;
16704         //render
16705         /*
16706          * Render classic select for iso
16707          */
16708         
16709         if(Roo.isIOS && this.useNativeIOS){
16710             cfg = this.getAutoCreateNativeIOS();
16711             return cfg;
16712         }
16713         
16714         /*
16715          * Touch Devices
16716          */
16717         
16718         if(Roo.isTouch && this.mobileTouchView){
16719             cfg = this.getAutoCreateTouchView();
16720             return cfg;;
16721         }
16722         
16723         /*
16724          *  Normal ComboBox
16725          */
16726         if(!this.tickable){
16727             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16728             return cfg;
16729         }
16730         
16731         /*
16732          *  ComboBox with tickable selections
16733          */
16734              
16735         var align = this.labelAlign || this.parentLabelAlign();
16736         
16737         cfg = {
16738             cls : 'form-group roo-combobox-tickable' //input-group
16739         };
16740         
16741         var btn_text_select = '';
16742         var btn_text_done = '';
16743         var btn_text_cancel = '';
16744         
16745         if (this.btn_text_show) {
16746             btn_text_select = 'Select';
16747             btn_text_done = 'Done';
16748             btn_text_cancel = 'Cancel'; 
16749         }
16750         
16751         var buttons = {
16752             tag : 'div',
16753             cls : 'tickable-buttons',
16754             cn : [
16755                 {
16756                     tag : 'button',
16757                     type : 'button',
16758                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16759                     //html : this.triggerText
16760                     html: btn_text_select
16761                 },
16762                 {
16763                     tag : 'button',
16764                     type : 'button',
16765                     name : 'ok',
16766                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16767                     //html : 'Done'
16768                     html: btn_text_done
16769                 },
16770                 {
16771                     tag : 'button',
16772                     type : 'button',
16773                     name : 'cancel',
16774                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16775                     //html : 'Cancel'
16776                     html: btn_text_cancel
16777                 }
16778             ]
16779         };
16780         
16781         if(this.editable){
16782             buttons.cn.unshift({
16783                 tag: 'input',
16784                 cls: 'roo-select2-search-field-input'
16785             });
16786         }
16787         
16788         var _this = this;
16789         
16790         Roo.each(buttons.cn, function(c){
16791             if (_this.size) {
16792                 c.cls += ' btn-' + _this.size;
16793             }
16794
16795             if (_this.disabled) {
16796                 c.disabled = true;
16797             }
16798         });
16799         
16800         var box = {
16801             tag: 'div',
16802             style : 'display: contents',
16803             cn: [
16804                 {
16805                     tag: 'input',
16806                     type : 'hidden',
16807                     cls: 'form-hidden-field'
16808                 },
16809                 {
16810                     tag: 'ul',
16811                     cls: 'roo-select2-choices',
16812                     cn:[
16813                         {
16814                             tag: 'li',
16815                             cls: 'roo-select2-search-field',
16816                             cn: [
16817                                 buttons
16818                             ]
16819                         }
16820                     ]
16821                 }
16822             ]
16823         };
16824         
16825         var combobox = {
16826             cls: 'roo-select2-container input-group roo-select2-container-multi',
16827             cn: [
16828                 
16829                 box
16830 //                {
16831 //                    tag: 'ul',
16832 //                    cls: 'typeahead typeahead-long dropdown-menu',
16833 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16834 //                }
16835             ]
16836         };
16837         
16838         if(this.hasFeedback && !this.allowBlank){
16839             
16840             var feedback = {
16841                 tag: 'span',
16842                 cls: 'glyphicon form-control-feedback'
16843             };
16844
16845             combobox.cn.push(feedback);
16846         }
16847         
16848         
16849         
16850         var indicator = {
16851             tag : 'i',
16852             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16853             tooltip : 'This field is required'
16854         };
16855         if (Roo.bootstrap.version == 4) {
16856             indicator = {
16857                 tag : 'i',
16858                 style : 'display:none'
16859             };
16860         }
16861         if (align ==='left' && this.fieldLabel.length) {
16862             
16863             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16864             
16865             cfg.cn = [
16866                 indicator,
16867                 {
16868                     tag: 'label',
16869                     'for' :  id,
16870                     cls : 'control-label col-form-label',
16871                     html : this.fieldLabel
16872
16873                 },
16874                 {
16875                     cls : "", 
16876                     cn: [
16877                         combobox
16878                     ]
16879                 }
16880
16881             ];
16882             
16883             var labelCfg = cfg.cn[1];
16884             var contentCfg = cfg.cn[2];
16885             
16886
16887             if(this.indicatorpos == 'right'){
16888                 
16889                 cfg.cn = [
16890                     {
16891                         tag: 'label',
16892                         'for' :  id,
16893                         cls : 'control-label col-form-label',
16894                         cn : [
16895                             {
16896                                 tag : 'span',
16897                                 html : this.fieldLabel
16898                             },
16899                             indicator
16900                         ]
16901                     },
16902                     {
16903                         cls : "",
16904                         cn: [
16905                             combobox
16906                         ]
16907                     }
16908
16909                 ];
16910                 
16911                 
16912                 
16913                 labelCfg = cfg.cn[0];
16914                 contentCfg = cfg.cn[1];
16915             
16916             }
16917             
16918             if(this.labelWidth > 12){
16919                 labelCfg.style = "width: " + this.labelWidth + 'px';
16920             }
16921             if(this.width * 1 > 0){
16922                 contentCfg.style = "width: " + this.width + 'px';
16923             }
16924             if(this.labelWidth < 13 && this.labelmd == 0){
16925                 this.labelmd = this.labelWidth;
16926             }
16927             
16928             if(this.labellg > 0){
16929                 labelCfg.cls += ' col-lg-' + this.labellg;
16930                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16931             }
16932             
16933             if(this.labelmd > 0){
16934                 labelCfg.cls += ' col-md-' + this.labelmd;
16935                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16936             }
16937             
16938             if(this.labelsm > 0){
16939                 labelCfg.cls += ' col-sm-' + this.labelsm;
16940                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16941             }
16942             
16943             if(this.labelxs > 0){
16944                 labelCfg.cls += ' col-xs-' + this.labelxs;
16945                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16946             }
16947                 
16948                 
16949         } else if ( this.fieldLabel.length) {
16950 //                Roo.log(" label");
16951                  cfg.cn = [
16952                    indicator,
16953                     {
16954                         tag: 'label',
16955                         //cls : 'input-group-addon',
16956                         html : this.fieldLabel
16957                     },
16958                     combobox
16959                 ];
16960                 
16961                 if(this.indicatorpos == 'right'){
16962                     cfg.cn = [
16963                         {
16964                             tag: 'label',
16965                             //cls : 'input-group-addon',
16966                             html : this.fieldLabel
16967                         },
16968                         indicator,
16969                         combobox
16970                     ];
16971                     
16972                 }
16973
16974         } else {
16975             
16976 //                Roo.log(" no label && no align");
16977                 cfg = combobox
16978                      
16979                 
16980         }
16981          
16982         var settings=this;
16983         ['xs','sm','md','lg'].map(function(size){
16984             if (settings[size]) {
16985                 cfg.cls += ' col-' + size + '-' + settings[size];
16986             }
16987         });
16988         
16989         return cfg;
16990         
16991     },
16992     
16993     _initEventsCalled : false,
16994     
16995     // private
16996     initEvents: function()
16997     {   
16998         if (this._initEventsCalled) { // as we call render... prevent looping...
16999             return;
17000         }
17001         this._initEventsCalled = true;
17002         
17003         if (!this.store) {
17004             throw "can not find store for combo";
17005         }
17006         
17007         this.indicator = this.indicatorEl();
17008         
17009         this.store = Roo.factory(this.store, Roo.data);
17010         this.store.parent = this;
17011         
17012         // if we are building from html. then this element is so complex, that we can not really
17013         // use the rendered HTML.
17014         // so we have to trash and replace the previous code.
17015         if (Roo.XComponent.build_from_html) {
17016             // remove this element....
17017             var e = this.el.dom, k=0;
17018             while (e ) { e = e.previousSibling;  ++k;}
17019
17020             this.el.remove();
17021             
17022             this.el=false;
17023             this.rendered = false;
17024             
17025             this.render(this.parent().getChildContainer(true), k);
17026         }
17027         
17028         if(Roo.isIOS && this.useNativeIOS){
17029             this.initIOSView();
17030             return;
17031         }
17032         
17033         /*
17034          * Touch Devices
17035          */
17036         
17037         if(Roo.isTouch && this.mobileTouchView){
17038             this.initTouchView();
17039             return;
17040         }
17041         
17042         if(this.tickable){
17043             this.initTickableEvents();
17044             return;
17045         }
17046         
17047         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17048         
17049         if(this.hiddenName){
17050             
17051             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17052             
17053             this.hiddenField.dom.value =
17054                 this.hiddenValue !== undefined ? this.hiddenValue :
17055                 this.value !== undefined ? this.value : '';
17056
17057             // prevent input submission
17058             this.el.dom.removeAttribute('name');
17059             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17060              
17061              
17062         }
17063         //if(Roo.isGecko){
17064         //    this.el.dom.setAttribute('autocomplete', 'off');
17065         //}
17066         
17067         var cls = 'x-combo-list';
17068         
17069         //this.list = new Roo.Layer({
17070         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17071         //});
17072         
17073         var _this = this;
17074         
17075         (function(){
17076             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17077             _this.list.setWidth(lw);
17078         }).defer(100);
17079         
17080         this.list.on('mouseover', this.onViewOver, this);
17081         this.list.on('mousemove', this.onViewMove, this);
17082         this.list.on('scroll', this.onViewScroll, this);
17083         
17084         /*
17085         this.list.swallowEvent('mousewheel');
17086         this.assetHeight = 0;
17087
17088         if(this.title){
17089             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17090             this.assetHeight += this.header.getHeight();
17091         }
17092
17093         this.innerList = this.list.createChild({cls:cls+'-inner'});
17094         this.innerList.on('mouseover', this.onViewOver, this);
17095         this.innerList.on('mousemove', this.onViewMove, this);
17096         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17097         
17098         if(this.allowBlank && !this.pageSize && !this.disableClear){
17099             this.footer = this.list.createChild({cls:cls+'-ft'});
17100             this.pageTb = new Roo.Toolbar(this.footer);
17101            
17102         }
17103         if(this.pageSize){
17104             this.footer = this.list.createChild({cls:cls+'-ft'});
17105             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17106                     {pageSize: this.pageSize});
17107             
17108         }
17109         
17110         if (this.pageTb && this.allowBlank && !this.disableClear) {
17111             var _this = this;
17112             this.pageTb.add(new Roo.Toolbar.Fill(), {
17113                 cls: 'x-btn-icon x-btn-clear',
17114                 text: '&#160;',
17115                 handler: function()
17116                 {
17117                     _this.collapse();
17118                     _this.clearValue();
17119                     _this.onSelect(false, -1);
17120                 }
17121             });
17122         }
17123         if (this.footer) {
17124             this.assetHeight += this.footer.getHeight();
17125         }
17126         */
17127             
17128         if(!this.tpl){
17129             this.tpl = Roo.bootstrap.version == 4 ?
17130                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17131                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17132         }
17133
17134         this.view = new Roo.View(this.list, this.tpl, {
17135             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17136         });
17137         //this.view.wrapEl.setDisplayed(false);
17138         this.view.on('click', this.onViewClick, this);
17139         
17140         
17141         this.store.on('beforeload', this.onBeforeLoad, this);
17142         this.store.on('load', this.onLoad, this);
17143         this.store.on('loadexception', this.onLoadException, this);
17144         /*
17145         if(this.resizable){
17146             this.resizer = new Roo.Resizable(this.list,  {
17147                pinned:true, handles:'se'
17148             });
17149             this.resizer.on('resize', function(r, w, h){
17150                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17151                 this.listWidth = w;
17152                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17153                 this.restrictHeight();
17154             }, this);
17155             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17156         }
17157         */
17158         if(!this.editable){
17159             this.editable = true;
17160             this.setEditable(false);
17161         }
17162         
17163         /*
17164         
17165         if (typeof(this.events.add.listeners) != 'undefined') {
17166             
17167             this.addicon = this.wrap.createChild(
17168                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17169        
17170             this.addicon.on('click', function(e) {
17171                 this.fireEvent('add', this);
17172             }, this);
17173         }
17174         if (typeof(this.events.edit.listeners) != 'undefined') {
17175             
17176             this.editicon = this.wrap.createChild(
17177                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17178             if (this.addicon) {
17179                 this.editicon.setStyle('margin-left', '40px');
17180             }
17181             this.editicon.on('click', function(e) {
17182                 
17183                 // we fire even  if inothing is selected..
17184                 this.fireEvent('edit', this, this.lastData );
17185                 
17186             }, this);
17187         }
17188         */
17189         
17190         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17191             "up" : function(e){
17192                 this.inKeyMode = true;
17193                 this.selectPrev();
17194             },
17195
17196             "down" : function(e){
17197                 if(!this.isExpanded()){
17198                     this.onTriggerClick();
17199                 }else{
17200                     this.inKeyMode = true;
17201                     this.selectNext();
17202                 }
17203             },
17204
17205             "enter" : function(e){
17206 //                this.onViewClick();
17207                 //return true;
17208                 this.collapse();
17209                 
17210                 if(this.fireEvent("specialkey", this, e)){
17211                     this.onViewClick(false);
17212                 }
17213                 
17214                 return true;
17215             },
17216
17217             "esc" : function(e){
17218                 this.collapse();
17219             },
17220
17221             "tab" : function(e){
17222                 this.collapse();
17223                 
17224                 if(this.fireEvent("specialkey", this, e)){
17225                     this.onViewClick(false);
17226                 }
17227                 
17228                 return true;
17229             },
17230
17231             scope : this,
17232
17233             doRelay : function(foo, bar, hname){
17234                 if(hname == 'down' || this.scope.isExpanded()){
17235                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17236                 }
17237                 return true;
17238             },
17239
17240             forceKeyDown: true
17241         });
17242         
17243         
17244         this.queryDelay = Math.max(this.queryDelay || 10,
17245                 this.mode == 'local' ? 10 : 250);
17246         
17247         
17248         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17249         
17250         if(this.typeAhead){
17251             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17252         }
17253         if(this.editable !== false){
17254             this.inputEl().on("keyup", this.onKeyUp, this);
17255         }
17256         if(this.forceSelection){
17257             this.inputEl().on('blur', this.doForce, this);
17258         }
17259         
17260         if(this.multiple){
17261             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17262             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17263         }
17264     },
17265     
17266     initTickableEvents: function()
17267     {   
17268         this.createList();
17269         
17270         if(this.hiddenName){
17271             
17272             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17273             
17274             this.hiddenField.dom.value =
17275                 this.hiddenValue !== undefined ? this.hiddenValue :
17276                 this.value !== undefined ? this.value : '';
17277
17278             // prevent input submission
17279             this.el.dom.removeAttribute('name');
17280             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17281              
17282              
17283         }
17284         
17285 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17286         
17287         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17288         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17289         if(this.triggerList){
17290             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17291         }
17292          
17293         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17294         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17295         
17296         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17297         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17298         
17299         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17300         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17301         
17302         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17303         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17304         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17305         
17306         this.okBtn.hide();
17307         this.cancelBtn.hide();
17308         
17309         var _this = this;
17310         
17311         (function(){
17312             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17313             _this.list.setWidth(lw);
17314         }).defer(100);
17315         
17316         this.list.on('mouseover', this.onViewOver, this);
17317         this.list.on('mousemove', this.onViewMove, this);
17318         
17319         this.list.on('scroll', this.onViewScroll, this);
17320         
17321         if(!this.tpl){
17322             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17323                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17324         }
17325
17326         this.view = new Roo.View(this.list, this.tpl, {
17327             singleSelect:true,
17328             tickable:true,
17329             parent:this,
17330             store: this.store,
17331             selectedClass: this.selectedClass
17332         });
17333         
17334         //this.view.wrapEl.setDisplayed(false);
17335         this.view.on('click', this.onViewClick, this);
17336         
17337         
17338         
17339         this.store.on('beforeload', this.onBeforeLoad, this);
17340         this.store.on('load', this.onLoad, this);
17341         this.store.on('loadexception', this.onLoadException, this);
17342         
17343         if(this.editable){
17344             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17345                 "up" : function(e){
17346                     this.inKeyMode = true;
17347                     this.selectPrev();
17348                 },
17349
17350                 "down" : function(e){
17351                     this.inKeyMode = true;
17352                     this.selectNext();
17353                 },
17354
17355                 "enter" : function(e){
17356                     if(this.fireEvent("specialkey", this, e)){
17357                         this.onViewClick(false);
17358                     }
17359                     
17360                     return true;
17361                 },
17362
17363                 "esc" : function(e){
17364                     this.onTickableFooterButtonClick(e, false, false);
17365                 },
17366
17367                 "tab" : function(e){
17368                     this.fireEvent("specialkey", this, e);
17369                     
17370                     this.onTickableFooterButtonClick(e, false, false);
17371                     
17372                     return true;
17373                 },
17374
17375                 scope : this,
17376
17377                 doRelay : function(e, fn, key){
17378                     if(this.scope.isExpanded()){
17379                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17380                     }
17381                     return true;
17382                 },
17383
17384                 forceKeyDown: true
17385             });
17386         }
17387         
17388         this.queryDelay = Math.max(this.queryDelay || 10,
17389                 this.mode == 'local' ? 10 : 250);
17390         
17391         
17392         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17393         
17394         if(this.typeAhead){
17395             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17396         }
17397         
17398         if(this.editable !== false){
17399             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17400         }
17401         
17402         this.indicator = this.indicatorEl();
17403         
17404         if(this.indicator){
17405             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17406             this.indicator.hide();
17407         }
17408         
17409     },
17410
17411     onDestroy : function(){
17412         if(this.view){
17413             this.view.setStore(null);
17414             this.view.el.removeAllListeners();
17415             this.view.el.remove();
17416             this.view.purgeListeners();
17417         }
17418         if(this.list){
17419             this.list.dom.innerHTML  = '';
17420         }
17421         
17422         if(this.store){
17423             this.store.un('beforeload', this.onBeforeLoad, this);
17424             this.store.un('load', this.onLoad, this);
17425             this.store.un('loadexception', this.onLoadException, this);
17426         }
17427         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17428     },
17429
17430     // private
17431     fireKey : function(e){
17432         if(e.isNavKeyPress() && !this.list.isVisible()){
17433             this.fireEvent("specialkey", this, e);
17434         }
17435     },
17436
17437     // private
17438     onResize: function(w, h)
17439     {
17440         
17441         
17442 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17443 //        
17444 //        if(typeof w != 'number'){
17445 //            // we do not handle it!?!?
17446 //            return;
17447 //        }
17448 //        var tw = this.trigger.getWidth();
17449 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17450 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17451 //        var x = w - tw;
17452 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17453 //            
17454 //        //this.trigger.setStyle('left', x+'px');
17455 //        
17456 //        if(this.list && this.listWidth === undefined){
17457 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17458 //            this.list.setWidth(lw);
17459 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17460 //        }
17461         
17462     
17463         
17464     },
17465
17466     /**
17467      * Allow or prevent the user from directly editing the field text.  If false is passed,
17468      * the user will only be able to select from the items defined in the dropdown list.  This method
17469      * is the runtime equivalent of setting the 'editable' config option at config time.
17470      * @param {Boolean} value True to allow the user to directly edit the field text
17471      */
17472     setEditable : function(value){
17473         if(value == this.editable){
17474             return;
17475         }
17476         this.editable = value;
17477         if(!value){
17478             this.inputEl().dom.setAttribute('readOnly', true);
17479             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17480             this.inputEl().addClass('x-combo-noedit');
17481         }else{
17482             this.inputEl().dom.removeAttribute('readOnly');
17483             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17484             this.inputEl().removeClass('x-combo-noedit');
17485         }
17486     },
17487
17488     // private
17489     
17490     onBeforeLoad : function(combo,opts){
17491         if(!this.hasFocus){
17492             return;
17493         }
17494          if (!opts.add) {
17495             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17496          }
17497         this.restrictHeight();
17498         this.selectedIndex = -1;
17499     },
17500
17501     // private
17502     onLoad : function(){
17503         
17504         this.hasQuery = false;
17505         
17506         if(!this.hasFocus){
17507             return;
17508         }
17509         
17510         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17511             this.loading.hide();
17512         }
17513         
17514         if(this.store.getCount() > 0){
17515             
17516             this.expand();
17517             this.restrictHeight();
17518             if(this.lastQuery == this.allQuery){
17519                 if(this.editable && !this.tickable){
17520                     this.inputEl().dom.select();
17521                 }
17522                 
17523                 if(
17524                     !this.selectByValue(this.value, true) &&
17525                     this.autoFocus && 
17526                     (
17527                         !this.store.lastOptions ||
17528                         typeof(this.store.lastOptions.add) == 'undefined' || 
17529                         this.store.lastOptions.add != true
17530                     )
17531                 ){
17532                     this.select(0, true);
17533                 }
17534             }else{
17535                 if(this.autoFocus){
17536                     this.selectNext();
17537                 }
17538                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17539                     this.taTask.delay(this.typeAheadDelay);
17540                 }
17541             }
17542         }else{
17543             this.onEmptyResults();
17544         }
17545         
17546         //this.el.focus();
17547     },
17548     // private
17549     onLoadException : function()
17550     {
17551         this.hasQuery = false;
17552         
17553         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17554             this.loading.hide();
17555         }
17556         
17557         if(this.tickable && this.editable){
17558             return;
17559         }
17560         
17561         this.collapse();
17562         // only causes errors at present
17563         //Roo.log(this.store.reader.jsonData);
17564         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17565             // fixme
17566             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17567         //}
17568         
17569         
17570     },
17571     // private
17572     onTypeAhead : function(){
17573         if(this.store.getCount() > 0){
17574             var r = this.store.getAt(0);
17575             var newValue = r.data[this.displayField];
17576             var len = newValue.length;
17577             var selStart = this.getRawValue().length;
17578             
17579             if(selStart != len){
17580                 this.setRawValue(newValue);
17581                 this.selectText(selStart, newValue.length);
17582             }
17583         }
17584     },
17585
17586     // private
17587     onSelect : function(record, index){
17588         
17589         if(this.fireEvent('beforeselect', this, record, index) !== false){
17590         
17591             this.setFromData(index > -1 ? record.data : false);
17592             
17593             this.collapse();
17594             this.fireEvent('select', this, record, index);
17595         }
17596     },
17597
17598     /**
17599      * Returns the currently selected field value or empty string if no value is set.
17600      * @return {String} value The selected value
17601      */
17602     getValue : function()
17603     {
17604         if(Roo.isIOS && this.useNativeIOS){
17605             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17606         }
17607         
17608         if(this.multiple){
17609             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17610         }
17611         
17612         if(this.valueField){
17613             return typeof this.value != 'undefined' ? this.value : '';
17614         }else{
17615             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17616         }
17617     },
17618     
17619     getRawValue : function()
17620     {
17621         if(Roo.isIOS && this.useNativeIOS){
17622             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17623         }
17624         
17625         var v = this.inputEl().getValue();
17626         
17627         return v;
17628     },
17629
17630     /**
17631      * Clears any text/value currently set in the field
17632      */
17633     clearValue : function(){
17634         
17635         if(this.hiddenField){
17636             this.hiddenField.dom.value = '';
17637         }
17638         this.value = '';
17639         this.setRawValue('');
17640         this.lastSelectionText = '';
17641         this.lastData = false;
17642         
17643         var close = this.closeTriggerEl();
17644         
17645         if(close){
17646             close.hide();
17647         }
17648         
17649         this.validate();
17650         
17651     },
17652
17653     /**
17654      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17655      * will be displayed in the field.  If the value does not match the data value of an existing item,
17656      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17657      * Otherwise the field will be blank (although the value will still be set).
17658      * @param {String} value The value to match
17659      */
17660     setValue : function(v)
17661     {
17662         if(Roo.isIOS && this.useNativeIOS){
17663             this.setIOSValue(v);
17664             return;
17665         }
17666         
17667         if(this.multiple){
17668             this.syncValue();
17669             return;
17670         }
17671         
17672         var text = v;
17673         if(this.valueField){
17674             var r = this.findRecord(this.valueField, v);
17675             if(r){
17676                 text = r.data[this.displayField];
17677             }else if(this.valueNotFoundText !== undefined){
17678                 text = this.valueNotFoundText;
17679             }
17680         }
17681         this.lastSelectionText = text;
17682         if(this.hiddenField){
17683             this.hiddenField.dom.value = v;
17684         }
17685         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17686         this.value = v;
17687         
17688         var close = this.closeTriggerEl();
17689         
17690         if(close){
17691             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17692         }
17693         
17694         this.validate();
17695     },
17696     /**
17697      * @property {Object} the last set data for the element
17698      */
17699     
17700     lastData : false,
17701     /**
17702      * Sets the value of the field based on a object which is related to the record format for the store.
17703      * @param {Object} value the value to set as. or false on reset?
17704      */
17705     setFromData : function(o){
17706         
17707         if(this.multiple){
17708             this.addItem(o);
17709             return;
17710         }
17711             
17712         var dv = ''; // display value
17713         var vv = ''; // value value..
17714         this.lastData = o;
17715         if (this.displayField) {
17716             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17717         } else {
17718             // this is an error condition!!!
17719             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17720         }
17721         
17722         if(this.valueField){
17723             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17724         }
17725         
17726         var close = this.closeTriggerEl();
17727         
17728         if(close){
17729             if(dv.length || vv * 1 > 0){
17730                 close.show() ;
17731                 this.blockFocus=true;
17732             } else {
17733                 close.hide();
17734             }             
17735         }
17736         
17737         if(this.hiddenField){
17738             this.hiddenField.dom.value = vv;
17739             
17740             this.lastSelectionText = dv;
17741             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17742             this.value = vv;
17743             return;
17744         }
17745         // no hidden field.. - we store the value in 'value', but still display
17746         // display field!!!!
17747         this.lastSelectionText = dv;
17748         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17749         this.value = vv;
17750         
17751         
17752         
17753     },
17754     // private
17755     reset : function(){
17756         // overridden so that last data is reset..
17757         
17758         if(this.multiple){
17759             this.clearItem();
17760             return;
17761         }
17762         
17763         this.setValue(this.originalValue);
17764         //this.clearInvalid();
17765         this.lastData = false;
17766         if (this.view) {
17767             this.view.clearSelections();
17768         }
17769         
17770         this.validate();
17771     },
17772     // private
17773     findRecord : function(prop, value){
17774         var record;
17775         if(this.store.getCount() > 0){
17776             this.store.each(function(r){
17777                 if(r.data[prop] == value){
17778                     record = r;
17779                     return false;
17780                 }
17781                 return true;
17782             });
17783         }
17784         return record;
17785     },
17786     
17787     getName: function()
17788     {
17789         // returns hidden if it's set..
17790         if (!this.rendered) {return ''};
17791         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17792         
17793     },
17794     // private
17795     onViewMove : function(e, t){
17796         this.inKeyMode = false;
17797     },
17798
17799     // private
17800     onViewOver : function(e, t){
17801         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17802             return;
17803         }
17804         var item = this.view.findItemFromChild(t);
17805         
17806         if(item){
17807             var index = this.view.indexOf(item);
17808             this.select(index, false);
17809         }
17810     },
17811
17812     // private
17813     onViewClick : function(view, doFocus, el, e)
17814     {
17815         var index = this.view.getSelectedIndexes()[0];
17816         
17817         var r = this.store.getAt(index);
17818         
17819         if(this.tickable){
17820             
17821             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17822                 return;
17823             }
17824             
17825             var rm = false;
17826             var _this = this;
17827             
17828             Roo.each(this.tickItems, function(v,k){
17829                 
17830                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17831                     Roo.log(v);
17832                     _this.tickItems.splice(k, 1);
17833                     
17834                     if(typeof(e) == 'undefined' && view == false){
17835                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17836                     }
17837                     
17838                     rm = true;
17839                     return;
17840                 }
17841             });
17842             
17843             if(rm){
17844                 return;
17845             }
17846             
17847             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17848                 this.tickItems.push(r.data);
17849             }
17850             
17851             if(typeof(e) == 'undefined' && view == false){
17852                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17853             }
17854                     
17855             return;
17856         }
17857         
17858         if(r){
17859             this.onSelect(r, index);
17860         }
17861         if(doFocus !== false && !this.blockFocus){
17862             this.inputEl().focus();
17863         }
17864     },
17865
17866     // private
17867     restrictHeight : function(){
17868         //this.innerList.dom.style.height = '';
17869         //var inner = this.innerList.dom;
17870         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17871         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17872         //this.list.beginUpdate();
17873         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17874         this.list.alignTo(this.inputEl(), this.listAlign);
17875         this.list.alignTo(this.inputEl(), this.listAlign);
17876         //this.list.endUpdate();
17877     },
17878
17879     // private
17880     onEmptyResults : function(){
17881         
17882         if(this.tickable && this.editable){
17883             this.hasFocus = false;
17884             this.restrictHeight();
17885             return;
17886         }
17887         
17888         this.collapse();
17889     },
17890
17891     /**
17892      * Returns true if the dropdown list is expanded, else false.
17893      */
17894     isExpanded : function(){
17895         return this.list.isVisible();
17896     },
17897
17898     /**
17899      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17900      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17901      * @param {String} value The data value of the item to select
17902      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17903      * selected item if it is not currently in view (defaults to true)
17904      * @return {Boolean} True if the value matched an item in the list, else false
17905      */
17906     selectByValue : function(v, scrollIntoView){
17907         if(v !== undefined && v !== null){
17908             var r = this.findRecord(this.valueField || this.displayField, v);
17909             if(r){
17910                 this.select(this.store.indexOf(r), scrollIntoView);
17911                 return true;
17912             }
17913         }
17914         return false;
17915     },
17916
17917     /**
17918      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17919      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17920      * @param {Number} index The zero-based index of the list item to select
17921      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17922      * selected item if it is not currently in view (defaults to true)
17923      */
17924     select : function(index, scrollIntoView){
17925         this.selectedIndex = index;
17926         this.view.select(index);
17927         if(scrollIntoView !== false){
17928             var el = this.view.getNode(index);
17929             /*
17930              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17931              */
17932             if(el){
17933                 this.list.scrollChildIntoView(el, false);
17934             }
17935         }
17936     },
17937
17938     // private
17939     selectNext : function(){
17940         var ct = this.store.getCount();
17941         if(ct > 0){
17942             if(this.selectedIndex == -1){
17943                 this.select(0);
17944             }else if(this.selectedIndex < ct-1){
17945                 this.select(this.selectedIndex+1);
17946             }
17947         }
17948     },
17949
17950     // private
17951     selectPrev : function(){
17952         var ct = this.store.getCount();
17953         if(ct > 0){
17954             if(this.selectedIndex == -1){
17955                 this.select(0);
17956             }else if(this.selectedIndex != 0){
17957                 this.select(this.selectedIndex-1);
17958             }
17959         }
17960     },
17961
17962     // private
17963     onKeyUp : function(e){
17964         if(this.editable !== false && !e.isSpecialKey()){
17965             this.lastKey = e.getKey();
17966             this.dqTask.delay(this.queryDelay);
17967         }
17968     },
17969
17970     // private
17971     validateBlur : function(){
17972         return !this.list || !this.list.isVisible();   
17973     },
17974
17975     // private
17976     initQuery : function(){
17977         
17978         var v = this.getRawValue();
17979         
17980         if(this.tickable && this.editable){
17981             v = this.tickableInputEl().getValue();
17982         }
17983         
17984         this.doQuery(v);
17985     },
17986
17987     // private
17988     doForce : function(){
17989         if(this.inputEl().dom.value.length > 0){
17990             this.inputEl().dom.value =
17991                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17992              
17993         }
17994     },
17995
17996     /**
17997      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17998      * query allowing the query action to be canceled if needed.
17999      * @param {String} query The SQL query to execute
18000      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18001      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18002      * saved in the current store (defaults to false)
18003      */
18004     doQuery : function(q, forceAll){
18005         
18006         if(q === undefined || q === null){
18007             q = '';
18008         }
18009         var qe = {
18010             query: q,
18011             forceAll: forceAll,
18012             combo: this,
18013             cancel:false
18014         };
18015         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18016             return false;
18017         }
18018         q = qe.query;
18019         
18020         forceAll = qe.forceAll;
18021         if(forceAll === true || (q.length >= this.minChars)){
18022             
18023             this.hasQuery = true;
18024             
18025             if(this.lastQuery != q || this.alwaysQuery){
18026                 this.lastQuery = q;
18027                 if(this.mode == 'local'){
18028                     this.selectedIndex = -1;
18029                     if(forceAll){
18030                         this.store.clearFilter();
18031                     }else{
18032                         
18033                         if(this.specialFilter){
18034                             this.fireEvent('specialfilter', this);
18035                             this.onLoad();
18036                             return;
18037                         }
18038                         
18039                         this.store.filter(this.displayField, q);
18040                     }
18041                     
18042                     this.store.fireEvent("datachanged", this.store);
18043                     
18044                     this.onLoad();
18045                     
18046                     
18047                 }else{
18048                     
18049                     this.store.baseParams[this.queryParam] = q;
18050                     
18051                     var options = {params : this.getParams(q)};
18052                     
18053                     if(this.loadNext){
18054                         options.add = true;
18055                         options.params.start = this.page * this.pageSize;
18056                     }
18057                     
18058                     this.store.load(options);
18059                     
18060                     /*
18061                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18062                      *  we should expand the list on onLoad
18063                      *  so command out it
18064                      */
18065 //                    this.expand();
18066                 }
18067             }else{
18068                 this.selectedIndex = -1;
18069                 this.onLoad();   
18070             }
18071         }
18072         
18073         this.loadNext = false;
18074     },
18075     
18076     // private
18077     getParams : function(q){
18078         var p = {};
18079         //p[this.queryParam] = q;
18080         
18081         if(this.pageSize){
18082             p.start = 0;
18083             p.limit = this.pageSize;
18084         }
18085         return p;
18086     },
18087
18088     /**
18089      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18090      */
18091     collapse : function(){
18092         if(!this.isExpanded()){
18093             return;
18094         }
18095         
18096         this.list.hide();
18097         
18098         this.hasFocus = false;
18099         
18100         if(this.tickable){
18101             this.okBtn.hide();
18102             this.cancelBtn.hide();
18103             this.trigger.show();
18104             
18105             if(this.editable){
18106                 this.tickableInputEl().dom.value = '';
18107                 this.tickableInputEl().blur();
18108             }
18109             
18110         }
18111         
18112         Roo.get(document).un('mousedown', this.collapseIf, this);
18113         Roo.get(document).un('mousewheel', this.collapseIf, this);
18114         if (!this.editable) {
18115             Roo.get(document).un('keydown', this.listKeyPress, this);
18116         }
18117         this.fireEvent('collapse', this);
18118         
18119         this.validate();
18120     },
18121
18122     // private
18123     collapseIf : function(e){
18124         var in_combo  = e.within(this.el);
18125         var in_list =  e.within(this.list);
18126         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18127         
18128         if (in_combo || in_list || is_list) {
18129             //e.stopPropagation();
18130             return;
18131         }
18132         
18133         if(this.tickable){
18134             this.onTickableFooterButtonClick(e, false, false);
18135         }
18136
18137         this.collapse();
18138         
18139     },
18140
18141     /**
18142      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18143      */
18144     expand : function(){
18145        
18146         if(this.isExpanded() || !this.hasFocus){
18147             return;
18148         }
18149         
18150         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18151         this.list.setWidth(lw);
18152         
18153         Roo.log('expand');
18154         
18155         this.list.show();
18156         
18157         this.restrictHeight();
18158         
18159         if(this.tickable){
18160             
18161             this.tickItems = Roo.apply([], this.item);
18162             
18163             this.okBtn.show();
18164             this.cancelBtn.show();
18165             this.trigger.hide();
18166             
18167             if(this.editable){
18168                 this.tickableInputEl().focus();
18169             }
18170             
18171         }
18172         
18173         Roo.get(document).on('mousedown', this.collapseIf, this);
18174         Roo.get(document).on('mousewheel', this.collapseIf, this);
18175         if (!this.editable) {
18176             Roo.get(document).on('keydown', this.listKeyPress, this);
18177         }
18178         
18179         this.fireEvent('expand', this);
18180     },
18181
18182     // private
18183     // Implements the default empty TriggerField.onTriggerClick function
18184     onTriggerClick : function(e)
18185     {
18186         Roo.log('trigger click');
18187         
18188         if(this.disabled || !this.triggerList){
18189             return;
18190         }
18191         
18192         this.page = 0;
18193         this.loadNext = false;
18194         
18195         if(this.isExpanded()){
18196             this.collapse();
18197             if (!this.blockFocus) {
18198                 this.inputEl().focus();
18199             }
18200             
18201         }else {
18202             this.hasFocus = true;
18203             if(this.triggerAction == 'all') {
18204                 this.doQuery(this.allQuery, true);
18205             } else {
18206                 this.doQuery(this.getRawValue());
18207             }
18208             if (!this.blockFocus) {
18209                 this.inputEl().focus();
18210             }
18211         }
18212     },
18213     
18214     onTickableTriggerClick : function(e)
18215     {
18216         if(this.disabled){
18217             return;
18218         }
18219         
18220         this.page = 0;
18221         this.loadNext = false;
18222         this.hasFocus = true;
18223         
18224         if(this.triggerAction == 'all') {
18225             this.doQuery(this.allQuery, true);
18226         } else {
18227             this.doQuery(this.getRawValue());
18228         }
18229     },
18230     
18231     onSearchFieldClick : function(e)
18232     {
18233         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18234             this.onTickableFooterButtonClick(e, false, false);
18235             return;
18236         }
18237         
18238         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18239             return;
18240         }
18241         
18242         this.page = 0;
18243         this.loadNext = false;
18244         this.hasFocus = true;
18245         
18246         if(this.triggerAction == 'all') {
18247             this.doQuery(this.allQuery, true);
18248         } else {
18249             this.doQuery(this.getRawValue());
18250         }
18251     },
18252     
18253     listKeyPress : function(e)
18254     {
18255         //Roo.log('listkeypress');
18256         // scroll to first matching element based on key pres..
18257         if (e.isSpecialKey()) {
18258             return false;
18259         }
18260         var k = String.fromCharCode(e.getKey()).toUpperCase();
18261         //Roo.log(k);
18262         var match  = false;
18263         var csel = this.view.getSelectedNodes();
18264         var cselitem = false;
18265         if (csel.length) {
18266             var ix = this.view.indexOf(csel[0]);
18267             cselitem  = this.store.getAt(ix);
18268             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18269                 cselitem = false;
18270             }
18271             
18272         }
18273         
18274         this.store.each(function(v) { 
18275             if (cselitem) {
18276                 // start at existing selection.
18277                 if (cselitem.id == v.id) {
18278                     cselitem = false;
18279                 }
18280                 return true;
18281             }
18282                 
18283             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18284                 match = this.store.indexOf(v);
18285                 return false;
18286             }
18287             return true;
18288         }, this);
18289         
18290         if (match === false) {
18291             return true; // no more action?
18292         }
18293         // scroll to?
18294         this.view.select(match);
18295         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18296         sn.scrollIntoView(sn.dom.parentNode, false);
18297     },
18298     
18299     onViewScroll : function(e, t){
18300         
18301         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){
18302             return;
18303         }
18304         
18305         this.hasQuery = true;
18306         
18307         this.loading = this.list.select('.loading', true).first();
18308         
18309         if(this.loading === null){
18310             this.list.createChild({
18311                 tag: 'div',
18312                 cls: 'loading roo-select2-more-results roo-select2-active',
18313                 html: 'Loading more results...'
18314             });
18315             
18316             this.loading = this.list.select('.loading', true).first();
18317             
18318             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18319             
18320             this.loading.hide();
18321         }
18322         
18323         this.loading.show();
18324         
18325         var _combo = this;
18326         
18327         this.page++;
18328         this.loadNext = true;
18329         
18330         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18331         
18332         return;
18333     },
18334     
18335     addItem : function(o)
18336     {   
18337         var dv = ''; // display value
18338         
18339         if (this.displayField) {
18340             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18341         } else {
18342             // this is an error condition!!!
18343             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18344         }
18345         
18346         if(!dv.length){
18347             return;
18348         }
18349         
18350         var choice = this.choices.createChild({
18351             tag: 'li',
18352             cls: 'roo-select2-search-choice',
18353             cn: [
18354                 {
18355                     tag: 'div',
18356                     html: dv
18357                 },
18358                 {
18359                     tag: 'a',
18360                     href: '#',
18361                     cls: 'roo-select2-search-choice-close fa fa-times',
18362                     tabindex: '-1'
18363                 }
18364             ]
18365             
18366         }, this.searchField);
18367         
18368         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18369         
18370         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18371         
18372         this.item.push(o);
18373         
18374         this.lastData = o;
18375         
18376         this.syncValue();
18377         
18378         this.inputEl().dom.value = '';
18379         
18380         this.validate();
18381     },
18382     
18383     onRemoveItem : function(e, _self, o)
18384     {
18385         e.preventDefault();
18386         
18387         this.lastItem = Roo.apply([], this.item);
18388         
18389         var index = this.item.indexOf(o.data) * 1;
18390         
18391         if( index < 0){
18392             Roo.log('not this item?!');
18393             return;
18394         }
18395         
18396         this.item.splice(index, 1);
18397         o.item.remove();
18398         
18399         this.syncValue();
18400         
18401         this.fireEvent('remove', this, e);
18402         
18403         this.validate();
18404         
18405     },
18406     
18407     syncValue : function()
18408     {
18409         if(!this.item.length){
18410             this.clearValue();
18411             return;
18412         }
18413             
18414         var value = [];
18415         var _this = this;
18416         Roo.each(this.item, function(i){
18417             if(_this.valueField){
18418                 value.push(i[_this.valueField]);
18419                 return;
18420             }
18421
18422             value.push(i);
18423         });
18424
18425         this.value = value.join(',');
18426
18427         if(this.hiddenField){
18428             this.hiddenField.dom.value = this.value;
18429         }
18430         
18431         this.store.fireEvent("datachanged", this.store);
18432         
18433         this.validate();
18434     },
18435     
18436     clearItem : function()
18437     {
18438         if(!this.multiple){
18439             return;
18440         }
18441         
18442         this.item = [];
18443         
18444         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18445            c.remove();
18446         });
18447         
18448         this.syncValue();
18449         
18450         this.validate();
18451         
18452         if(this.tickable && !Roo.isTouch){
18453             this.view.refresh();
18454         }
18455     },
18456     
18457     inputEl: function ()
18458     {
18459         if(Roo.isIOS && this.useNativeIOS){
18460             return this.el.select('select.roo-ios-select', true).first();
18461         }
18462         
18463         if(Roo.isTouch && this.mobileTouchView){
18464             return this.el.select('input.form-control',true).first();
18465         }
18466         
18467         if(this.tickable){
18468             return this.searchField;
18469         }
18470         
18471         return this.el.select('input.form-control',true).first();
18472     },
18473     
18474     onTickableFooterButtonClick : function(e, btn, el)
18475     {
18476         e.preventDefault();
18477         
18478         this.lastItem = Roo.apply([], this.item);
18479         
18480         if(btn && btn.name == 'cancel'){
18481             this.tickItems = Roo.apply([], this.item);
18482             this.collapse();
18483             return;
18484         }
18485         
18486         this.clearItem();
18487         
18488         var _this = this;
18489         
18490         Roo.each(this.tickItems, function(o){
18491             _this.addItem(o);
18492         });
18493         
18494         this.collapse();
18495         
18496     },
18497     
18498     validate : function()
18499     {
18500         if(this.getVisibilityEl().hasClass('hidden')){
18501             return true;
18502         }
18503         
18504         var v = this.getRawValue();
18505         
18506         if(this.multiple){
18507             v = this.getValue();
18508         }
18509         
18510         if(this.disabled || this.allowBlank || v.length){
18511             this.markValid();
18512             return true;
18513         }
18514         
18515         this.markInvalid();
18516         return false;
18517     },
18518     
18519     tickableInputEl : function()
18520     {
18521         if(!this.tickable || !this.editable){
18522             return this.inputEl();
18523         }
18524         
18525         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18526     },
18527     
18528     
18529     getAutoCreateTouchView : function()
18530     {
18531         var id = Roo.id();
18532         
18533         var cfg = {
18534             cls: 'form-group' //input-group
18535         };
18536         
18537         var input =  {
18538             tag: 'input',
18539             id : id,
18540             type : this.inputType,
18541             cls : 'form-control x-combo-noedit',
18542             autocomplete: 'new-password',
18543             placeholder : this.placeholder || '',
18544             readonly : true
18545         };
18546         
18547         if (this.name) {
18548             input.name = this.name;
18549         }
18550         
18551         if (this.size) {
18552             input.cls += ' input-' + this.size;
18553         }
18554         
18555         if (this.disabled) {
18556             input.disabled = true;
18557         }
18558         
18559         var inputblock = {
18560             cls : 'roo-combobox-wrap',
18561             cn : [
18562                 input
18563             ]
18564         };
18565         
18566         if(this.before){
18567             inputblock.cls += ' input-group';
18568             
18569             inputblock.cn.unshift({
18570                 tag :'span',
18571                 cls : 'input-group-addon input-group-prepend input-group-text',
18572                 html : this.before
18573             });
18574         }
18575         
18576         if(this.removable && !this.multiple){
18577             inputblock.cls += ' roo-removable';
18578             
18579             inputblock.cn.push({
18580                 tag: 'button',
18581                 html : 'x',
18582                 cls : 'roo-combo-removable-btn close'
18583             });
18584         }
18585
18586         if(this.hasFeedback && !this.allowBlank){
18587             
18588             inputblock.cls += ' has-feedback';
18589             
18590             inputblock.cn.push({
18591                 tag: 'span',
18592                 cls: 'glyphicon form-control-feedback'
18593             });
18594             
18595         }
18596         
18597         if (this.after) {
18598             
18599             inputblock.cls += (this.before) ? '' : ' input-group';
18600             
18601             inputblock.cn.push({
18602                 tag :'span',
18603                 cls : 'input-group-addon input-group-append input-group-text',
18604                 html : this.after
18605             });
18606         }
18607
18608         
18609         var ibwrap = inputblock;
18610         
18611         if(this.multiple){
18612             ibwrap = {
18613                 tag: 'ul',
18614                 cls: 'roo-select2-choices',
18615                 cn:[
18616                     {
18617                         tag: 'li',
18618                         cls: 'roo-select2-search-field',
18619                         cn: [
18620
18621                             inputblock
18622                         ]
18623                     }
18624                 ]
18625             };
18626         
18627             
18628         }
18629         
18630         var combobox = {
18631             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18632             cn: [
18633                 {
18634                     tag: 'input',
18635                     type : 'hidden',
18636                     cls: 'form-hidden-field'
18637                 },
18638                 ibwrap
18639             ]
18640         };
18641         
18642         if(!this.multiple && this.showToggleBtn){
18643             
18644             var caret = {
18645                 cls: 'caret'
18646             };
18647             
18648             if (this.caret != false) {
18649                 caret = {
18650                      tag: 'i',
18651                      cls: 'fa fa-' + this.caret
18652                 };
18653                 
18654             }
18655             
18656             combobox.cn.push({
18657                 tag :'span',
18658                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18659                 cn : [
18660                     Roo.bootstrap.version == 3 ? caret : '',
18661                     {
18662                         tag: 'span',
18663                         cls: 'combobox-clear',
18664                         cn  : [
18665                             {
18666                                 tag : 'i',
18667                                 cls: 'icon-remove'
18668                             }
18669                         ]
18670                     }
18671                 ]
18672
18673             })
18674         }
18675         
18676         if(this.multiple){
18677             combobox.cls += ' roo-select2-container-multi';
18678         }
18679         
18680         var required =  this.allowBlank ?  {
18681                     tag : 'i',
18682                     style: 'display: none'
18683                 } : {
18684                    tag : 'i',
18685                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18686                    tooltip : 'This field is required'
18687                 };
18688         
18689         var align = this.labelAlign || this.parentLabelAlign();
18690         
18691         if (align ==='left' && this.fieldLabel.length) {
18692
18693             cfg.cn = [
18694                 required,
18695                 {
18696                     tag: 'label',
18697                     cls : 'control-label col-form-label',
18698                     html : this.fieldLabel
18699
18700                 },
18701                 {
18702                     cls : 'roo-combobox-wrap ', 
18703                     cn: [
18704                         combobox
18705                     ]
18706                 }
18707             ];
18708             
18709             var labelCfg = cfg.cn[1];
18710             var contentCfg = cfg.cn[2];
18711             
18712
18713             if(this.indicatorpos == 'right'){
18714                 cfg.cn = [
18715                     {
18716                         tag: 'label',
18717                         'for' :  id,
18718                         cls : 'control-label col-form-label',
18719                         cn : [
18720                             {
18721                                 tag : 'span',
18722                                 html : this.fieldLabel
18723                             },
18724                             required
18725                         ]
18726                     },
18727                     {
18728                         cls : "roo-combobox-wrap ",
18729                         cn: [
18730                             combobox
18731                         ]
18732                     }
18733
18734                 ];
18735                 
18736                 labelCfg = cfg.cn[0];
18737                 contentCfg = cfg.cn[1];
18738             }
18739             
18740            
18741             
18742             if(this.labelWidth > 12){
18743                 labelCfg.style = "width: " + this.labelWidth + 'px';
18744             }
18745            
18746             if(this.labelWidth < 13 && this.labelmd == 0){
18747                 this.labelmd = this.labelWidth;
18748             }
18749             
18750             if(this.labellg > 0){
18751                 labelCfg.cls += ' col-lg-' + this.labellg;
18752                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18753             }
18754             
18755             if(this.labelmd > 0){
18756                 labelCfg.cls += ' col-md-' + this.labelmd;
18757                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18758             }
18759             
18760             if(this.labelsm > 0){
18761                 labelCfg.cls += ' col-sm-' + this.labelsm;
18762                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18763             }
18764             
18765             if(this.labelxs > 0){
18766                 labelCfg.cls += ' col-xs-' + this.labelxs;
18767                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18768             }
18769                 
18770                 
18771         } else if ( this.fieldLabel.length) {
18772             cfg.cn = [
18773                required,
18774                 {
18775                     tag: 'label',
18776                     cls : 'control-label',
18777                     html : this.fieldLabel
18778
18779                 },
18780                 {
18781                     cls : '', 
18782                     cn: [
18783                         combobox
18784                     ]
18785                 }
18786             ];
18787             
18788             if(this.indicatorpos == 'right'){
18789                 cfg.cn = [
18790                     {
18791                         tag: 'label',
18792                         cls : 'control-label',
18793                         html : this.fieldLabel,
18794                         cn : [
18795                             required
18796                         ]
18797                     },
18798                     {
18799                         cls : '', 
18800                         cn: [
18801                             combobox
18802                         ]
18803                     }
18804                 ];
18805             }
18806         } else {
18807             cfg.cn = combobox;    
18808         }
18809         
18810         
18811         var settings = this;
18812         
18813         ['xs','sm','md','lg'].map(function(size){
18814             if (settings[size]) {
18815                 cfg.cls += ' col-' + size + '-' + settings[size];
18816             }
18817         });
18818         
18819         return cfg;
18820     },
18821     
18822     initTouchView : function()
18823     {
18824         this.renderTouchView();
18825         
18826         this.touchViewEl.on('scroll', function(){
18827             this.el.dom.scrollTop = 0;
18828         }, this);
18829         
18830         this.originalValue = this.getValue();
18831         
18832         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18833         
18834         this.inputEl().on("click", this.showTouchView, this);
18835         if (this.triggerEl) {
18836             this.triggerEl.on("click", this.showTouchView, this);
18837         }
18838         
18839         
18840         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18841         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18842         
18843         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18844         
18845         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18846         this.store.on('load', this.onTouchViewLoad, this);
18847         this.store.on('loadexception', this.onTouchViewLoadException, this);
18848         
18849         if(this.hiddenName){
18850             
18851             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18852             
18853             this.hiddenField.dom.value =
18854                 this.hiddenValue !== undefined ? this.hiddenValue :
18855                 this.value !== undefined ? this.value : '';
18856         
18857             this.el.dom.removeAttribute('name');
18858             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18859         }
18860         
18861         if(this.multiple){
18862             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18863             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18864         }
18865         
18866         if(this.removable && !this.multiple){
18867             var close = this.closeTriggerEl();
18868             if(close){
18869                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18870                 close.on('click', this.removeBtnClick, this, close);
18871             }
18872         }
18873         /*
18874          * fix the bug in Safari iOS8
18875          */
18876         this.inputEl().on("focus", function(e){
18877             document.activeElement.blur();
18878         }, this);
18879         
18880         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18881         
18882         return;
18883         
18884         
18885     },
18886     
18887     renderTouchView : function()
18888     {
18889         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18890         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891         
18892         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18893         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894         
18895         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18896         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897         this.touchViewBodyEl.setStyle('overflow', 'auto');
18898         
18899         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18900         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901         
18902         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18903         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904         
18905     },
18906     
18907     showTouchView : function()
18908     {
18909         if(this.disabled){
18910             return;
18911         }
18912         
18913         this.touchViewHeaderEl.hide();
18914
18915         if(this.modalTitle.length){
18916             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18917             this.touchViewHeaderEl.show();
18918         }
18919
18920         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18921         this.touchViewEl.show();
18922
18923         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18924         
18925         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18926         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18927
18928         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18929
18930         if(this.modalTitle.length){
18931             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18932         }
18933         
18934         this.touchViewBodyEl.setHeight(bodyHeight);
18935
18936         if(this.animate){
18937             var _this = this;
18938             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18939         }else{
18940             this.touchViewEl.addClass(['in','show']);
18941         }
18942         
18943         if(this._touchViewMask){
18944             Roo.get(document.body).addClass("x-body-masked");
18945             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18946             this._touchViewMask.setStyle('z-index', 10000);
18947             this._touchViewMask.addClass('show');
18948         }
18949         
18950         this.doTouchViewQuery();
18951         
18952     },
18953     
18954     hideTouchView : function()
18955     {
18956         this.touchViewEl.removeClass(['in','show']);
18957
18958         if(this.animate){
18959             var _this = this;
18960             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18961         }else{
18962             this.touchViewEl.setStyle('display', 'none');
18963         }
18964         
18965         if(this._touchViewMask){
18966             this._touchViewMask.removeClass('show');
18967             Roo.get(document.body).removeClass("x-body-masked");
18968         }
18969     },
18970     
18971     setTouchViewValue : function()
18972     {
18973         if(this.multiple){
18974             this.clearItem();
18975         
18976             var _this = this;
18977
18978             Roo.each(this.tickItems, function(o){
18979                 this.addItem(o);
18980             }, this);
18981         }
18982         
18983         this.hideTouchView();
18984     },
18985     
18986     doTouchViewQuery : function()
18987     {
18988         var qe = {
18989             query: '',
18990             forceAll: true,
18991             combo: this,
18992             cancel:false
18993         };
18994         
18995         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18996             return false;
18997         }
18998         
18999         if(!this.alwaysQuery || this.mode == 'local'){
19000             this.onTouchViewLoad();
19001             return;
19002         }
19003         
19004         this.store.load();
19005     },
19006     
19007     onTouchViewBeforeLoad : function(combo,opts)
19008     {
19009         return;
19010     },
19011
19012     // private
19013     onTouchViewLoad : function()
19014     {
19015         if(this.store.getCount() < 1){
19016             this.onTouchViewEmptyResults();
19017             return;
19018         }
19019         
19020         this.clearTouchView();
19021         
19022         var rawValue = this.getRawValue();
19023         
19024         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19025         
19026         this.tickItems = [];
19027         
19028         this.store.data.each(function(d, rowIndex){
19029             var row = this.touchViewListGroup.createChild(template);
19030             
19031             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19032                 row.addClass(d.data.cls);
19033             }
19034             
19035             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19036                 var cfg = {
19037                     data : d.data,
19038                     html : d.data[this.displayField]
19039                 };
19040                 
19041                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19042                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19043                 }
19044             }
19045             row.removeClass('selected');
19046             if(!this.multiple && this.valueField &&
19047                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19048             {
19049                 // radio buttons..
19050                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19051                 row.addClass('selected');
19052             }
19053             
19054             if(this.multiple && this.valueField &&
19055                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19056             {
19057                 
19058                 // checkboxes...
19059                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19060                 this.tickItems.push(d.data);
19061             }
19062             
19063             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19064             
19065         }, this);
19066         
19067         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19068         
19069         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19070
19071         if(this.modalTitle.length){
19072             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19073         }
19074
19075         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19076         
19077         if(this.mobile_restrict_height && listHeight < bodyHeight){
19078             this.touchViewBodyEl.setHeight(listHeight);
19079         }
19080         
19081         var _this = this;
19082         
19083         if(firstChecked && listHeight > bodyHeight){
19084             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19085         }
19086         
19087     },
19088     
19089     onTouchViewLoadException : function()
19090     {
19091         this.hideTouchView();
19092     },
19093     
19094     onTouchViewEmptyResults : function()
19095     {
19096         this.clearTouchView();
19097         
19098         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19099         
19100         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19101         
19102     },
19103     
19104     clearTouchView : function()
19105     {
19106         this.touchViewListGroup.dom.innerHTML = '';
19107     },
19108     
19109     onTouchViewClick : function(e, el, o)
19110     {
19111         e.preventDefault();
19112         
19113         var row = o.row;
19114         var rowIndex = o.rowIndex;
19115         
19116         var r = this.store.getAt(rowIndex);
19117         
19118         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19119             
19120             if(!this.multiple){
19121                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19122                     c.dom.removeAttribute('checked');
19123                 }, this);
19124
19125                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19126
19127                 this.setFromData(r.data);
19128
19129                 var close = this.closeTriggerEl();
19130
19131                 if(close){
19132                     close.show();
19133                 }
19134
19135                 this.hideTouchView();
19136
19137                 this.fireEvent('select', this, r, rowIndex);
19138
19139                 return;
19140             }
19141
19142             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19143                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19144                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19145                 return;
19146             }
19147
19148             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19149             this.addItem(r.data);
19150             this.tickItems.push(r.data);
19151         }
19152     },
19153     
19154     getAutoCreateNativeIOS : function()
19155     {
19156         var cfg = {
19157             cls: 'form-group' //input-group,
19158         };
19159         
19160         var combobox =  {
19161             tag: 'select',
19162             cls : 'roo-ios-select'
19163         };
19164         
19165         if (this.name) {
19166             combobox.name = this.name;
19167         }
19168         
19169         if (this.disabled) {
19170             combobox.disabled = true;
19171         }
19172         
19173         var settings = this;
19174         
19175         ['xs','sm','md','lg'].map(function(size){
19176             if (settings[size]) {
19177                 cfg.cls += ' col-' + size + '-' + settings[size];
19178             }
19179         });
19180         
19181         cfg.cn = combobox;
19182         
19183         return cfg;
19184         
19185     },
19186     
19187     initIOSView : function()
19188     {
19189         this.store.on('load', this.onIOSViewLoad, this);
19190         
19191         return;
19192     },
19193     
19194     onIOSViewLoad : function()
19195     {
19196         if(this.store.getCount() < 1){
19197             return;
19198         }
19199         
19200         this.clearIOSView();
19201         
19202         if(this.allowBlank) {
19203             
19204             var default_text = '-- SELECT --';
19205             
19206             if(this.placeholder.length){
19207                 default_text = this.placeholder;
19208             }
19209             
19210             if(this.emptyTitle.length){
19211                 default_text += ' - ' + this.emptyTitle + ' -';
19212             }
19213             
19214             var opt = this.inputEl().createChild({
19215                 tag: 'option',
19216                 value : 0,
19217                 html : default_text
19218             });
19219             
19220             var o = {};
19221             o[this.valueField] = 0;
19222             o[this.displayField] = default_text;
19223             
19224             this.ios_options.push({
19225                 data : o,
19226                 el : opt
19227             });
19228             
19229         }
19230         
19231         this.store.data.each(function(d, rowIndex){
19232             
19233             var html = '';
19234             
19235             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19236                 html = d.data[this.displayField];
19237             }
19238             
19239             var value = '';
19240             
19241             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19242                 value = d.data[this.valueField];
19243             }
19244             
19245             var option = {
19246                 tag: 'option',
19247                 value : value,
19248                 html : html
19249             };
19250             
19251             if(this.value == d.data[this.valueField]){
19252                 option['selected'] = true;
19253             }
19254             
19255             var opt = this.inputEl().createChild(option);
19256             
19257             this.ios_options.push({
19258                 data : d.data,
19259                 el : opt
19260             });
19261             
19262         }, this);
19263         
19264         this.inputEl().on('change', function(){
19265            this.fireEvent('select', this);
19266         }, this);
19267         
19268     },
19269     
19270     clearIOSView: function()
19271     {
19272         this.inputEl().dom.innerHTML = '';
19273         
19274         this.ios_options = [];
19275     },
19276     
19277     setIOSValue: function(v)
19278     {
19279         this.value = v;
19280         
19281         if(!this.ios_options){
19282             return;
19283         }
19284         
19285         Roo.each(this.ios_options, function(opts){
19286            
19287            opts.el.dom.removeAttribute('selected');
19288            
19289            if(opts.data[this.valueField] != v){
19290                return;
19291            }
19292            
19293            opts.el.dom.setAttribute('selected', true);
19294            
19295         }, this);
19296     }
19297
19298     /** 
19299     * @cfg {Boolean} grow 
19300     * @hide 
19301     */
19302     /** 
19303     * @cfg {Number} growMin 
19304     * @hide 
19305     */
19306     /** 
19307     * @cfg {Number} growMax 
19308     * @hide 
19309     */
19310     /**
19311      * @hide
19312      * @method autoSize
19313      */
19314 });
19315
19316 Roo.apply(Roo.bootstrap.ComboBox,  {
19317     
19318     header : {
19319         tag: 'div',
19320         cls: 'modal-header',
19321         cn: [
19322             {
19323                 tag: 'h4',
19324                 cls: 'modal-title'
19325             }
19326         ]
19327     },
19328     
19329     body : {
19330         tag: 'div',
19331         cls: 'modal-body',
19332         cn: [
19333             {
19334                 tag: 'ul',
19335                 cls: 'list-group'
19336             }
19337         ]
19338     },
19339     
19340     listItemRadio : {
19341         tag: 'li',
19342         cls: 'list-group-item',
19343         cn: [
19344             {
19345                 tag: 'span',
19346                 cls: 'roo-combobox-list-group-item-value'
19347             },
19348             {
19349                 tag: 'div',
19350                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19351                 cn: [
19352                     {
19353                         tag: 'input',
19354                         type: 'radio'
19355                     },
19356                     {
19357                         tag: 'label'
19358                     }
19359                 ]
19360             }
19361         ]
19362     },
19363     
19364     listItemCheckbox : {
19365         tag: 'li',
19366         cls: 'list-group-item',
19367         cn: [
19368             {
19369                 tag: 'span',
19370                 cls: 'roo-combobox-list-group-item-value'
19371             },
19372             {
19373                 tag: 'div',
19374                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19375                 cn: [
19376                     {
19377                         tag: 'input',
19378                         type: 'checkbox'
19379                     },
19380                     {
19381                         tag: 'label'
19382                     }
19383                 ]
19384             }
19385         ]
19386     },
19387     
19388     emptyResult : {
19389         tag: 'div',
19390         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19391     },
19392     
19393     footer : {
19394         tag: 'div',
19395         cls: 'modal-footer',
19396         cn: [
19397             {
19398                 tag: 'div',
19399                 cls: 'row',
19400                 cn: [
19401                     {
19402                         tag: 'div',
19403                         cls: 'col-xs-6 text-left',
19404                         cn: {
19405                             tag: 'button',
19406                             cls: 'btn btn-danger roo-touch-view-cancel',
19407                             html: 'Cancel'
19408                         }
19409                     },
19410                     {
19411                         tag: 'div',
19412                         cls: 'col-xs-6 text-right',
19413                         cn: {
19414                             tag: 'button',
19415                             cls: 'btn btn-success roo-touch-view-ok',
19416                             html: 'OK'
19417                         }
19418                     }
19419                 ]
19420             }
19421         ]
19422         
19423     }
19424 });
19425
19426 Roo.apply(Roo.bootstrap.ComboBox,  {
19427     
19428     touchViewTemplate : {
19429         tag: 'div',
19430         cls: 'modal fade roo-combobox-touch-view',
19431         cn: [
19432             {
19433                 tag: 'div',
19434                 cls: 'modal-dialog',
19435                 style : 'position:fixed', // we have to fix position....
19436                 cn: [
19437                     {
19438                         tag: 'div',
19439                         cls: 'modal-content',
19440                         cn: [
19441                             Roo.bootstrap.ComboBox.header,
19442                             Roo.bootstrap.ComboBox.body,
19443                             Roo.bootstrap.ComboBox.footer
19444                         ]
19445                     }
19446                 ]
19447             }
19448         ]
19449     }
19450 });/*
19451  * Based on:
19452  * Ext JS Library 1.1.1
19453  * Copyright(c) 2006-2007, Ext JS, LLC.
19454  *
19455  * Originally Released Under LGPL - original licence link has changed is not relivant.
19456  *
19457  * Fork - LGPL
19458  * <script type="text/javascript">
19459  */
19460
19461 /**
19462  * @class Roo.View
19463  * @extends Roo.util.Observable
19464  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19465  * This class also supports single and multi selection modes. <br>
19466  * Create a data model bound view:
19467  <pre><code>
19468  var store = new Roo.data.Store(...);
19469
19470  var view = new Roo.View({
19471     el : "my-element",
19472     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19473  
19474     singleSelect: true,
19475     selectedClass: "ydataview-selected",
19476     store: store
19477  });
19478
19479  // listen for node click?
19480  view.on("click", function(vw, index, node, e){
19481  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19482  });
19483
19484  // load XML data
19485  dataModel.load("foobar.xml");
19486  </code></pre>
19487  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19488  * <br><br>
19489  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19490  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19491  * 
19492  * Note: old style constructor is still suported (container, template, config)
19493  * 
19494  * @constructor
19495  * Create a new View
19496  * @param {Object} config The config object
19497  * 
19498  */
19499 Roo.View = function(config, depreciated_tpl, depreciated_config){
19500     
19501     this.parent = false;
19502     
19503     if (typeof(depreciated_tpl) == 'undefined') {
19504         // new way.. - universal constructor.
19505         Roo.apply(this, config);
19506         this.el  = Roo.get(this.el);
19507     } else {
19508         // old format..
19509         this.el  = Roo.get(config);
19510         this.tpl = depreciated_tpl;
19511         Roo.apply(this, depreciated_config);
19512     }
19513     this.wrapEl  = this.el.wrap().wrap();
19514     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19515     
19516     
19517     if(typeof(this.tpl) == "string"){
19518         this.tpl = new Roo.Template(this.tpl);
19519     } else {
19520         // support xtype ctors..
19521         this.tpl = new Roo.factory(this.tpl, Roo);
19522     }
19523     
19524     
19525     this.tpl.compile();
19526     
19527     /** @private */
19528     this.addEvents({
19529         /**
19530          * @event beforeclick
19531          * Fires before a click is processed. Returns false to cancel the default action.
19532          * @param {Roo.View} this
19533          * @param {Number} index The index of the target node
19534          * @param {HTMLElement} node The target node
19535          * @param {Roo.EventObject} e The raw event object
19536          */
19537             "beforeclick" : true,
19538         /**
19539          * @event click
19540          * Fires when a template node is clicked.
19541          * @param {Roo.View} this
19542          * @param {Number} index The index of the target node
19543          * @param {HTMLElement} node The target node
19544          * @param {Roo.EventObject} e The raw event object
19545          */
19546             "click" : true,
19547         /**
19548          * @event dblclick
19549          * Fires when a template node is double clicked.
19550          * @param {Roo.View} this
19551          * @param {Number} index The index of the target node
19552          * @param {HTMLElement} node The target node
19553          * @param {Roo.EventObject} e The raw event object
19554          */
19555             "dblclick" : true,
19556         /**
19557          * @event contextmenu
19558          * Fires when a template node is right clicked.
19559          * @param {Roo.View} this
19560          * @param {Number} index The index of the target node
19561          * @param {HTMLElement} node The target node
19562          * @param {Roo.EventObject} e The raw event object
19563          */
19564             "contextmenu" : true,
19565         /**
19566          * @event selectionchange
19567          * Fires when the selected nodes change.
19568          * @param {Roo.View} this
19569          * @param {Array} selections Array of the selected nodes
19570          */
19571             "selectionchange" : true,
19572     
19573         /**
19574          * @event beforeselect
19575          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19576          * @param {Roo.View} this
19577          * @param {HTMLElement} node The node to be selected
19578          * @param {Array} selections Array of currently selected nodes
19579          */
19580             "beforeselect" : true,
19581         /**
19582          * @event preparedata
19583          * Fires on every row to render, to allow you to change the data.
19584          * @param {Roo.View} this
19585          * @param {Object} data to be rendered (change this)
19586          */
19587           "preparedata" : true
19588           
19589           
19590         });
19591
19592
19593
19594     this.el.on({
19595         "click": this.onClick,
19596         "dblclick": this.onDblClick,
19597         "contextmenu": this.onContextMenu,
19598         scope:this
19599     });
19600
19601     this.selections = [];
19602     this.nodes = [];
19603     this.cmp = new Roo.CompositeElementLite([]);
19604     if(this.store){
19605         this.store = Roo.factory(this.store, Roo.data);
19606         this.setStore(this.store, true);
19607     }
19608     
19609     if ( this.footer && this.footer.xtype) {
19610            
19611          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19612         
19613         this.footer.dataSource = this.store;
19614         this.footer.container = fctr;
19615         this.footer = Roo.factory(this.footer, Roo);
19616         fctr.insertFirst(this.el);
19617         
19618         // this is a bit insane - as the paging toolbar seems to detach the el..
19619 //        dom.parentNode.parentNode.parentNode
19620          // they get detached?
19621     }
19622     
19623     
19624     Roo.View.superclass.constructor.call(this);
19625     
19626     
19627 };
19628
19629 Roo.extend(Roo.View, Roo.util.Observable, {
19630     
19631      /**
19632      * @cfg {Roo.data.Store} store Data store to load data from.
19633      */
19634     store : false,
19635     
19636     /**
19637      * @cfg {String|Roo.Element} el The container element.
19638      */
19639     el : '',
19640     
19641     /**
19642      * @cfg {String|Roo.Template} tpl The template used by this View 
19643      */
19644     tpl : false,
19645     /**
19646      * @cfg {String} dataName the named area of the template to use as the data area
19647      *                          Works with domtemplates roo-name="name"
19648      */
19649     dataName: false,
19650     /**
19651      * @cfg {String} selectedClass The css class to add to selected nodes
19652      */
19653     selectedClass : "x-view-selected",
19654      /**
19655      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19656      */
19657     emptyText : "",
19658     
19659     /**
19660      * @cfg {String} text to display on mask (default Loading)
19661      */
19662     mask : false,
19663     /**
19664      * @cfg {Boolean} multiSelect Allow multiple selection
19665      */
19666     multiSelect : false,
19667     /**
19668      * @cfg {Boolean} singleSelect Allow single selection
19669      */
19670     singleSelect:  false,
19671     
19672     /**
19673      * @cfg {Boolean} toggleSelect - selecting 
19674      */
19675     toggleSelect : false,
19676     
19677     /**
19678      * @cfg {Boolean} tickable - selecting 
19679      */
19680     tickable : false,
19681     
19682     /**
19683      * Returns the element this view is bound to.
19684      * @return {Roo.Element}
19685      */
19686     getEl : function(){
19687         return this.wrapEl;
19688     },
19689     
19690     
19691
19692     /**
19693      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19694      */
19695     refresh : function(){
19696         //Roo.log('refresh');
19697         var t = this.tpl;
19698         
19699         // if we are using something like 'domtemplate', then
19700         // the what gets used is:
19701         // t.applySubtemplate(NAME, data, wrapping data..)
19702         // the outer template then get' applied with
19703         //     the store 'extra data'
19704         // and the body get's added to the
19705         //      roo-name="data" node?
19706         //      <span class='roo-tpl-{name}'></span> ?????
19707         
19708         
19709         
19710         this.clearSelections();
19711         this.el.update("");
19712         var html = [];
19713         var records = this.store.getRange();
19714         if(records.length < 1) {
19715             
19716             // is this valid??  = should it render a template??
19717             
19718             this.el.update(this.emptyText);
19719             return;
19720         }
19721         var el = this.el;
19722         if (this.dataName) {
19723             this.el.update(t.apply(this.store.meta)); //????
19724             el = this.el.child('.roo-tpl-' + this.dataName);
19725         }
19726         
19727         for(var i = 0, len = records.length; i < len; i++){
19728             var data = this.prepareData(records[i].data, i, records[i]);
19729             this.fireEvent("preparedata", this, data, i, records[i]);
19730             
19731             var d = Roo.apply({}, data);
19732             
19733             if(this.tickable){
19734                 Roo.apply(d, {'roo-id' : Roo.id()});
19735                 
19736                 var _this = this;
19737             
19738                 Roo.each(this.parent.item, function(item){
19739                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19740                         return;
19741                     }
19742                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19743                 });
19744             }
19745             
19746             html[html.length] = Roo.util.Format.trim(
19747                 this.dataName ?
19748                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19749                     t.apply(d)
19750             );
19751         }
19752         
19753         
19754         
19755         el.update(html.join(""));
19756         this.nodes = el.dom.childNodes;
19757         this.updateIndexes(0);
19758     },
19759     
19760
19761     /**
19762      * Function to override to reformat the data that is sent to
19763      * the template for each node.
19764      * DEPRICATED - use the preparedata event handler.
19765      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19766      * a JSON object for an UpdateManager bound view).
19767      */
19768     prepareData : function(data, index, record)
19769     {
19770         this.fireEvent("preparedata", this, data, index, record);
19771         return data;
19772     },
19773
19774     onUpdate : function(ds, record){
19775         // Roo.log('on update');   
19776         this.clearSelections();
19777         var index = this.store.indexOf(record);
19778         var n = this.nodes[index];
19779         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19780         n.parentNode.removeChild(n);
19781         this.updateIndexes(index, index);
19782     },
19783
19784     
19785     
19786 // --------- FIXME     
19787     onAdd : function(ds, records, index)
19788     {
19789         //Roo.log(['on Add', ds, records, index] );        
19790         this.clearSelections();
19791         if(this.nodes.length == 0){
19792             this.refresh();
19793             return;
19794         }
19795         var n = this.nodes[index];
19796         for(var i = 0, len = records.length; i < len; i++){
19797             var d = this.prepareData(records[i].data, i, records[i]);
19798             if(n){
19799                 this.tpl.insertBefore(n, d);
19800             }else{
19801                 
19802                 this.tpl.append(this.el, d);
19803             }
19804         }
19805         this.updateIndexes(index);
19806     },
19807
19808     onRemove : function(ds, record, index){
19809        // Roo.log('onRemove');
19810         this.clearSelections();
19811         var el = this.dataName  ?
19812             this.el.child('.roo-tpl-' + this.dataName) :
19813             this.el; 
19814         
19815         el.dom.removeChild(this.nodes[index]);
19816         this.updateIndexes(index);
19817     },
19818
19819     /**
19820      * Refresh an individual node.
19821      * @param {Number} index
19822      */
19823     refreshNode : function(index){
19824         this.onUpdate(this.store, this.store.getAt(index));
19825     },
19826
19827     updateIndexes : function(startIndex, endIndex){
19828         var ns = this.nodes;
19829         startIndex = startIndex || 0;
19830         endIndex = endIndex || ns.length - 1;
19831         for(var i = startIndex; i <= endIndex; i++){
19832             ns[i].nodeIndex = i;
19833         }
19834     },
19835
19836     /**
19837      * Changes the data store this view uses and refresh the view.
19838      * @param {Store} store
19839      */
19840     setStore : function(store, initial){
19841         if(!initial && this.store){
19842             this.store.un("datachanged", this.refresh);
19843             this.store.un("add", this.onAdd);
19844             this.store.un("remove", this.onRemove);
19845             this.store.un("update", this.onUpdate);
19846             this.store.un("clear", this.refresh);
19847             this.store.un("beforeload", this.onBeforeLoad);
19848             this.store.un("load", this.onLoad);
19849             this.store.un("loadexception", this.onLoad);
19850         }
19851         if(store){
19852           
19853             store.on("datachanged", this.refresh, this);
19854             store.on("add", this.onAdd, this);
19855             store.on("remove", this.onRemove, this);
19856             store.on("update", this.onUpdate, this);
19857             store.on("clear", this.refresh, this);
19858             store.on("beforeload", this.onBeforeLoad, this);
19859             store.on("load", this.onLoad, this);
19860             store.on("loadexception", this.onLoad, this);
19861         }
19862         
19863         if(store){
19864             this.refresh();
19865         }
19866     },
19867     /**
19868      * onbeforeLoad - masks the loading area.
19869      *
19870      */
19871     onBeforeLoad : function(store,opts)
19872     {
19873          //Roo.log('onBeforeLoad');   
19874         if (!opts.add) {
19875             this.el.update("");
19876         }
19877         this.el.mask(this.mask ? this.mask : "Loading" ); 
19878     },
19879     onLoad : function ()
19880     {
19881         this.el.unmask();
19882     },
19883     
19884
19885     /**
19886      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19887      * @param {HTMLElement} node
19888      * @return {HTMLElement} The template node
19889      */
19890     findItemFromChild : function(node){
19891         var el = this.dataName  ?
19892             this.el.child('.roo-tpl-' + this.dataName,true) :
19893             this.el.dom; 
19894         
19895         if(!node || node.parentNode == el){
19896                     return node;
19897             }
19898             var p = node.parentNode;
19899             while(p && p != el){
19900             if(p.parentNode == el){
19901                 return p;
19902             }
19903             p = p.parentNode;
19904         }
19905             return null;
19906     },
19907
19908     /** @ignore */
19909     onClick : function(e){
19910         var item = this.findItemFromChild(e.getTarget());
19911         if(item){
19912             var index = this.indexOf(item);
19913             if(this.onItemClick(item, index, e) !== false){
19914                 this.fireEvent("click", this, index, item, e);
19915             }
19916         }else{
19917             this.clearSelections();
19918         }
19919     },
19920
19921     /** @ignore */
19922     onContextMenu : function(e){
19923         var item = this.findItemFromChild(e.getTarget());
19924         if(item){
19925             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19926         }
19927     },
19928
19929     /** @ignore */
19930     onDblClick : function(e){
19931         var item = this.findItemFromChild(e.getTarget());
19932         if(item){
19933             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19934         }
19935     },
19936
19937     onItemClick : function(item, index, e)
19938     {
19939         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19940             return false;
19941         }
19942         if (this.toggleSelect) {
19943             var m = this.isSelected(item) ? 'unselect' : 'select';
19944             //Roo.log(m);
19945             var _t = this;
19946             _t[m](item, true, false);
19947             return true;
19948         }
19949         if(this.multiSelect || this.singleSelect){
19950             if(this.multiSelect && e.shiftKey && this.lastSelection){
19951                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19952             }else{
19953                 this.select(item, this.multiSelect && e.ctrlKey);
19954                 this.lastSelection = item;
19955             }
19956             
19957             if(!this.tickable){
19958                 e.preventDefault();
19959             }
19960             
19961         }
19962         return true;
19963     },
19964
19965     /**
19966      * Get the number of selected nodes.
19967      * @return {Number}
19968      */
19969     getSelectionCount : function(){
19970         return this.selections.length;
19971     },
19972
19973     /**
19974      * Get the currently selected nodes.
19975      * @return {Array} An array of HTMLElements
19976      */
19977     getSelectedNodes : function(){
19978         return this.selections;
19979     },
19980
19981     /**
19982      * Get the indexes of the selected nodes.
19983      * @return {Array}
19984      */
19985     getSelectedIndexes : function(){
19986         var indexes = [], s = this.selections;
19987         for(var i = 0, len = s.length; i < len; i++){
19988             indexes.push(s[i].nodeIndex);
19989         }
19990         return indexes;
19991     },
19992
19993     /**
19994      * Clear all selections
19995      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19996      */
19997     clearSelections : function(suppressEvent){
19998         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19999             this.cmp.elements = this.selections;
20000             this.cmp.removeClass(this.selectedClass);
20001             this.selections = [];
20002             if(!suppressEvent){
20003                 this.fireEvent("selectionchange", this, this.selections);
20004             }
20005         }
20006     },
20007
20008     /**
20009      * Returns true if the passed node is selected
20010      * @param {HTMLElement/Number} node The node or node index
20011      * @return {Boolean}
20012      */
20013     isSelected : function(node){
20014         var s = this.selections;
20015         if(s.length < 1){
20016             return false;
20017         }
20018         node = this.getNode(node);
20019         return s.indexOf(node) !== -1;
20020     },
20021
20022     /**
20023      * Selects nodes.
20024      * @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
20025      * @param {Boolean} keepExisting (optional) true to keep existing selections
20026      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20027      */
20028     select : function(nodeInfo, keepExisting, suppressEvent){
20029         if(nodeInfo instanceof Array){
20030             if(!keepExisting){
20031                 this.clearSelections(true);
20032             }
20033             for(var i = 0, len = nodeInfo.length; i < len; i++){
20034                 this.select(nodeInfo[i], true, true);
20035             }
20036             return;
20037         } 
20038         var node = this.getNode(nodeInfo);
20039         if(!node || this.isSelected(node)){
20040             return; // already selected.
20041         }
20042         if(!keepExisting){
20043             this.clearSelections(true);
20044         }
20045         
20046         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20047             Roo.fly(node).addClass(this.selectedClass);
20048             this.selections.push(node);
20049             if(!suppressEvent){
20050                 this.fireEvent("selectionchange", this, this.selections);
20051             }
20052         }
20053         
20054         
20055     },
20056       /**
20057      * Unselects nodes.
20058      * @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
20059      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20060      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20061      */
20062     unselect : function(nodeInfo, keepExisting, suppressEvent)
20063     {
20064         if(nodeInfo instanceof Array){
20065             Roo.each(this.selections, function(s) {
20066                 this.unselect(s, nodeInfo);
20067             }, this);
20068             return;
20069         }
20070         var node = this.getNode(nodeInfo);
20071         if(!node || !this.isSelected(node)){
20072             //Roo.log("not selected");
20073             return; // not selected.
20074         }
20075         // fireevent???
20076         var ns = [];
20077         Roo.each(this.selections, function(s) {
20078             if (s == node ) {
20079                 Roo.fly(node).removeClass(this.selectedClass);
20080
20081                 return;
20082             }
20083             ns.push(s);
20084         },this);
20085         
20086         this.selections= ns;
20087         this.fireEvent("selectionchange", this, this.selections);
20088     },
20089
20090     /**
20091      * Gets a template node.
20092      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20093      * @return {HTMLElement} The node or null if it wasn't found
20094      */
20095     getNode : function(nodeInfo){
20096         if(typeof nodeInfo == "string"){
20097             return document.getElementById(nodeInfo);
20098         }else if(typeof nodeInfo == "number"){
20099             return this.nodes[nodeInfo];
20100         }
20101         return nodeInfo;
20102     },
20103
20104     /**
20105      * Gets a range template nodes.
20106      * @param {Number} startIndex
20107      * @param {Number} endIndex
20108      * @return {Array} An array of nodes
20109      */
20110     getNodes : function(start, end){
20111         var ns = this.nodes;
20112         start = start || 0;
20113         end = typeof end == "undefined" ? ns.length - 1 : end;
20114         var nodes = [];
20115         if(start <= end){
20116             for(var i = start; i <= end; i++){
20117                 nodes.push(ns[i]);
20118             }
20119         } else{
20120             for(var i = start; i >= end; i--){
20121                 nodes.push(ns[i]);
20122             }
20123         }
20124         return nodes;
20125     },
20126
20127     /**
20128      * Finds the index of the passed node
20129      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20130      * @return {Number} The index of the node or -1
20131      */
20132     indexOf : function(node){
20133         node = this.getNode(node);
20134         if(typeof node.nodeIndex == "number"){
20135             return node.nodeIndex;
20136         }
20137         var ns = this.nodes;
20138         for(var i = 0, len = ns.length; i < len; i++){
20139             if(ns[i] == node){
20140                 return i;
20141             }
20142         }
20143         return -1;
20144     }
20145 });
20146 /*
20147  * - LGPL
20148  *
20149  * based on jquery fullcalendar
20150  * 
20151  */
20152
20153 Roo.bootstrap = Roo.bootstrap || {};
20154 /**
20155  * @class Roo.bootstrap.Calendar
20156  * @extends Roo.bootstrap.Component
20157  * Bootstrap Calendar class
20158  * @cfg {Boolean} loadMask (true|false) default false
20159  * @cfg {Object} header generate the user specific header of the calendar, default false
20160
20161  * @constructor
20162  * Create a new Container
20163  * @param {Object} config The config object
20164  */
20165
20166
20167
20168 Roo.bootstrap.Calendar = function(config){
20169     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20170      this.addEvents({
20171         /**
20172              * @event select
20173              * Fires when a date is selected
20174              * @param {DatePicker} this
20175              * @param {Date} date The selected date
20176              */
20177         'select': true,
20178         /**
20179              * @event monthchange
20180              * Fires when the displayed month changes 
20181              * @param {DatePicker} this
20182              * @param {Date} date The selected month
20183              */
20184         'monthchange': true,
20185         /**
20186              * @event evententer
20187              * Fires when mouse over an event
20188              * @param {Calendar} this
20189              * @param {event} Event
20190              */
20191         'evententer': true,
20192         /**
20193              * @event eventleave
20194              * Fires when the mouse leaves an
20195              * @param {Calendar} this
20196              * @param {event}
20197              */
20198         'eventleave': true,
20199         /**
20200              * @event eventclick
20201              * Fires when the mouse click an
20202              * @param {Calendar} this
20203              * @param {event}
20204              */
20205         'eventclick': true
20206         
20207     });
20208
20209 };
20210
20211 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20212     
20213           /**
20214      * @cfg {Roo.data.Store} store
20215      * The data source for the calendar
20216      */
20217         store : false,
20218      /**
20219      * @cfg {Number} startDay
20220      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20221      */
20222     startDay : 0,
20223     
20224     loadMask : false,
20225     
20226     header : false,
20227       
20228     getAutoCreate : function(){
20229         
20230         
20231         var fc_button = function(name, corner, style, content ) {
20232             return Roo.apply({},{
20233                 tag : 'span',
20234                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20235                          (corner.length ?
20236                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20237                             ''
20238                         ),
20239                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20240                 unselectable: 'on'
20241             });
20242         };
20243         
20244         var header = {};
20245         
20246         if(!this.header){
20247             header = {
20248                 tag : 'table',
20249                 cls : 'fc-header',
20250                 style : 'width:100%',
20251                 cn : [
20252                     {
20253                         tag: 'tr',
20254                         cn : [
20255                             {
20256                                 tag : 'td',
20257                                 cls : 'fc-header-left',
20258                                 cn : [
20259                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20260                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20261                                     { tag: 'span', cls: 'fc-header-space' },
20262                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20263
20264
20265                                 ]
20266                             },
20267
20268                             {
20269                                 tag : 'td',
20270                                 cls : 'fc-header-center',
20271                                 cn : [
20272                                     {
20273                                         tag: 'span',
20274                                         cls: 'fc-header-title',
20275                                         cn : {
20276                                             tag: 'H2',
20277                                             html : 'month / year'
20278                                         }
20279                                     }
20280
20281                                 ]
20282                             },
20283                             {
20284                                 tag : 'td',
20285                                 cls : 'fc-header-right',
20286                                 cn : [
20287                               /*      fc_button('month', 'left', '', 'month' ),
20288                                     fc_button('week', '', '', 'week' ),
20289                                     fc_button('day', 'right', '', 'day' )
20290                                 */    
20291
20292                                 ]
20293                             }
20294
20295                         ]
20296                     }
20297                 ]
20298             };
20299         }
20300         
20301         header = this.header;
20302         
20303        
20304         var cal_heads = function() {
20305             var ret = [];
20306             // fixme - handle this.
20307             
20308             for (var i =0; i < Date.dayNames.length; i++) {
20309                 var d = Date.dayNames[i];
20310                 ret.push({
20311                     tag: 'th',
20312                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20313                     html : d.substring(0,3)
20314                 });
20315                 
20316             }
20317             ret[0].cls += ' fc-first';
20318             ret[6].cls += ' fc-last';
20319             return ret;
20320         };
20321         var cal_cell = function(n) {
20322             return  {
20323                 tag: 'td',
20324                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20325                 cn : [
20326                     {
20327                         cn : [
20328                             {
20329                                 cls: 'fc-day-number',
20330                                 html: 'D'
20331                             },
20332                             {
20333                                 cls: 'fc-day-content',
20334                              
20335                                 cn : [
20336                                      {
20337                                         style: 'position: relative;' // height: 17px;
20338                                     }
20339                                 ]
20340                             }
20341                             
20342                             
20343                         ]
20344                     }
20345                 ]
20346                 
20347             }
20348         };
20349         var cal_rows = function() {
20350             
20351             var ret = [];
20352             for (var r = 0; r < 6; r++) {
20353                 var row= {
20354                     tag : 'tr',
20355                     cls : 'fc-week',
20356                     cn : []
20357                 };
20358                 
20359                 for (var i =0; i < Date.dayNames.length; i++) {
20360                     var d = Date.dayNames[i];
20361                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20362
20363                 }
20364                 row.cn[0].cls+=' fc-first';
20365                 row.cn[0].cn[0].style = 'min-height:90px';
20366                 row.cn[6].cls+=' fc-last';
20367                 ret.push(row);
20368                 
20369             }
20370             ret[0].cls += ' fc-first';
20371             ret[4].cls += ' fc-prev-last';
20372             ret[5].cls += ' fc-last';
20373             return ret;
20374             
20375         };
20376         
20377         var cal_table = {
20378             tag: 'table',
20379             cls: 'fc-border-separate',
20380             style : 'width:100%',
20381             cellspacing  : 0,
20382             cn : [
20383                 { 
20384                     tag: 'thead',
20385                     cn : [
20386                         { 
20387                             tag: 'tr',
20388                             cls : 'fc-first fc-last',
20389                             cn : cal_heads()
20390                         }
20391                     ]
20392                 },
20393                 { 
20394                     tag: 'tbody',
20395                     cn : cal_rows()
20396                 }
20397                   
20398             ]
20399         };
20400          
20401          var cfg = {
20402             cls : 'fc fc-ltr',
20403             cn : [
20404                 header,
20405                 {
20406                     cls : 'fc-content',
20407                     style : "position: relative;",
20408                     cn : [
20409                         {
20410                             cls : 'fc-view fc-view-month fc-grid',
20411                             style : 'position: relative',
20412                             unselectable : 'on',
20413                             cn : [
20414                                 {
20415                                     cls : 'fc-event-container',
20416                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20417                                 },
20418                                 cal_table
20419                             ]
20420                         }
20421                     ]
20422     
20423                 }
20424            ] 
20425             
20426         };
20427         
20428          
20429         
20430         return cfg;
20431     },
20432     
20433     
20434     initEvents : function()
20435     {
20436         if(!this.store){
20437             throw "can not find store for calendar";
20438         }
20439         
20440         var mark = {
20441             tag: "div",
20442             cls:"x-dlg-mask",
20443             style: "text-align:center",
20444             cn: [
20445                 {
20446                     tag: "div",
20447                     style: "background-color:white;width:50%;margin:250 auto",
20448                     cn: [
20449                         {
20450                             tag: "img",
20451                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20452                         },
20453                         {
20454                             tag: "span",
20455                             html: "Loading"
20456                         }
20457                         
20458                     ]
20459                 }
20460             ]
20461         };
20462         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20463         
20464         var size = this.el.select('.fc-content', true).first().getSize();
20465         this.maskEl.setSize(size.width, size.height);
20466         this.maskEl.enableDisplayMode("block");
20467         if(!this.loadMask){
20468             this.maskEl.hide();
20469         }
20470         
20471         this.store = Roo.factory(this.store, Roo.data);
20472         this.store.on('load', this.onLoad, this);
20473         this.store.on('beforeload', this.onBeforeLoad, this);
20474         
20475         this.resize();
20476         
20477         this.cells = this.el.select('.fc-day',true);
20478         //Roo.log(this.cells);
20479         this.textNodes = this.el.query('.fc-day-number');
20480         this.cells.addClassOnOver('fc-state-hover');
20481         
20482         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20483         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20484         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20485         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20486         
20487         this.on('monthchange', this.onMonthChange, this);
20488         
20489         this.update(new Date().clearTime());
20490     },
20491     
20492     resize : function() {
20493         var sz  = this.el.getSize();
20494         
20495         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20496         this.el.select('.fc-day-content div',true).setHeight(34);
20497     },
20498     
20499     
20500     // private
20501     showPrevMonth : function(e){
20502         this.update(this.activeDate.add("mo", -1));
20503     },
20504     showToday : function(e){
20505         this.update(new Date().clearTime());
20506     },
20507     // private
20508     showNextMonth : function(e){
20509         this.update(this.activeDate.add("mo", 1));
20510     },
20511
20512     // private
20513     showPrevYear : function(){
20514         this.update(this.activeDate.add("y", -1));
20515     },
20516
20517     // private
20518     showNextYear : function(){
20519         this.update(this.activeDate.add("y", 1));
20520     },
20521
20522     
20523    // private
20524     update : function(date)
20525     {
20526         var vd = this.activeDate;
20527         this.activeDate = date;
20528 //        if(vd && this.el){
20529 //            var t = date.getTime();
20530 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20531 //                Roo.log('using add remove');
20532 //                
20533 //                this.fireEvent('monthchange', this, date);
20534 //                
20535 //                this.cells.removeClass("fc-state-highlight");
20536 //                this.cells.each(function(c){
20537 //                   if(c.dateValue == t){
20538 //                       c.addClass("fc-state-highlight");
20539 //                       setTimeout(function(){
20540 //                            try{c.dom.firstChild.focus();}catch(e){}
20541 //                       }, 50);
20542 //                       return false;
20543 //                   }
20544 //                   return true;
20545 //                });
20546 //                return;
20547 //            }
20548 //        }
20549         
20550         var days = date.getDaysInMonth();
20551         
20552         var firstOfMonth = date.getFirstDateOfMonth();
20553         var startingPos = firstOfMonth.getDay()-this.startDay;
20554         
20555         if(startingPos < this.startDay){
20556             startingPos += 7;
20557         }
20558         
20559         var pm = date.add(Date.MONTH, -1);
20560         var prevStart = pm.getDaysInMonth()-startingPos;
20561 //        
20562         this.cells = this.el.select('.fc-day',true);
20563         this.textNodes = this.el.query('.fc-day-number');
20564         this.cells.addClassOnOver('fc-state-hover');
20565         
20566         var cells = this.cells.elements;
20567         var textEls = this.textNodes;
20568         
20569         Roo.each(cells, function(cell){
20570             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20571         });
20572         
20573         days += startingPos;
20574
20575         // convert everything to numbers so it's fast
20576         var day = 86400000;
20577         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20578         //Roo.log(d);
20579         //Roo.log(pm);
20580         //Roo.log(prevStart);
20581         
20582         var today = new Date().clearTime().getTime();
20583         var sel = date.clearTime().getTime();
20584         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20585         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20586         var ddMatch = this.disabledDatesRE;
20587         var ddText = this.disabledDatesText;
20588         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20589         var ddaysText = this.disabledDaysText;
20590         var format = this.format;
20591         
20592         var setCellClass = function(cal, cell){
20593             cell.row = 0;
20594             cell.events = [];
20595             cell.more = [];
20596             //Roo.log('set Cell Class');
20597             cell.title = "";
20598             var t = d.getTime();
20599             
20600             //Roo.log(d);
20601             
20602             cell.dateValue = t;
20603             if(t == today){
20604                 cell.className += " fc-today";
20605                 cell.className += " fc-state-highlight";
20606                 cell.title = cal.todayText;
20607             }
20608             if(t == sel){
20609                 // disable highlight in other month..
20610                 //cell.className += " fc-state-highlight";
20611                 
20612             }
20613             // disabling
20614             if(t < min) {
20615                 cell.className = " fc-state-disabled";
20616                 cell.title = cal.minText;
20617                 return;
20618             }
20619             if(t > max) {
20620                 cell.className = " fc-state-disabled";
20621                 cell.title = cal.maxText;
20622                 return;
20623             }
20624             if(ddays){
20625                 if(ddays.indexOf(d.getDay()) != -1){
20626                     cell.title = ddaysText;
20627                     cell.className = " fc-state-disabled";
20628                 }
20629             }
20630             if(ddMatch && format){
20631                 var fvalue = d.dateFormat(format);
20632                 if(ddMatch.test(fvalue)){
20633                     cell.title = ddText.replace("%0", fvalue);
20634                     cell.className = " fc-state-disabled";
20635                 }
20636             }
20637             
20638             if (!cell.initialClassName) {
20639                 cell.initialClassName = cell.dom.className;
20640             }
20641             
20642             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20643         };
20644
20645         var i = 0;
20646         
20647         for(; i < startingPos; i++) {
20648             textEls[i].innerHTML = (++prevStart);
20649             d.setDate(d.getDate()+1);
20650             
20651             cells[i].className = "fc-past fc-other-month";
20652             setCellClass(this, cells[i]);
20653         }
20654         
20655         var intDay = 0;
20656         
20657         for(; i < days; i++){
20658             intDay = i - startingPos + 1;
20659             textEls[i].innerHTML = (intDay);
20660             d.setDate(d.getDate()+1);
20661             
20662             cells[i].className = ''; // "x-date-active";
20663             setCellClass(this, cells[i]);
20664         }
20665         var extraDays = 0;
20666         
20667         for(; i < 42; i++) {
20668             textEls[i].innerHTML = (++extraDays);
20669             d.setDate(d.getDate()+1);
20670             
20671             cells[i].className = "fc-future fc-other-month";
20672             setCellClass(this, cells[i]);
20673         }
20674         
20675         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20676         
20677         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20678         
20679         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20680         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20681         
20682         if(totalRows != 6){
20683             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20684             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20685         }
20686         
20687         this.fireEvent('monthchange', this, date);
20688         
20689         
20690         /*
20691         if(!this.internalRender){
20692             var main = this.el.dom.firstChild;
20693             var w = main.offsetWidth;
20694             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20695             Roo.fly(main).setWidth(w);
20696             this.internalRender = true;
20697             // opera does not respect the auto grow header center column
20698             // then, after it gets a width opera refuses to recalculate
20699             // without a second pass
20700             if(Roo.isOpera && !this.secondPass){
20701                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20702                 this.secondPass = true;
20703                 this.update.defer(10, this, [date]);
20704             }
20705         }
20706         */
20707         
20708     },
20709     
20710     findCell : function(dt) {
20711         dt = dt.clearTime().getTime();
20712         var ret = false;
20713         this.cells.each(function(c){
20714             //Roo.log("check " +c.dateValue + '?=' + dt);
20715             if(c.dateValue == dt){
20716                 ret = c;
20717                 return false;
20718             }
20719             return true;
20720         });
20721         
20722         return ret;
20723     },
20724     
20725     findCells : function(ev) {
20726         var s = ev.start.clone().clearTime().getTime();
20727        // Roo.log(s);
20728         var e= ev.end.clone().clearTime().getTime();
20729        // Roo.log(e);
20730         var ret = [];
20731         this.cells.each(function(c){
20732              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20733             
20734             if(c.dateValue > e){
20735                 return ;
20736             }
20737             if(c.dateValue < s){
20738                 return ;
20739             }
20740             ret.push(c);
20741         });
20742         
20743         return ret;    
20744     },
20745     
20746 //    findBestRow: function(cells)
20747 //    {
20748 //        var ret = 0;
20749 //        
20750 //        for (var i =0 ; i < cells.length;i++) {
20751 //            ret  = Math.max(cells[i].rows || 0,ret);
20752 //        }
20753 //        return ret;
20754 //        
20755 //    },
20756     
20757     
20758     addItem : function(ev)
20759     {
20760         // look for vertical location slot in
20761         var cells = this.findCells(ev);
20762         
20763 //        ev.row = this.findBestRow(cells);
20764         
20765         // work out the location.
20766         
20767         var crow = false;
20768         var rows = [];
20769         for(var i =0; i < cells.length; i++) {
20770             
20771             cells[i].row = cells[0].row;
20772             
20773             if(i == 0){
20774                 cells[i].row = cells[i].row + 1;
20775             }
20776             
20777             if (!crow) {
20778                 crow = {
20779                     start : cells[i],
20780                     end :  cells[i]
20781                 };
20782                 continue;
20783             }
20784             if (crow.start.getY() == cells[i].getY()) {
20785                 // on same row.
20786                 crow.end = cells[i];
20787                 continue;
20788             }
20789             // different row.
20790             rows.push(crow);
20791             crow = {
20792                 start: cells[i],
20793                 end : cells[i]
20794             };
20795             
20796         }
20797         
20798         rows.push(crow);
20799         ev.els = [];
20800         ev.rows = rows;
20801         ev.cells = cells;
20802         
20803         cells[0].events.push(ev);
20804         
20805         this.calevents.push(ev);
20806     },
20807     
20808     clearEvents: function() {
20809         
20810         if(!this.calevents){
20811             return;
20812         }
20813         
20814         Roo.each(this.cells.elements, function(c){
20815             c.row = 0;
20816             c.events = [];
20817             c.more = [];
20818         });
20819         
20820         Roo.each(this.calevents, function(e) {
20821             Roo.each(e.els, function(el) {
20822                 el.un('mouseenter' ,this.onEventEnter, this);
20823                 el.un('mouseleave' ,this.onEventLeave, this);
20824                 el.remove();
20825             },this);
20826         },this);
20827         
20828         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20829             e.remove();
20830         });
20831         
20832     },
20833     
20834     renderEvents: function()
20835     {   
20836         var _this = this;
20837         
20838         this.cells.each(function(c) {
20839             
20840             if(c.row < 5){
20841                 return;
20842             }
20843             
20844             var ev = c.events;
20845             
20846             var r = 4;
20847             if(c.row != c.events.length){
20848                 r = 4 - (4 - (c.row - c.events.length));
20849             }
20850             
20851             c.events = ev.slice(0, r);
20852             c.more = ev.slice(r);
20853             
20854             if(c.more.length && c.more.length == 1){
20855                 c.events.push(c.more.pop());
20856             }
20857             
20858             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20859             
20860         });
20861             
20862         this.cells.each(function(c) {
20863             
20864             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20865             
20866             
20867             for (var e = 0; e < c.events.length; e++){
20868                 var ev = c.events[e];
20869                 var rows = ev.rows;
20870                 
20871                 for(var i = 0; i < rows.length; i++) {
20872                 
20873                     // how many rows should it span..
20874
20875                     var  cfg = {
20876                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20877                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20878
20879                         unselectable : "on",
20880                         cn : [
20881                             {
20882                                 cls: 'fc-event-inner',
20883                                 cn : [
20884     //                                {
20885     //                                  tag:'span',
20886     //                                  cls: 'fc-event-time',
20887     //                                  html : cells.length > 1 ? '' : ev.time
20888     //                                },
20889                                     {
20890                                       tag:'span',
20891                                       cls: 'fc-event-title',
20892                                       html : String.format('{0}', ev.title)
20893                                     }
20894
20895
20896                                 ]
20897                             },
20898                             {
20899                                 cls: 'ui-resizable-handle ui-resizable-e',
20900                                 html : '&nbsp;&nbsp;&nbsp'
20901                             }
20902
20903                         ]
20904                     };
20905
20906                     if (i == 0) {
20907                         cfg.cls += ' fc-event-start';
20908                     }
20909                     if ((i+1) == rows.length) {
20910                         cfg.cls += ' fc-event-end';
20911                     }
20912
20913                     var ctr = _this.el.select('.fc-event-container',true).first();
20914                     var cg = ctr.createChild(cfg);
20915
20916                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20917                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20918
20919                     var r = (c.more.length) ? 1 : 0;
20920                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20921                     cg.setWidth(ebox.right - sbox.x -2);
20922
20923                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20924                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20925                     cg.on('click', _this.onEventClick, _this, ev);
20926
20927                     ev.els.push(cg);
20928                     
20929                 }
20930                 
20931             }
20932             
20933             
20934             if(c.more.length){
20935                 var  cfg = {
20936                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20937                     style : 'position: absolute',
20938                     unselectable : "on",
20939                     cn : [
20940                         {
20941                             cls: 'fc-event-inner',
20942                             cn : [
20943                                 {
20944                                   tag:'span',
20945                                   cls: 'fc-event-title',
20946                                   html : 'More'
20947                                 }
20948
20949
20950                             ]
20951                         },
20952                         {
20953                             cls: 'ui-resizable-handle ui-resizable-e',
20954                             html : '&nbsp;&nbsp;&nbsp'
20955                         }
20956
20957                     ]
20958                 };
20959
20960                 var ctr = _this.el.select('.fc-event-container',true).first();
20961                 var cg = ctr.createChild(cfg);
20962
20963                 var sbox = c.select('.fc-day-content',true).first().getBox();
20964                 var ebox = c.select('.fc-day-content',true).first().getBox();
20965                 //Roo.log(cg);
20966                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20967                 cg.setWidth(ebox.right - sbox.x -2);
20968
20969                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20970                 
20971             }
20972             
20973         });
20974         
20975         
20976         
20977     },
20978     
20979     onEventEnter: function (e, el,event,d) {
20980         this.fireEvent('evententer', this, el, event);
20981     },
20982     
20983     onEventLeave: function (e, el,event,d) {
20984         this.fireEvent('eventleave', this, el, event);
20985     },
20986     
20987     onEventClick: function (e, el,event,d) {
20988         this.fireEvent('eventclick', this, el, event);
20989     },
20990     
20991     onMonthChange: function () {
20992         this.store.load();
20993     },
20994     
20995     onMoreEventClick: function(e, el, more)
20996     {
20997         var _this = this;
20998         
20999         this.calpopover.placement = 'right';
21000         this.calpopover.setTitle('More');
21001         
21002         this.calpopover.setContent('');
21003         
21004         var ctr = this.calpopover.el.select('.popover-content', true).first();
21005         
21006         Roo.each(more, function(m){
21007             var cfg = {
21008                 cls : 'fc-event-hori fc-event-draggable',
21009                 html : m.title
21010             };
21011             var cg = ctr.createChild(cfg);
21012             
21013             cg.on('click', _this.onEventClick, _this, m);
21014         });
21015         
21016         this.calpopover.show(el);
21017         
21018         
21019     },
21020     
21021     onLoad: function () 
21022     {   
21023         this.calevents = [];
21024         var cal = this;
21025         
21026         if(this.store.getCount() > 0){
21027             this.store.data.each(function(d){
21028                cal.addItem({
21029                     id : d.data.id,
21030                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21031                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21032                     time : d.data.start_time,
21033                     title : d.data.title,
21034                     description : d.data.description,
21035                     venue : d.data.venue
21036                 });
21037             });
21038         }
21039         
21040         this.renderEvents();
21041         
21042         if(this.calevents.length && this.loadMask){
21043             this.maskEl.hide();
21044         }
21045     },
21046     
21047     onBeforeLoad: function()
21048     {
21049         this.clearEvents();
21050         if(this.loadMask){
21051             this.maskEl.show();
21052         }
21053     }
21054 });
21055
21056  
21057  /*
21058  * - LGPL
21059  *
21060  * element
21061  * 
21062  */
21063
21064 /**
21065  * @class Roo.bootstrap.Popover
21066  * @extends Roo.bootstrap.Component
21067  * @builder-top
21068  * Bootstrap Popover class
21069  * @cfg {String} html contents of the popover   (or false to use children..)
21070  * @cfg {String} title of popover (or false to hide)
21071  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21072  * @cfg {String} trigger click || hover (or false to trigger manually)
21073  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21074  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21075  *      - if false and it has a 'parent' then it will be automatically added to that element
21076  *      - if string - Roo.get  will be called 
21077  * @cfg {Number} delay - delay before showing
21078  
21079  * @constructor
21080  * Create a new Popover
21081  * @param {Object} config The config object
21082  */
21083
21084 Roo.bootstrap.Popover = function(config){
21085     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21086     
21087     this.addEvents({
21088         // raw events
21089          /**
21090          * @event show
21091          * After the popover show
21092          * 
21093          * @param {Roo.bootstrap.Popover} this
21094          */
21095         "show" : true,
21096         /**
21097          * @event hide
21098          * After the popover hide
21099          * 
21100          * @param {Roo.bootstrap.Popover} this
21101          */
21102         "hide" : true
21103     });
21104 };
21105
21106 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21107     
21108     title: false,
21109     html: false,
21110     
21111     placement : 'right',
21112     trigger : 'hover', // hover
21113     modal : false,
21114     delay : 0,
21115     
21116     over: false,
21117     
21118     can_build_overlaid : false,
21119     
21120     maskEl : false, // the mask element
21121     headerEl : false,
21122     contentEl : false,
21123     alignEl : false, // when show is called with an element - this get's stored.
21124     
21125     getChildContainer : function()
21126     {
21127         return this.contentEl;
21128         
21129     },
21130     getPopoverHeader : function()
21131     {
21132         this.title = true; // flag not to hide it..
21133         this.headerEl.addClass('p-0');
21134         return this.headerEl
21135     },
21136     
21137     
21138     getAutoCreate : function(){
21139          
21140         var cfg = {
21141            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21142            style: 'display:block',
21143            cn : [
21144                 {
21145                     cls : 'arrow'
21146                 },
21147                 {
21148                     cls : 'popover-inner ',
21149                     cn : [
21150                         {
21151                             tag: 'h3',
21152                             cls: 'popover-title popover-header',
21153                             html : this.title === false ? '' : this.title
21154                         },
21155                         {
21156                             cls : 'popover-content popover-body '  + (this.cls || ''),
21157                             html : this.html || ''
21158                         }
21159                     ]
21160                     
21161                 }
21162            ]
21163         };
21164         
21165         return cfg;
21166     },
21167     /**
21168      * @param {string} the title
21169      */
21170     setTitle: function(str)
21171     {
21172         this.title = str;
21173         if (this.el) {
21174             this.headerEl.dom.innerHTML = str;
21175         }
21176         
21177     },
21178     /**
21179      * @param {string} the body content
21180      */
21181     setContent: function(str)
21182     {
21183         this.html = str;
21184         if (this.contentEl) {
21185             this.contentEl.dom.innerHTML = str;
21186         }
21187         
21188     },
21189     // as it get's added to the bottom of the page.
21190     onRender : function(ct, position)
21191     {
21192         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21193         
21194         
21195         
21196         if(!this.el){
21197             var cfg = Roo.apply({},  this.getAutoCreate());
21198             cfg.id = Roo.id();
21199             
21200             if (this.cls) {
21201                 cfg.cls += ' ' + this.cls;
21202             }
21203             if (this.style) {
21204                 cfg.style = this.style;
21205             }
21206             //Roo.log("adding to ");
21207             this.el = Roo.get(document.body).createChild(cfg, position);
21208 //            Roo.log(this.el);
21209         }
21210         
21211         this.contentEl = this.el.select('.popover-content',true).first();
21212         this.headerEl =  this.el.select('.popover-title',true).first();
21213         
21214         var nitems = [];
21215         if(typeof(this.items) != 'undefined'){
21216             var items = this.items;
21217             delete this.items;
21218
21219             for(var i =0;i < items.length;i++) {
21220                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21221             }
21222         }
21223
21224         this.items = nitems;
21225         
21226         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21227         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21228         
21229         
21230         
21231         this.initEvents();
21232     },
21233     
21234     resizeMask : function()
21235     {
21236         this.maskEl.setSize(
21237             Roo.lib.Dom.getViewWidth(true),
21238             Roo.lib.Dom.getViewHeight(true)
21239         );
21240     },
21241     
21242     initEvents : function()
21243     {
21244         
21245         if (!this.modal) { 
21246             Roo.bootstrap.Popover.register(this);
21247         }
21248          
21249         this.arrowEl = this.el.select('.arrow',true).first();
21250         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21251         this.el.enableDisplayMode('block');
21252         this.el.hide();
21253  
21254         
21255         if (this.over === false && !this.parent()) {
21256             return; 
21257         }
21258         if (this.triggers === false) {
21259             return;
21260         }
21261          
21262         // support parent
21263         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21264         var triggers = this.trigger ? this.trigger.split(' ') : [];
21265         Roo.each(triggers, function(trigger) {
21266         
21267             if (trigger == 'click') {
21268                 on_el.on('click', this.toggle, this);
21269             } else if (trigger != 'manual') {
21270                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21271                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21272       
21273                 on_el.on(eventIn  ,this.enter, this);
21274                 on_el.on(eventOut, this.leave, this);
21275             }
21276         }, this);
21277     },
21278     
21279     
21280     // private
21281     timeout : null,
21282     hoverState : null,
21283     
21284     toggle : function () {
21285         this.hoverState == 'in' ? this.leave() : this.enter();
21286     },
21287     
21288     enter : function () {
21289         
21290         clearTimeout(this.timeout);
21291     
21292         this.hoverState = 'in';
21293     
21294         if (!this.delay || !this.delay.show) {
21295             this.show();
21296             return;
21297         }
21298         var _t = this;
21299         this.timeout = setTimeout(function () {
21300             if (_t.hoverState == 'in') {
21301                 _t.show();
21302             }
21303         }, this.delay.show)
21304     },
21305     
21306     leave : function() {
21307         clearTimeout(this.timeout);
21308     
21309         this.hoverState = 'out';
21310     
21311         if (!this.delay || !this.delay.hide) {
21312             this.hide();
21313             return;
21314         }
21315         var _t = this;
21316         this.timeout = setTimeout(function () {
21317             if (_t.hoverState == 'out') {
21318                 _t.hide();
21319             }
21320         }, this.delay.hide)
21321     },
21322     /**
21323      * Show the popover
21324      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21325      * @param {string} (left|right|top|bottom) position
21326      */
21327     show : function (on_el, placement)
21328     {
21329         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21330         on_el = on_el || false; // default to false
21331          
21332         if (!on_el) {
21333             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21334                 on_el = this.parent().el;
21335             } else if (this.over) {
21336                 on_el = Roo.get(this.over);
21337             }
21338             
21339         }
21340         
21341         this.alignEl = Roo.get( on_el );
21342
21343         if (!this.el) {
21344             this.render(document.body);
21345         }
21346         
21347         
21348          
21349         
21350         if (this.title === false) {
21351             this.headerEl.hide();
21352         }
21353         
21354        
21355         this.el.show();
21356         this.el.dom.style.display = 'block';
21357          
21358  
21359         if (this.alignEl) {
21360             this.updatePosition(this.placement, true);
21361              
21362         } else {
21363             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21364             var es = this.el.getSize();
21365             var x = Roo.lib.Dom.getViewWidth()/2;
21366             var y = Roo.lib.Dom.getViewHeight()/2;
21367             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21368             
21369         }
21370
21371         
21372         //var arrow = this.el.select('.arrow',true).first();
21373         //arrow.set(align[2], 
21374         
21375         this.el.addClass('in');
21376         
21377          
21378         
21379         this.hoverState = 'in';
21380         
21381         if (this.modal) {
21382             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21383             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21384             this.maskEl.dom.style.display = 'block';
21385             this.maskEl.addClass('show');
21386         }
21387         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21388  
21389         this.fireEvent('show', this);
21390         
21391     },
21392     /**
21393      * fire this manually after loading a grid in the table for example
21394      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21395      * @param {Boolean} try and move it if we cant get right position.
21396      */
21397     updatePosition : function(placement, try_move)
21398     {
21399         // allow for calling with no parameters
21400         placement = placement   ? placement :  this.placement;
21401         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21402         
21403         this.el.removeClass([
21404             'fade','top','bottom', 'left', 'right','in',
21405             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21406         ]);
21407         this.el.addClass(placement + ' bs-popover-' + placement);
21408         
21409         if (!this.alignEl ) {
21410             return false;
21411         }
21412         
21413         switch (placement) {
21414             case 'right':
21415                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21416                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21417                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21418                     //normal display... or moved up/down.
21419                     this.el.setXY(offset);
21420                     var xy = this.alignEl.getAnchorXY('tr', false);
21421                     xy[0]+=2;xy[1]+=5;
21422                     this.arrowEl.setXY(xy);
21423                     return true;
21424                 }
21425                 // continue through...
21426                 return this.updatePosition('left', false);
21427                 
21428             
21429             case 'left':
21430                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21431                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21432                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21433                     //normal display... or moved up/down.
21434                     this.el.setXY(offset);
21435                     var xy = this.alignEl.getAnchorXY('tl', false);
21436                     xy[0]-=10;xy[1]+=5; // << fix me
21437                     this.arrowEl.setXY(xy);
21438                     return true;
21439                 }
21440                 // call self...
21441                 return this.updatePosition('right', false);
21442             
21443             case 'top':
21444                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21445                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21446                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21447                     //normal display... or moved up/down.
21448                     this.el.setXY(offset);
21449                     var xy = this.alignEl.getAnchorXY('t', false);
21450                     xy[1]-=10; // << fix me
21451                     this.arrowEl.setXY(xy);
21452                     return true;
21453                 }
21454                 // fall through
21455                return this.updatePosition('bottom', false);
21456             
21457             case 'bottom':
21458                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21459                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21460                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21461                     //normal display... or moved up/down.
21462                     this.el.setXY(offset);
21463                     var xy = this.alignEl.getAnchorXY('b', false);
21464                      xy[1]+=2; // << fix me
21465                     this.arrowEl.setXY(xy);
21466                     return true;
21467                 }
21468                 // fall through
21469                 return this.updatePosition('top', false);
21470                 
21471             
21472         }
21473         
21474         
21475         return false;
21476     },
21477     
21478     hide : function()
21479     {
21480         this.el.setXY([0,0]);
21481         this.el.removeClass('in');
21482         this.el.hide();
21483         this.hoverState = null;
21484         this.maskEl.hide(); // always..
21485         this.fireEvent('hide', this);
21486     }
21487     
21488 });
21489
21490
21491 Roo.apply(Roo.bootstrap.Popover, {
21492
21493     alignment : {
21494         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21495         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21496         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21497         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21498     },
21499     
21500     zIndex : 20001,
21501
21502     clickHander : false,
21503     
21504     
21505
21506     onMouseDown : function(e)
21507     {
21508         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21509             /// what is nothing is showing..
21510             this.hideAll();
21511         }
21512          
21513     },
21514     
21515     
21516     popups : [],
21517     
21518     register : function(popup)
21519     {
21520         if (!Roo.bootstrap.Popover.clickHandler) {
21521             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21522         }
21523         // hide other popups.
21524         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21525         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21526         this.hideAll(); //<< why?
21527         //this.popups.push(popup);
21528     },
21529     hideAll : function()
21530     {
21531         this.popups.forEach(function(p) {
21532             p.hide();
21533         });
21534     },
21535     onShow : function() {
21536         Roo.bootstrap.Popover.popups.push(this);
21537     },
21538     onHide : function() {
21539         Roo.bootstrap.Popover.popups.remove(this);
21540     } 
21541
21542 });/*
21543  * - LGPL
21544  *
21545  * Card header - holder for the card header elements.
21546  * 
21547  */
21548
21549 /**
21550  * @class Roo.bootstrap.PopoverNav
21551  * @extends Roo.bootstrap.NavGroup
21552  * Bootstrap Popover header navigation class
21553  * @constructor
21554  * Create a new Popover Header Navigation 
21555  * @param {Object} config The config object
21556  */
21557
21558 Roo.bootstrap.PopoverNav = function(config){
21559     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21560 };
21561
21562 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21563     
21564     
21565     container_method : 'getPopoverHeader' 
21566     
21567      
21568     
21569     
21570    
21571 });
21572
21573  
21574
21575  /*
21576  * - LGPL
21577  *
21578  * Progress
21579  * 
21580  */
21581
21582 /**
21583  * @class Roo.bootstrap.Progress
21584  * @extends Roo.bootstrap.Component
21585  * Bootstrap Progress class
21586  * @cfg {Boolean} striped striped of the progress bar
21587  * @cfg {Boolean} active animated of the progress bar
21588  * 
21589  * 
21590  * @constructor
21591  * Create a new Progress
21592  * @param {Object} config The config object
21593  */
21594
21595 Roo.bootstrap.Progress = function(config){
21596     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21597 };
21598
21599 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21600     
21601     striped : false,
21602     active: false,
21603     
21604     getAutoCreate : function(){
21605         var cfg = {
21606             tag: 'div',
21607             cls: 'progress'
21608         };
21609         
21610         
21611         if(this.striped){
21612             cfg.cls += ' progress-striped';
21613         }
21614       
21615         if(this.active){
21616             cfg.cls += ' active';
21617         }
21618         
21619         
21620         return cfg;
21621     }
21622    
21623 });
21624
21625  
21626
21627  /*
21628  * - LGPL
21629  *
21630  * ProgressBar
21631  * 
21632  */
21633
21634 /**
21635  * @class Roo.bootstrap.ProgressBar
21636  * @extends Roo.bootstrap.Component
21637  * Bootstrap ProgressBar class
21638  * @cfg {Number} aria_valuenow aria-value now
21639  * @cfg {Number} aria_valuemin aria-value min
21640  * @cfg {Number} aria_valuemax aria-value max
21641  * @cfg {String} label label for the progress bar
21642  * @cfg {String} panel (success | info | warning | danger )
21643  * @cfg {String} role role of the progress bar
21644  * @cfg {String} sr_only text
21645  * 
21646  * 
21647  * @constructor
21648  * Create a new ProgressBar
21649  * @param {Object} config The config object
21650  */
21651
21652 Roo.bootstrap.ProgressBar = function(config){
21653     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21654 };
21655
21656 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21657     
21658     aria_valuenow : 0,
21659     aria_valuemin : 0,
21660     aria_valuemax : 100,
21661     label : false,
21662     panel : false,
21663     role : false,
21664     sr_only: false,
21665     
21666     getAutoCreate : function()
21667     {
21668         
21669         var cfg = {
21670             tag: 'div',
21671             cls: 'progress-bar',
21672             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21673         };
21674         
21675         if(this.sr_only){
21676             cfg.cn = {
21677                 tag: 'span',
21678                 cls: 'sr-only',
21679                 html: this.sr_only
21680             }
21681         }
21682         
21683         if(this.role){
21684             cfg.role = this.role;
21685         }
21686         
21687         if(this.aria_valuenow){
21688             cfg['aria-valuenow'] = this.aria_valuenow;
21689         }
21690         
21691         if(this.aria_valuemin){
21692             cfg['aria-valuemin'] = this.aria_valuemin;
21693         }
21694         
21695         if(this.aria_valuemax){
21696             cfg['aria-valuemax'] = this.aria_valuemax;
21697         }
21698         
21699         if(this.label && !this.sr_only){
21700             cfg.html = this.label;
21701         }
21702         
21703         if(this.panel){
21704             cfg.cls += ' progress-bar-' + this.panel;
21705         }
21706         
21707         return cfg;
21708     },
21709     
21710     update : function(aria_valuenow)
21711     {
21712         this.aria_valuenow = aria_valuenow;
21713         
21714         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21715     }
21716    
21717 });
21718
21719  
21720
21721  /*
21722  * - LGPL
21723  *
21724  * column
21725  * 
21726  */
21727
21728 /**
21729  * @class Roo.bootstrap.TabGroup
21730  * @extends Roo.bootstrap.Column
21731  * Bootstrap Column class
21732  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21733  * @cfg {Boolean} carousel true to make the group behave like a carousel
21734  * @cfg {Boolean} bullets show bullets for the panels
21735  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21736  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21737  * @cfg {Boolean} showarrow (true|false) show arrow default true
21738  * 
21739  * @constructor
21740  * Create a new TabGroup
21741  * @param {Object} config The config object
21742  */
21743
21744 Roo.bootstrap.TabGroup = function(config){
21745     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21746     if (!this.navId) {
21747         this.navId = Roo.id();
21748     }
21749     this.tabs = [];
21750     Roo.bootstrap.TabGroup.register(this);
21751     
21752 };
21753
21754 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21755     
21756     carousel : false,
21757     transition : false,
21758     bullets : 0,
21759     timer : 0,
21760     autoslide : false,
21761     slideFn : false,
21762     slideOnTouch : false,
21763     showarrow : true,
21764     
21765     getAutoCreate : function()
21766     {
21767         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21768         
21769         cfg.cls += ' tab-content';
21770         
21771         if (this.carousel) {
21772             cfg.cls += ' carousel slide';
21773             
21774             cfg.cn = [{
21775                cls : 'carousel-inner',
21776                cn : []
21777             }];
21778         
21779             if(this.bullets  && !Roo.isTouch){
21780                 
21781                 var bullets = {
21782                     cls : 'carousel-bullets',
21783                     cn : []
21784                 };
21785                
21786                 if(this.bullets_cls){
21787                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21788                 }
21789                 
21790                 bullets.cn.push({
21791                     cls : 'clear'
21792                 });
21793                 
21794                 cfg.cn[0].cn.push(bullets);
21795             }
21796             
21797             if(this.showarrow){
21798                 cfg.cn[0].cn.push({
21799                     tag : 'div',
21800                     class : 'carousel-arrow',
21801                     cn : [
21802                         {
21803                             tag : 'div',
21804                             class : 'carousel-prev',
21805                             cn : [
21806                                 {
21807                                     tag : 'i',
21808                                     class : 'fa fa-chevron-left'
21809                                 }
21810                             ]
21811                         },
21812                         {
21813                             tag : 'div',
21814                             class : 'carousel-next',
21815                             cn : [
21816                                 {
21817                                     tag : 'i',
21818                                     class : 'fa fa-chevron-right'
21819                                 }
21820                             ]
21821                         }
21822                     ]
21823                 });
21824             }
21825             
21826         }
21827         
21828         return cfg;
21829     },
21830     
21831     initEvents:  function()
21832     {
21833 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21834 //            this.el.on("touchstart", this.onTouchStart, this);
21835 //        }
21836         
21837         if(this.autoslide){
21838             var _this = this;
21839             
21840             this.slideFn = window.setInterval(function() {
21841                 _this.showPanelNext();
21842             }, this.timer);
21843         }
21844         
21845         if(this.showarrow){
21846             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21847             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21848         }
21849         
21850         
21851     },
21852     
21853 //    onTouchStart : function(e, el, o)
21854 //    {
21855 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21856 //            return;
21857 //        }
21858 //        
21859 //        this.showPanelNext();
21860 //    },
21861     
21862     
21863     getChildContainer : function()
21864     {
21865         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21866     },
21867     
21868     /**
21869     * register a Navigation item
21870     * @param {Roo.bootstrap.NavItem} the navitem to add
21871     */
21872     register : function(item)
21873     {
21874         this.tabs.push( item);
21875         item.navId = this.navId; // not really needed..
21876         this.addBullet();
21877     
21878     },
21879     
21880     getActivePanel : function()
21881     {
21882         var r = false;
21883         Roo.each(this.tabs, function(t) {
21884             if (t.active) {
21885                 r = t;
21886                 return false;
21887             }
21888             return null;
21889         });
21890         return r;
21891         
21892     },
21893     getPanelByName : function(n)
21894     {
21895         var r = false;
21896         Roo.each(this.tabs, function(t) {
21897             if (t.tabId == n) {
21898                 r = t;
21899                 return false;
21900             }
21901             return null;
21902         });
21903         return r;
21904     },
21905     indexOfPanel : function(p)
21906     {
21907         var r = false;
21908         Roo.each(this.tabs, function(t,i) {
21909             if (t.tabId == p.tabId) {
21910                 r = i;
21911                 return false;
21912             }
21913             return null;
21914         });
21915         return r;
21916     },
21917     /**
21918      * show a specific panel
21919      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21920      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21921      */
21922     showPanel : function (pan)
21923     {
21924         if(this.transition || typeof(pan) == 'undefined'){
21925             Roo.log("waiting for the transitionend");
21926             return false;
21927         }
21928         
21929         if (typeof(pan) == 'number') {
21930             pan = this.tabs[pan];
21931         }
21932         
21933         if (typeof(pan) == 'string') {
21934             pan = this.getPanelByName(pan);
21935         }
21936         
21937         var cur = this.getActivePanel();
21938         
21939         if(!pan || !cur){
21940             Roo.log('pan or acitve pan is undefined');
21941             return false;
21942         }
21943         
21944         if (pan.tabId == this.getActivePanel().tabId) {
21945             return true;
21946         }
21947         
21948         if (false === cur.fireEvent('beforedeactivate')) {
21949             return false;
21950         }
21951         
21952         if(this.bullets > 0 && !Roo.isTouch){
21953             this.setActiveBullet(this.indexOfPanel(pan));
21954         }
21955         
21956         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21957             
21958             //class="carousel-item carousel-item-next carousel-item-left"
21959             
21960             this.transition = true;
21961             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21962             var lr = dir == 'next' ? 'left' : 'right';
21963             pan.el.addClass(dir); // or prev
21964             pan.el.addClass('carousel-item-' + dir); // or prev
21965             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21966             cur.el.addClass(lr); // or right
21967             pan.el.addClass(lr);
21968             cur.el.addClass('carousel-item-' +lr); // or right
21969             pan.el.addClass('carousel-item-' +lr);
21970             
21971             
21972             var _this = this;
21973             cur.el.on('transitionend', function() {
21974                 Roo.log("trans end?");
21975                 
21976                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21977                 pan.setActive(true);
21978                 
21979                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21980                 cur.setActive(false);
21981                 
21982                 _this.transition = false;
21983                 
21984             }, this, { single:  true } );
21985             
21986             return true;
21987         }
21988         
21989         cur.setActive(false);
21990         pan.setActive(true);
21991         
21992         return true;
21993         
21994     },
21995     showPanelNext : function()
21996     {
21997         var i = this.indexOfPanel(this.getActivePanel());
21998         
21999         if (i >= this.tabs.length - 1 && !this.autoslide) {
22000             return;
22001         }
22002         
22003         if (i >= this.tabs.length - 1 && this.autoslide) {
22004             i = -1;
22005         }
22006         
22007         this.showPanel(this.tabs[i+1]);
22008     },
22009     
22010     showPanelPrev : function()
22011     {
22012         var i = this.indexOfPanel(this.getActivePanel());
22013         
22014         if (i  < 1 && !this.autoslide) {
22015             return;
22016         }
22017         
22018         if (i < 1 && this.autoslide) {
22019             i = this.tabs.length;
22020         }
22021         
22022         this.showPanel(this.tabs[i-1]);
22023     },
22024     
22025     
22026     addBullet: function()
22027     {
22028         if(!this.bullets || Roo.isTouch){
22029             return;
22030         }
22031         var ctr = this.el.select('.carousel-bullets',true).first();
22032         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22033         var bullet = ctr.createChild({
22034             cls : 'bullet bullet-' + i
22035         },ctr.dom.lastChild);
22036         
22037         
22038         var _this = this;
22039         
22040         bullet.on('click', (function(e, el, o, ii, t){
22041
22042             e.preventDefault();
22043
22044             this.showPanel(ii);
22045
22046             if(this.autoslide && this.slideFn){
22047                 clearInterval(this.slideFn);
22048                 this.slideFn = window.setInterval(function() {
22049                     _this.showPanelNext();
22050                 }, this.timer);
22051             }
22052
22053         }).createDelegate(this, [i, bullet], true));
22054                 
22055         
22056     },
22057      
22058     setActiveBullet : function(i)
22059     {
22060         if(Roo.isTouch){
22061             return;
22062         }
22063         
22064         Roo.each(this.el.select('.bullet', true).elements, function(el){
22065             el.removeClass('selected');
22066         });
22067
22068         var bullet = this.el.select('.bullet-' + i, true).first();
22069         
22070         if(!bullet){
22071             return;
22072         }
22073         
22074         bullet.addClass('selected');
22075     }
22076     
22077     
22078   
22079 });
22080
22081  
22082
22083  
22084  
22085 Roo.apply(Roo.bootstrap.TabGroup, {
22086     
22087     groups: {},
22088      /**
22089     * register a Navigation Group
22090     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22091     */
22092     register : function(navgrp)
22093     {
22094         this.groups[navgrp.navId] = navgrp;
22095         
22096     },
22097     /**
22098     * fetch a Navigation Group based on the navigation ID
22099     * if one does not exist , it will get created.
22100     * @param {string} the navgroup to add
22101     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22102     */
22103     get: function(navId) {
22104         if (typeof(this.groups[navId]) == 'undefined') {
22105             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22106         }
22107         return this.groups[navId] ;
22108     }
22109     
22110     
22111     
22112 });
22113
22114  /*
22115  * - LGPL
22116  *
22117  * TabPanel
22118  * 
22119  */
22120
22121 /**
22122  * @class Roo.bootstrap.TabPanel
22123  * @extends Roo.bootstrap.Component
22124  * Bootstrap TabPanel class
22125  * @cfg {Boolean} active panel active
22126  * @cfg {String} html panel content
22127  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22128  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22129  * @cfg {String} href click to link..
22130  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22131  * 
22132  * 
22133  * @constructor
22134  * Create a new TabPanel
22135  * @param {Object} config The config object
22136  */
22137
22138 Roo.bootstrap.TabPanel = function(config){
22139     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22140     this.addEvents({
22141         /**
22142              * @event changed
22143              * Fires when the active status changes
22144              * @param {Roo.bootstrap.TabPanel} this
22145              * @param {Boolean} state the new state
22146             
22147          */
22148         'changed': true,
22149         /**
22150              * @event beforedeactivate
22151              * Fires before a tab is de-activated - can be used to do validation on a form.
22152              * @param {Roo.bootstrap.TabPanel} this
22153              * @return {Boolean} false if there is an error
22154             
22155          */
22156         'beforedeactivate': true
22157      });
22158     
22159     this.tabId = this.tabId || Roo.id();
22160   
22161 };
22162
22163 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22164     
22165     active: false,
22166     html: false,
22167     tabId: false,
22168     navId : false,
22169     href : '',
22170     touchSlide : false,
22171     getAutoCreate : function(){
22172         
22173         
22174         var cfg = {
22175             tag: 'div',
22176             // item is needed for carousel - not sure if it has any effect otherwise
22177             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22178             html: this.html || ''
22179         };
22180         
22181         if(this.active){
22182             cfg.cls += ' active';
22183         }
22184         
22185         if(this.tabId){
22186             cfg.tabId = this.tabId;
22187         }
22188         
22189         
22190         
22191         return cfg;
22192     },
22193     
22194     initEvents:  function()
22195     {
22196         var p = this.parent();
22197         
22198         this.navId = this.navId || p.navId;
22199         
22200         if (typeof(this.navId) != 'undefined') {
22201             // not really needed.. but just in case.. parent should be a NavGroup.
22202             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22203             
22204             tg.register(this);
22205             
22206             var i = tg.tabs.length - 1;
22207             
22208             if(this.active && tg.bullets > 0 && i < tg.bullets){
22209                 tg.setActiveBullet(i);
22210             }
22211         }
22212         
22213         this.el.on('click', this.onClick, this);
22214         
22215         if(Roo.isTouch && this.touchSlide){
22216             this.el.on("touchstart", this.onTouchStart, this);
22217             this.el.on("touchmove", this.onTouchMove, this);
22218             this.el.on("touchend", this.onTouchEnd, this);
22219         }
22220         
22221     },
22222     
22223     onRender : function(ct, position)
22224     {
22225         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22226     },
22227     
22228     setActive : function(state)
22229     {
22230         Roo.log("panel - set active " + this.tabId + "=" + state);
22231         
22232         this.active = state;
22233         if (!state) {
22234             this.el.removeClass('active');
22235             
22236         } else  if (!this.el.hasClass('active')) {
22237             this.el.addClass('active');
22238         }
22239         
22240         this.fireEvent('changed', this, state);
22241     },
22242     
22243     onClick : function(e)
22244     {
22245         e.preventDefault();
22246         
22247         if(!this.href.length){
22248             return;
22249         }
22250         
22251         window.location.href = this.href;
22252     },
22253     
22254     startX : 0,
22255     startY : 0,
22256     endX : 0,
22257     endY : 0,
22258     swiping : false,
22259     
22260     onTouchStart : function(e)
22261     {
22262         this.swiping = false;
22263         
22264         this.startX = e.browserEvent.touches[0].clientX;
22265         this.startY = e.browserEvent.touches[0].clientY;
22266     },
22267     
22268     onTouchMove : function(e)
22269     {
22270         this.swiping = true;
22271         
22272         this.endX = e.browserEvent.touches[0].clientX;
22273         this.endY = e.browserEvent.touches[0].clientY;
22274     },
22275     
22276     onTouchEnd : function(e)
22277     {
22278         if(!this.swiping){
22279             this.onClick(e);
22280             return;
22281         }
22282         
22283         var tabGroup = this.parent();
22284         
22285         if(this.endX > this.startX){ // swiping right
22286             tabGroup.showPanelPrev();
22287             return;
22288         }
22289         
22290         if(this.startX > this.endX){ // swiping left
22291             tabGroup.showPanelNext();
22292             return;
22293         }
22294     }
22295     
22296     
22297 });
22298  
22299
22300  
22301
22302  /*
22303  * - LGPL
22304  *
22305  * DateField
22306  * 
22307  */
22308
22309 /**
22310  * @class Roo.bootstrap.DateField
22311  * @extends Roo.bootstrap.Input
22312  * Bootstrap DateField class
22313  * @cfg {Number} weekStart default 0
22314  * @cfg {String} viewMode default empty, (months|years)
22315  * @cfg {String} minViewMode default empty, (months|years)
22316  * @cfg {Number} startDate default -Infinity
22317  * @cfg {Number} endDate default Infinity
22318  * @cfg {Boolean} todayHighlight default false
22319  * @cfg {Boolean} todayBtn default false
22320  * @cfg {Boolean} calendarWeeks default false
22321  * @cfg {Object} daysOfWeekDisabled default empty
22322  * @cfg {Boolean} singleMode default false (true | false)
22323  * 
22324  * @cfg {Boolean} keyboardNavigation default true
22325  * @cfg {String} language default en
22326  * 
22327  * @constructor
22328  * Create a new DateField
22329  * @param {Object} config The config object
22330  */
22331
22332 Roo.bootstrap.DateField = function(config){
22333     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22334      this.addEvents({
22335             /**
22336              * @event show
22337              * Fires when this field show.
22338              * @param {Roo.bootstrap.DateField} this
22339              * @param {Mixed} date The date value
22340              */
22341             show : true,
22342             /**
22343              * @event show
22344              * Fires when this field hide.
22345              * @param {Roo.bootstrap.DateField} this
22346              * @param {Mixed} date The date value
22347              */
22348             hide : true,
22349             /**
22350              * @event select
22351              * Fires when select a date.
22352              * @param {Roo.bootstrap.DateField} this
22353              * @param {Mixed} date The date value
22354              */
22355             select : true,
22356             /**
22357              * @event beforeselect
22358              * Fires when before select a date.
22359              * @param {Roo.bootstrap.DateField} this
22360              * @param {Mixed} date The date value
22361              */
22362             beforeselect : true
22363         });
22364 };
22365
22366 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22367     
22368     /**
22369      * @cfg {String} format
22370      * The default date format string which can be overriden for localization support.  The format must be
22371      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22372      */
22373     format : "m/d/y",
22374     /**
22375      * @cfg {String} altFormats
22376      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22377      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22378      */
22379     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22380     
22381     weekStart : 0,
22382     
22383     viewMode : '',
22384     
22385     minViewMode : '',
22386     
22387     todayHighlight : false,
22388     
22389     todayBtn: false,
22390     
22391     language: 'en',
22392     
22393     keyboardNavigation: true,
22394     
22395     calendarWeeks: false,
22396     
22397     startDate: -Infinity,
22398     
22399     endDate: Infinity,
22400     
22401     daysOfWeekDisabled: [],
22402     
22403     _events: [],
22404     
22405     singleMode : false,
22406     
22407     UTCDate: function()
22408     {
22409         return new Date(Date.UTC.apply(Date, arguments));
22410     },
22411     
22412     UTCToday: function()
22413     {
22414         var today = new Date();
22415         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22416     },
22417     
22418     getDate: function() {
22419             var d = this.getUTCDate();
22420             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22421     },
22422     
22423     getUTCDate: function() {
22424             return this.date;
22425     },
22426     
22427     setDate: function(d) {
22428             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22429     },
22430     
22431     setUTCDate: function(d) {
22432             this.date = d;
22433             this.setValue(this.formatDate(this.date));
22434     },
22435         
22436     onRender: function(ct, position)
22437     {
22438         
22439         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22440         
22441         this.language = this.language || 'en';
22442         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22443         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22444         
22445         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22446         this.format = this.format || 'm/d/y';
22447         this.isInline = false;
22448         this.isInput = true;
22449         this.component = this.el.select('.add-on', true).first() || false;
22450         this.component = (this.component && this.component.length === 0) ? false : this.component;
22451         this.hasInput = this.component && this.inputEl().length;
22452         
22453         if (typeof(this.minViewMode === 'string')) {
22454             switch (this.minViewMode) {
22455                 case 'months':
22456                     this.minViewMode = 1;
22457                     break;
22458                 case 'years':
22459                     this.minViewMode = 2;
22460                     break;
22461                 default:
22462                     this.minViewMode = 0;
22463                     break;
22464             }
22465         }
22466         
22467         if (typeof(this.viewMode === 'string')) {
22468             switch (this.viewMode) {
22469                 case 'months':
22470                     this.viewMode = 1;
22471                     break;
22472                 case 'years':
22473                     this.viewMode = 2;
22474                     break;
22475                 default:
22476                     this.viewMode = 0;
22477                     break;
22478             }
22479         }
22480                 
22481         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22482         
22483 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22484         
22485         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22486         
22487         this.picker().on('mousedown', this.onMousedown, this);
22488         this.picker().on('click', this.onClick, this);
22489         
22490         this.picker().addClass('datepicker-dropdown');
22491         
22492         this.startViewMode = this.viewMode;
22493         
22494         if(this.singleMode){
22495             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22496                 v.setVisibilityMode(Roo.Element.DISPLAY);
22497                 v.hide();
22498             });
22499             
22500             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22501                 v.setStyle('width', '189px');
22502             });
22503         }
22504         
22505         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22506             if(!this.calendarWeeks){
22507                 v.remove();
22508                 return;
22509             }
22510             
22511             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22512             v.attr('colspan', function(i, val){
22513                 return parseInt(val) + 1;
22514             });
22515         });
22516                         
22517         
22518         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22519         
22520         this.setStartDate(this.startDate);
22521         this.setEndDate(this.endDate);
22522         
22523         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22524         
22525         this.fillDow();
22526         this.fillMonths();
22527         this.update();
22528         this.showMode();
22529         
22530         if(this.isInline) {
22531             this.showPopup();
22532         }
22533     },
22534     
22535     picker : function()
22536     {
22537         return this.pickerEl;
22538 //        return this.el.select('.datepicker', true).first();
22539     },
22540     
22541     fillDow: function()
22542     {
22543         var dowCnt = this.weekStart;
22544         
22545         var dow = {
22546             tag: 'tr',
22547             cn: [
22548                 
22549             ]
22550         };
22551         
22552         if(this.calendarWeeks){
22553             dow.cn.push({
22554                 tag: 'th',
22555                 cls: 'cw',
22556                 html: '&nbsp;'
22557             })
22558         }
22559         
22560         while (dowCnt < this.weekStart + 7) {
22561             dow.cn.push({
22562                 tag: 'th',
22563                 cls: 'dow',
22564                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22565             });
22566         }
22567         
22568         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22569     },
22570     
22571     fillMonths: function()
22572     {    
22573         var i = 0;
22574         var months = this.picker().select('>.datepicker-months td', true).first();
22575         
22576         months.dom.innerHTML = '';
22577         
22578         while (i < 12) {
22579             var month = {
22580                 tag: 'span',
22581                 cls: 'month',
22582                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22583             };
22584             
22585             months.createChild(month);
22586         }
22587         
22588     },
22589     
22590     update: function()
22591     {
22592         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;
22593         
22594         if (this.date < this.startDate) {
22595             this.viewDate = new Date(this.startDate);
22596         } else if (this.date > this.endDate) {
22597             this.viewDate = new Date(this.endDate);
22598         } else {
22599             this.viewDate = new Date(this.date);
22600         }
22601         
22602         this.fill();
22603     },
22604     
22605     fill: function() 
22606     {
22607         var d = new Date(this.viewDate),
22608                 year = d.getUTCFullYear(),
22609                 month = d.getUTCMonth(),
22610                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22611                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22612                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22613                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22614                 currentDate = this.date && this.date.valueOf(),
22615                 today = this.UTCToday();
22616         
22617         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22618         
22619 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22620         
22621 //        this.picker.select('>tfoot th.today').
22622 //                                              .text(dates[this.language].today)
22623 //                                              .toggle(this.todayBtn !== false);
22624     
22625         this.updateNavArrows();
22626         this.fillMonths();
22627                                                 
22628         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22629         
22630         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22631          
22632         prevMonth.setUTCDate(day);
22633         
22634         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22635         
22636         var nextMonth = new Date(prevMonth);
22637         
22638         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22639         
22640         nextMonth = nextMonth.valueOf();
22641         
22642         var fillMonths = false;
22643         
22644         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22645         
22646         while(prevMonth.valueOf() <= nextMonth) {
22647             var clsName = '';
22648             
22649             if (prevMonth.getUTCDay() === this.weekStart) {
22650                 if(fillMonths){
22651                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22652                 }
22653                     
22654                 fillMonths = {
22655                     tag: 'tr',
22656                     cn: []
22657                 };
22658                 
22659                 if(this.calendarWeeks){
22660                     // ISO 8601: First week contains first thursday.
22661                     // ISO also states week starts on Monday, but we can be more abstract here.
22662                     var
22663                     // Start of current week: based on weekstart/current date
22664                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22665                     // Thursday of this week
22666                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22667                     // First Thursday of year, year from thursday
22668                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22669                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22670                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22671                     
22672                     fillMonths.cn.push({
22673                         tag: 'td',
22674                         cls: 'cw',
22675                         html: calWeek
22676                     });
22677                 }
22678             }
22679             
22680             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22681                 clsName += ' old';
22682             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22683                 clsName += ' new';
22684             }
22685             if (this.todayHighlight &&
22686                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22687                 prevMonth.getUTCMonth() == today.getMonth() &&
22688                 prevMonth.getUTCDate() == today.getDate()) {
22689                 clsName += ' today';
22690             }
22691             
22692             if (currentDate && prevMonth.valueOf() === currentDate) {
22693                 clsName += ' active';
22694             }
22695             
22696             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22697                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22698                     clsName += ' disabled';
22699             }
22700             
22701             fillMonths.cn.push({
22702                 tag: 'td',
22703                 cls: 'day ' + clsName,
22704                 html: prevMonth.getDate()
22705             });
22706             
22707             prevMonth.setDate(prevMonth.getDate()+1);
22708         }
22709           
22710         var currentYear = this.date && this.date.getUTCFullYear();
22711         var currentMonth = this.date && this.date.getUTCMonth();
22712         
22713         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22714         
22715         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22716             v.removeClass('active');
22717             
22718             if(currentYear === year && k === currentMonth){
22719                 v.addClass('active');
22720             }
22721             
22722             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22723                 v.addClass('disabled');
22724             }
22725             
22726         });
22727         
22728         
22729         year = parseInt(year/10, 10) * 10;
22730         
22731         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22732         
22733         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22734         
22735         year -= 1;
22736         for (var i = -1; i < 11; i++) {
22737             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22738                 tag: 'span',
22739                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22740                 html: year
22741             });
22742             
22743             year += 1;
22744         }
22745     },
22746     
22747     showMode: function(dir) 
22748     {
22749         if (dir) {
22750             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22751         }
22752         
22753         Roo.each(this.picker().select('>div',true).elements, function(v){
22754             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22755             v.hide();
22756         });
22757         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22758     },
22759     
22760     place: function()
22761     {
22762         if(this.isInline) {
22763             return;
22764         }
22765         
22766         this.picker().removeClass(['bottom', 'top']);
22767         
22768         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22769             /*
22770              * place to the top of element!
22771              *
22772              */
22773             
22774             this.picker().addClass('top');
22775             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22776             
22777             return;
22778         }
22779         
22780         this.picker().addClass('bottom');
22781         
22782         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22783     },
22784     
22785     parseDate : function(value)
22786     {
22787         if(!value || value instanceof Date){
22788             return value;
22789         }
22790         var v = Date.parseDate(value, this.format);
22791         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22792             v = Date.parseDate(value, 'Y-m-d');
22793         }
22794         if(!v && this.altFormats){
22795             if(!this.altFormatsArray){
22796                 this.altFormatsArray = this.altFormats.split("|");
22797             }
22798             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22799                 v = Date.parseDate(value, this.altFormatsArray[i]);
22800             }
22801         }
22802         return v;
22803     },
22804     
22805     formatDate : function(date, fmt)
22806     {   
22807         return (!date || !(date instanceof Date)) ?
22808         date : date.dateFormat(fmt || this.format);
22809     },
22810     
22811     onFocus : function()
22812     {
22813         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22814         this.showPopup();
22815     },
22816     
22817     onBlur : function()
22818     {
22819         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22820         
22821         var d = this.inputEl().getValue();
22822         
22823         this.setValue(d);
22824                 
22825         this.hidePopup();
22826     },
22827     
22828     showPopup : function()
22829     {
22830         this.picker().show();
22831         this.update();
22832         this.place();
22833         
22834         this.fireEvent('showpopup', this, this.date);
22835     },
22836     
22837     hidePopup : function()
22838     {
22839         if(this.isInline) {
22840             return;
22841         }
22842         this.picker().hide();
22843         this.viewMode = this.startViewMode;
22844         this.showMode();
22845         
22846         this.fireEvent('hidepopup', this, this.date);
22847         
22848     },
22849     
22850     onMousedown: function(e)
22851     {
22852         e.stopPropagation();
22853         e.preventDefault();
22854     },
22855     
22856     keyup: function(e)
22857     {
22858         Roo.bootstrap.DateField.superclass.keyup.call(this);
22859         this.update();
22860     },
22861
22862     setValue: function(v)
22863     {
22864         if(this.fireEvent('beforeselect', this, v) !== false){
22865             var d = new Date(this.parseDate(v) ).clearTime();
22866         
22867             if(isNaN(d.getTime())){
22868                 this.date = this.viewDate = '';
22869                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22870                 return;
22871             }
22872
22873             v = this.formatDate(d);
22874
22875             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22876
22877             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22878
22879             this.update();
22880
22881             this.fireEvent('select', this, this.date);
22882         }
22883     },
22884     
22885     getValue: function()
22886     {
22887         return this.formatDate(this.date);
22888     },
22889     
22890     fireKey: function(e)
22891     {
22892         if (!this.picker().isVisible()){
22893             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22894                 this.showPopup();
22895             }
22896             return;
22897         }
22898         
22899         var dateChanged = false,
22900         dir, day, month,
22901         newDate, newViewDate;
22902         
22903         switch(e.keyCode){
22904             case 27: // escape
22905                 this.hidePopup();
22906                 e.preventDefault();
22907                 break;
22908             case 37: // left
22909             case 39: // right
22910                 if (!this.keyboardNavigation) {
22911                     break;
22912                 }
22913                 dir = e.keyCode == 37 ? -1 : 1;
22914                 
22915                 if (e.ctrlKey){
22916                     newDate = this.moveYear(this.date, dir);
22917                     newViewDate = this.moveYear(this.viewDate, dir);
22918                 } else if (e.shiftKey){
22919                     newDate = this.moveMonth(this.date, dir);
22920                     newViewDate = this.moveMonth(this.viewDate, dir);
22921                 } else {
22922                     newDate = new Date(this.date);
22923                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22924                     newViewDate = new Date(this.viewDate);
22925                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22926                 }
22927                 if (this.dateWithinRange(newDate)){
22928                     this.date = newDate;
22929                     this.viewDate = newViewDate;
22930                     this.setValue(this.formatDate(this.date));
22931 //                    this.update();
22932                     e.preventDefault();
22933                     dateChanged = true;
22934                 }
22935                 break;
22936             case 38: // up
22937             case 40: // down
22938                 if (!this.keyboardNavigation) {
22939                     break;
22940                 }
22941                 dir = e.keyCode == 38 ? -1 : 1;
22942                 if (e.ctrlKey){
22943                     newDate = this.moveYear(this.date, dir);
22944                     newViewDate = this.moveYear(this.viewDate, dir);
22945                 } else if (e.shiftKey){
22946                     newDate = this.moveMonth(this.date, dir);
22947                     newViewDate = this.moveMonth(this.viewDate, dir);
22948                 } else {
22949                     newDate = new Date(this.date);
22950                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22951                     newViewDate = new Date(this.viewDate);
22952                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22953                 }
22954                 if (this.dateWithinRange(newDate)){
22955                     this.date = newDate;
22956                     this.viewDate = newViewDate;
22957                     this.setValue(this.formatDate(this.date));
22958 //                    this.update();
22959                     e.preventDefault();
22960                     dateChanged = true;
22961                 }
22962                 break;
22963             case 13: // enter
22964                 this.setValue(this.formatDate(this.date));
22965                 this.hidePopup();
22966                 e.preventDefault();
22967                 break;
22968             case 9: // tab
22969                 this.setValue(this.formatDate(this.date));
22970                 this.hidePopup();
22971                 break;
22972             case 16: // shift
22973             case 17: // ctrl
22974             case 18: // alt
22975                 break;
22976             default :
22977                 this.hidePopup();
22978                 
22979         }
22980     },
22981     
22982     
22983     onClick: function(e) 
22984     {
22985         e.stopPropagation();
22986         e.preventDefault();
22987         
22988         var target = e.getTarget();
22989         
22990         if(target.nodeName.toLowerCase() === 'i'){
22991             target = Roo.get(target).dom.parentNode;
22992         }
22993         
22994         var nodeName = target.nodeName;
22995         var className = target.className;
22996         var html = target.innerHTML;
22997         //Roo.log(nodeName);
22998         
22999         switch(nodeName.toLowerCase()) {
23000             case 'th':
23001                 switch(className) {
23002                     case 'switch':
23003                         this.showMode(1);
23004                         break;
23005                     case 'prev':
23006                     case 'next':
23007                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23008                         switch(this.viewMode){
23009                                 case 0:
23010                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23011                                         break;
23012                                 case 1:
23013                                 case 2:
23014                                         this.viewDate = this.moveYear(this.viewDate, dir);
23015                                         break;
23016                         }
23017                         this.fill();
23018                         break;
23019                     case 'today':
23020                         var date = new Date();
23021                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23022 //                        this.fill()
23023                         this.setValue(this.formatDate(this.date));
23024                         
23025                         this.hidePopup();
23026                         break;
23027                 }
23028                 break;
23029             case 'span':
23030                 if (className.indexOf('disabled') < 0) {
23031                 if (!this.viewDate) {
23032                     this.viewDate = new Date();
23033                 }
23034                 this.viewDate.setUTCDate(1);
23035                     if (className.indexOf('month') > -1) {
23036                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23037                     } else {
23038                         var year = parseInt(html, 10) || 0;
23039                         this.viewDate.setUTCFullYear(year);
23040                         
23041                     }
23042                     
23043                     if(this.singleMode){
23044                         this.setValue(this.formatDate(this.viewDate));
23045                         this.hidePopup();
23046                         return;
23047                     }
23048                     
23049                     this.showMode(-1);
23050                     this.fill();
23051                 }
23052                 break;
23053                 
23054             case 'td':
23055                 //Roo.log(className);
23056                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23057                     var day = parseInt(html, 10) || 1;
23058                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23059                         month = (this.viewDate || new Date()).getUTCMonth();
23060
23061                     if (className.indexOf('old') > -1) {
23062                         if(month === 0 ){
23063                             month = 11;
23064                             year -= 1;
23065                         }else{
23066                             month -= 1;
23067                         }
23068                     } else if (className.indexOf('new') > -1) {
23069                         if (month == 11) {
23070                             month = 0;
23071                             year += 1;
23072                         } else {
23073                             month += 1;
23074                         }
23075                     }
23076                     //Roo.log([year,month,day]);
23077                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23078                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23079 //                    this.fill();
23080                     //Roo.log(this.formatDate(this.date));
23081                     this.setValue(this.formatDate(this.date));
23082                     this.hidePopup();
23083                 }
23084                 break;
23085         }
23086     },
23087     
23088     setStartDate: function(startDate)
23089     {
23090         this.startDate = startDate || -Infinity;
23091         if (this.startDate !== -Infinity) {
23092             this.startDate = this.parseDate(this.startDate);
23093         }
23094         this.update();
23095         this.updateNavArrows();
23096     },
23097
23098     setEndDate: function(endDate)
23099     {
23100         this.endDate = endDate || Infinity;
23101         if (this.endDate !== Infinity) {
23102             this.endDate = this.parseDate(this.endDate);
23103         }
23104         this.update();
23105         this.updateNavArrows();
23106     },
23107     
23108     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23109     {
23110         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23111         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23112             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23113         }
23114         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23115             return parseInt(d, 10);
23116         });
23117         this.update();
23118         this.updateNavArrows();
23119     },
23120     
23121     updateNavArrows: function() 
23122     {
23123         if(this.singleMode){
23124             return;
23125         }
23126         
23127         var d = new Date(this.viewDate),
23128         year = d.getUTCFullYear(),
23129         month = d.getUTCMonth();
23130         
23131         Roo.each(this.picker().select('.prev', true).elements, function(v){
23132             v.show();
23133             switch (this.viewMode) {
23134                 case 0:
23135
23136                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23137                         v.hide();
23138                     }
23139                     break;
23140                 case 1:
23141                 case 2:
23142                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23143                         v.hide();
23144                     }
23145                     break;
23146             }
23147         });
23148         
23149         Roo.each(this.picker().select('.next', true).elements, function(v){
23150             v.show();
23151             switch (this.viewMode) {
23152                 case 0:
23153
23154                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23155                         v.hide();
23156                     }
23157                     break;
23158                 case 1:
23159                 case 2:
23160                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23161                         v.hide();
23162                     }
23163                     break;
23164             }
23165         })
23166     },
23167     
23168     moveMonth: function(date, dir)
23169     {
23170         if (!dir) {
23171             return date;
23172         }
23173         var new_date = new Date(date.valueOf()),
23174         day = new_date.getUTCDate(),
23175         month = new_date.getUTCMonth(),
23176         mag = Math.abs(dir),
23177         new_month, test;
23178         dir = dir > 0 ? 1 : -1;
23179         if (mag == 1){
23180             test = dir == -1
23181             // If going back one month, make sure month is not current month
23182             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23183             ? function(){
23184                 return new_date.getUTCMonth() == month;
23185             }
23186             // If going forward one month, make sure month is as expected
23187             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23188             : function(){
23189                 return new_date.getUTCMonth() != new_month;
23190             };
23191             new_month = month + dir;
23192             new_date.setUTCMonth(new_month);
23193             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23194             if (new_month < 0 || new_month > 11) {
23195                 new_month = (new_month + 12) % 12;
23196             }
23197         } else {
23198             // For magnitudes >1, move one month at a time...
23199             for (var i=0; i<mag; i++) {
23200                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23201                 new_date = this.moveMonth(new_date, dir);
23202             }
23203             // ...then reset the day, keeping it in the new month
23204             new_month = new_date.getUTCMonth();
23205             new_date.setUTCDate(day);
23206             test = function(){
23207                 return new_month != new_date.getUTCMonth();
23208             };
23209         }
23210         // Common date-resetting loop -- if date is beyond end of month, make it
23211         // end of month
23212         while (test()){
23213             new_date.setUTCDate(--day);
23214             new_date.setUTCMonth(new_month);
23215         }
23216         return new_date;
23217     },
23218
23219     moveYear: function(date, dir)
23220     {
23221         return this.moveMonth(date, dir*12);
23222     },
23223
23224     dateWithinRange: function(date)
23225     {
23226         return date >= this.startDate && date <= this.endDate;
23227     },
23228
23229     
23230     remove: function() 
23231     {
23232         this.picker().remove();
23233     },
23234     
23235     validateValue : function(value)
23236     {
23237         if(this.getVisibilityEl().hasClass('hidden')){
23238             return true;
23239         }
23240         
23241         if(value.length < 1)  {
23242             if(this.allowBlank){
23243                 return true;
23244             }
23245             return false;
23246         }
23247         
23248         if(value.length < this.minLength){
23249             return false;
23250         }
23251         if(value.length > this.maxLength){
23252             return false;
23253         }
23254         if(this.vtype){
23255             var vt = Roo.form.VTypes;
23256             if(!vt[this.vtype](value, this)){
23257                 return false;
23258             }
23259         }
23260         if(typeof this.validator == "function"){
23261             var msg = this.validator(value);
23262             if(msg !== true){
23263                 return false;
23264             }
23265         }
23266         
23267         if(this.regex && !this.regex.test(value)){
23268             return false;
23269         }
23270         
23271         if(typeof(this.parseDate(value)) == 'undefined'){
23272             return false;
23273         }
23274         
23275         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23276             return false;
23277         }      
23278         
23279         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23280             return false;
23281         } 
23282         
23283         
23284         return true;
23285     },
23286     
23287     reset : function()
23288     {
23289         this.date = this.viewDate = '';
23290         
23291         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23292     }
23293    
23294 });
23295
23296 Roo.apply(Roo.bootstrap.DateField,  {
23297     
23298     head : {
23299         tag: 'thead',
23300         cn: [
23301         {
23302             tag: 'tr',
23303             cn: [
23304             {
23305                 tag: 'th',
23306                 cls: 'prev',
23307                 html: '<i class="fa fa-arrow-left"/>'
23308             },
23309             {
23310                 tag: 'th',
23311                 cls: 'switch',
23312                 colspan: '5'
23313             },
23314             {
23315                 tag: 'th',
23316                 cls: 'next',
23317                 html: '<i class="fa fa-arrow-right"/>'
23318             }
23319
23320             ]
23321         }
23322         ]
23323     },
23324     
23325     content : {
23326         tag: 'tbody',
23327         cn: [
23328         {
23329             tag: 'tr',
23330             cn: [
23331             {
23332                 tag: 'td',
23333                 colspan: '7'
23334             }
23335             ]
23336         }
23337         ]
23338     },
23339     
23340     footer : {
23341         tag: 'tfoot',
23342         cn: [
23343         {
23344             tag: 'tr',
23345             cn: [
23346             {
23347                 tag: 'th',
23348                 colspan: '7',
23349                 cls: 'today'
23350             }
23351                     
23352             ]
23353         }
23354         ]
23355     },
23356     
23357     dates:{
23358         en: {
23359             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23360             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23361             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23362             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23363             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23364             today: "Today"
23365         }
23366     },
23367     
23368     modes: [
23369     {
23370         clsName: 'days',
23371         navFnc: 'Month',
23372         navStep: 1
23373     },
23374     {
23375         clsName: 'months',
23376         navFnc: 'FullYear',
23377         navStep: 1
23378     },
23379     {
23380         clsName: 'years',
23381         navFnc: 'FullYear',
23382         navStep: 10
23383     }]
23384 });
23385
23386 Roo.apply(Roo.bootstrap.DateField,  {
23387   
23388     template : {
23389         tag: 'div',
23390         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23391         cn: [
23392         {
23393             tag: 'div',
23394             cls: 'datepicker-days',
23395             cn: [
23396             {
23397                 tag: 'table',
23398                 cls: 'table-condensed',
23399                 cn:[
23400                 Roo.bootstrap.DateField.head,
23401                 {
23402                     tag: 'tbody'
23403                 },
23404                 Roo.bootstrap.DateField.footer
23405                 ]
23406             }
23407             ]
23408         },
23409         {
23410             tag: 'div',
23411             cls: 'datepicker-months',
23412             cn: [
23413             {
23414                 tag: 'table',
23415                 cls: 'table-condensed',
23416                 cn:[
23417                 Roo.bootstrap.DateField.head,
23418                 Roo.bootstrap.DateField.content,
23419                 Roo.bootstrap.DateField.footer
23420                 ]
23421             }
23422             ]
23423         },
23424         {
23425             tag: 'div',
23426             cls: 'datepicker-years',
23427             cn: [
23428             {
23429                 tag: 'table',
23430                 cls: 'table-condensed',
23431                 cn:[
23432                 Roo.bootstrap.DateField.head,
23433                 Roo.bootstrap.DateField.content,
23434                 Roo.bootstrap.DateField.footer
23435                 ]
23436             }
23437             ]
23438         }
23439         ]
23440     }
23441 });
23442
23443  
23444
23445  /*
23446  * - LGPL
23447  *
23448  * TimeField
23449  * 
23450  */
23451
23452 /**
23453  * @class Roo.bootstrap.TimeField
23454  * @extends Roo.bootstrap.Input
23455  * Bootstrap DateField class
23456  * 
23457  * 
23458  * @constructor
23459  * Create a new TimeField
23460  * @param {Object} config The config object
23461  */
23462
23463 Roo.bootstrap.TimeField = function(config){
23464     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23465     this.addEvents({
23466             /**
23467              * @event show
23468              * Fires when this field show.
23469              * @param {Roo.bootstrap.DateField} thisthis
23470              * @param {Mixed} date The date value
23471              */
23472             show : true,
23473             /**
23474              * @event show
23475              * Fires when this field hide.
23476              * @param {Roo.bootstrap.DateField} this
23477              * @param {Mixed} date The date value
23478              */
23479             hide : true,
23480             /**
23481              * @event select
23482              * Fires when select a date.
23483              * @param {Roo.bootstrap.DateField} this
23484              * @param {Mixed} date The date value
23485              */
23486             select : true
23487         });
23488 };
23489
23490 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23491     
23492     /**
23493      * @cfg {String} format
23494      * The default time format string which can be overriden for localization support.  The format must be
23495      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23496      */
23497     format : "H:i",
23498
23499     getAutoCreate : function()
23500     {
23501         this.after = '<i class="fa far fa-clock"></i>';
23502         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23503         
23504          
23505     },
23506     onRender: function(ct, position)
23507     {
23508         
23509         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23510                 
23511         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23512         
23513         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23514         
23515         this.pop = this.picker().select('>.datepicker-time',true).first();
23516         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23517         
23518         this.picker().on('mousedown', this.onMousedown, this);
23519         this.picker().on('click', this.onClick, this);
23520         
23521         this.picker().addClass('datepicker-dropdown');
23522     
23523         this.fillTime();
23524         this.update();
23525             
23526         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23527         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23528         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23529         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23530         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23531         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23532
23533     },
23534     
23535     fireKey: function(e){
23536         if (!this.picker().isVisible()){
23537             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23538                 this.show();
23539             }
23540             return;
23541         }
23542
23543         e.preventDefault();
23544         
23545         switch(e.keyCode){
23546             case 27: // escape
23547                 this.hide();
23548                 break;
23549             case 37: // left
23550             case 39: // right
23551                 this.onTogglePeriod();
23552                 break;
23553             case 38: // up
23554                 this.onIncrementMinutes();
23555                 break;
23556             case 40: // down
23557                 this.onDecrementMinutes();
23558                 break;
23559             case 13: // enter
23560             case 9: // tab
23561                 this.setTime();
23562                 break;
23563         }
23564     },
23565     
23566     onClick: function(e) {
23567         e.stopPropagation();
23568         e.preventDefault();
23569     },
23570     
23571     picker : function()
23572     {
23573         return this.pickerEl;
23574     },
23575     
23576     fillTime: function()
23577     {    
23578         var time = this.pop.select('tbody', true).first();
23579         
23580         time.dom.innerHTML = '';
23581         
23582         time.createChild({
23583             tag: 'tr',
23584             cn: [
23585                 {
23586                     tag: 'td',
23587                     cn: [
23588                         {
23589                             tag: 'a',
23590                             href: '#',
23591                             cls: 'btn',
23592                             cn: [
23593                                 {
23594                                     tag: 'i',
23595                                     cls: 'hours-up fa fas fa-chevron-up'
23596                                 }
23597                             ]
23598                         } 
23599                     ]
23600                 },
23601                 {
23602                     tag: 'td',
23603                     cls: 'separator'
23604                 },
23605                 {
23606                     tag: 'td',
23607                     cn: [
23608                         {
23609                             tag: 'a',
23610                             href: '#',
23611                             cls: 'btn',
23612                             cn: [
23613                                 {
23614                                     tag: 'i',
23615                                     cls: 'minutes-up fa fas fa-chevron-up'
23616                                 }
23617                             ]
23618                         }
23619                     ]
23620                 },
23621                 {
23622                     tag: 'td',
23623                     cls: 'separator'
23624                 }
23625             ]
23626         });
23627         
23628         time.createChild({
23629             tag: 'tr',
23630             cn: [
23631                 {
23632                     tag: 'td',
23633                     cn: [
23634                         {
23635                             tag: 'span',
23636                             cls: 'timepicker-hour',
23637                             html: '00'
23638                         }  
23639                     ]
23640                 },
23641                 {
23642                     tag: 'td',
23643                     cls: 'separator',
23644                     html: ':'
23645                 },
23646                 {
23647                     tag: 'td',
23648                     cn: [
23649                         {
23650                             tag: 'span',
23651                             cls: 'timepicker-minute',
23652                             html: '00'
23653                         }  
23654                     ]
23655                 },
23656                 {
23657                     tag: 'td',
23658                     cls: 'separator'
23659                 },
23660                 {
23661                     tag: 'td',
23662                     cn: [
23663                         {
23664                             tag: 'button',
23665                             type: 'button',
23666                             cls: 'btn btn-primary period',
23667                             html: 'AM'
23668                             
23669                         }
23670                     ]
23671                 }
23672             ]
23673         });
23674         
23675         time.createChild({
23676             tag: 'tr',
23677             cn: [
23678                 {
23679                     tag: 'td',
23680                     cn: [
23681                         {
23682                             tag: 'a',
23683                             href: '#',
23684                             cls: 'btn',
23685                             cn: [
23686                                 {
23687                                     tag: 'span',
23688                                     cls: 'hours-down fa fas fa-chevron-down'
23689                                 }
23690                             ]
23691                         }
23692                     ]
23693                 },
23694                 {
23695                     tag: 'td',
23696                     cls: 'separator'
23697                 },
23698                 {
23699                     tag: 'td',
23700                     cn: [
23701                         {
23702                             tag: 'a',
23703                             href: '#',
23704                             cls: 'btn',
23705                             cn: [
23706                                 {
23707                                     tag: 'span',
23708                                     cls: 'minutes-down fa fas fa-chevron-down'
23709                                 }
23710                             ]
23711                         }
23712                     ]
23713                 },
23714                 {
23715                     tag: 'td',
23716                     cls: 'separator'
23717                 }
23718             ]
23719         });
23720         
23721     },
23722     
23723     update: function()
23724     {
23725         
23726         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23727         
23728         this.fill();
23729     },
23730     
23731     fill: function() 
23732     {
23733         var hours = this.time.getHours();
23734         var minutes = this.time.getMinutes();
23735         var period = 'AM';
23736         
23737         if(hours > 11){
23738             period = 'PM';
23739         }
23740         
23741         if(hours == 0){
23742             hours = 12;
23743         }
23744         
23745         
23746         if(hours > 12){
23747             hours = hours - 12;
23748         }
23749         
23750         if(hours < 10){
23751             hours = '0' + hours;
23752         }
23753         
23754         if(minutes < 10){
23755             minutes = '0' + minutes;
23756         }
23757         
23758         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23759         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23760         this.pop.select('button', true).first().dom.innerHTML = period;
23761         
23762     },
23763     
23764     place: function()
23765     {   
23766         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23767         
23768         var cls = ['bottom'];
23769         
23770         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23771             cls.pop();
23772             cls.push('top');
23773         }
23774         
23775         cls.push('right');
23776         
23777         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23778             cls.pop();
23779             cls.push('left');
23780         }
23781         //this.picker().setXY(20000,20000);
23782         this.picker().addClass(cls.join('-'));
23783         
23784         var _this = this;
23785         
23786         Roo.each(cls, function(c){
23787             if(c == 'bottom'){
23788                 (function() {
23789                  //  
23790                 }).defer(200);
23791                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23792                 //_this.picker().setTop(_this.inputEl().getHeight());
23793                 return;
23794             }
23795             if(c == 'top'){
23796                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23797                 
23798                 //_this.picker().setTop(0 - _this.picker().getHeight());
23799                 return;
23800             }
23801             /*
23802             if(c == 'left'){
23803                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23804                 return;
23805             }
23806             if(c == 'right'){
23807                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23808                 return;
23809             }
23810             */
23811         });
23812         
23813     },
23814   
23815     onFocus : function()
23816     {
23817         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23818         this.show();
23819     },
23820     
23821     onBlur : function()
23822     {
23823         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23824         this.hide();
23825     },
23826     
23827     show : function()
23828     {
23829         this.picker().show();
23830         this.pop.show();
23831         this.update();
23832         this.place();
23833         
23834         this.fireEvent('show', this, this.date);
23835     },
23836     
23837     hide : function()
23838     {
23839         this.picker().hide();
23840         this.pop.hide();
23841         
23842         this.fireEvent('hide', this, this.date);
23843     },
23844     
23845     setTime : function()
23846     {
23847         this.hide();
23848         this.setValue(this.time.format(this.format));
23849         
23850         this.fireEvent('select', this, this.date);
23851         
23852         
23853     },
23854     
23855     onMousedown: function(e){
23856         e.stopPropagation();
23857         e.preventDefault();
23858     },
23859     
23860     onIncrementHours: function()
23861     {
23862         Roo.log('onIncrementHours');
23863         this.time = this.time.add(Date.HOUR, 1);
23864         this.update();
23865         
23866     },
23867     
23868     onDecrementHours: function()
23869     {
23870         Roo.log('onDecrementHours');
23871         this.time = this.time.add(Date.HOUR, -1);
23872         this.update();
23873     },
23874     
23875     onIncrementMinutes: function()
23876     {
23877         Roo.log('onIncrementMinutes');
23878         this.time = this.time.add(Date.MINUTE, 1);
23879         this.update();
23880     },
23881     
23882     onDecrementMinutes: function()
23883     {
23884         Roo.log('onDecrementMinutes');
23885         this.time = this.time.add(Date.MINUTE, -1);
23886         this.update();
23887     },
23888     
23889     onTogglePeriod: function()
23890     {
23891         Roo.log('onTogglePeriod');
23892         this.time = this.time.add(Date.HOUR, 12);
23893         this.update();
23894     }
23895     
23896    
23897 });
23898  
23899
23900 Roo.apply(Roo.bootstrap.TimeField,  {
23901   
23902     template : {
23903         tag: 'div',
23904         cls: 'datepicker dropdown-menu',
23905         cn: [
23906             {
23907                 tag: 'div',
23908                 cls: 'datepicker-time',
23909                 cn: [
23910                 {
23911                     tag: 'table',
23912                     cls: 'table-condensed',
23913                     cn:[
23914                         {
23915                             tag: 'tbody',
23916                             cn: [
23917                                 {
23918                                     tag: 'tr',
23919                                     cn: [
23920                                     {
23921                                         tag: 'td',
23922                                         colspan: '7'
23923                                     }
23924                                     ]
23925                                 }
23926                             ]
23927                         },
23928                         {
23929                             tag: 'tfoot',
23930                             cn: [
23931                                 {
23932                                     tag: 'tr',
23933                                     cn: [
23934                                     {
23935                                         tag: 'th',
23936                                         colspan: '7',
23937                                         cls: '',
23938                                         cn: [
23939                                             {
23940                                                 tag: 'button',
23941                                                 cls: 'btn btn-info ok',
23942                                                 html: 'OK'
23943                                             }
23944                                         ]
23945                                     }
23946                     
23947                                     ]
23948                                 }
23949                             ]
23950                         }
23951                     ]
23952                 }
23953                 ]
23954             }
23955         ]
23956     }
23957 });
23958
23959  
23960
23961  /*
23962  * - LGPL
23963  *
23964  * MonthField
23965  * 
23966  */
23967
23968 /**
23969  * @class Roo.bootstrap.MonthField
23970  * @extends Roo.bootstrap.Input
23971  * Bootstrap MonthField class
23972  * 
23973  * @cfg {String} language default en
23974  * 
23975  * @constructor
23976  * Create a new MonthField
23977  * @param {Object} config The config object
23978  */
23979
23980 Roo.bootstrap.MonthField = function(config){
23981     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23982     
23983     this.addEvents({
23984         /**
23985          * @event show
23986          * Fires when this field show.
23987          * @param {Roo.bootstrap.MonthField} this
23988          * @param {Mixed} date The date value
23989          */
23990         show : true,
23991         /**
23992          * @event show
23993          * Fires when this field hide.
23994          * @param {Roo.bootstrap.MonthField} this
23995          * @param {Mixed} date The date value
23996          */
23997         hide : true,
23998         /**
23999          * @event select
24000          * Fires when select a date.
24001          * @param {Roo.bootstrap.MonthField} this
24002          * @param {String} oldvalue The old value
24003          * @param {String} newvalue The new value
24004          */
24005         select : true
24006     });
24007 };
24008
24009 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24010     
24011     onRender: function(ct, position)
24012     {
24013         
24014         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24015         
24016         this.language = this.language || 'en';
24017         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24018         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24019         
24020         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24021         this.isInline = false;
24022         this.isInput = true;
24023         this.component = this.el.select('.add-on', true).first() || false;
24024         this.component = (this.component && this.component.length === 0) ? false : this.component;
24025         this.hasInput = this.component && this.inputEL().length;
24026         
24027         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24028         
24029         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24030         
24031         this.picker().on('mousedown', this.onMousedown, this);
24032         this.picker().on('click', this.onClick, this);
24033         
24034         this.picker().addClass('datepicker-dropdown');
24035         
24036         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24037             v.setStyle('width', '189px');
24038         });
24039         
24040         this.fillMonths();
24041         
24042         this.update();
24043         
24044         if(this.isInline) {
24045             this.show();
24046         }
24047         
24048     },
24049     
24050     setValue: function(v, suppressEvent)
24051     {   
24052         var o = this.getValue();
24053         
24054         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24055         
24056         this.update();
24057
24058         if(suppressEvent !== true){
24059             this.fireEvent('select', this, o, v);
24060         }
24061         
24062     },
24063     
24064     getValue: function()
24065     {
24066         return this.value;
24067     },
24068     
24069     onClick: function(e) 
24070     {
24071         e.stopPropagation();
24072         e.preventDefault();
24073         
24074         var target = e.getTarget();
24075         
24076         if(target.nodeName.toLowerCase() === 'i'){
24077             target = Roo.get(target).dom.parentNode;
24078         }
24079         
24080         var nodeName = target.nodeName;
24081         var className = target.className;
24082         var html = target.innerHTML;
24083         
24084         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24085             return;
24086         }
24087         
24088         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24089         
24090         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24091         
24092         this.hide();
24093                         
24094     },
24095     
24096     picker : function()
24097     {
24098         return this.pickerEl;
24099     },
24100     
24101     fillMonths: function()
24102     {    
24103         var i = 0;
24104         var months = this.picker().select('>.datepicker-months td', true).first();
24105         
24106         months.dom.innerHTML = '';
24107         
24108         while (i < 12) {
24109             var month = {
24110                 tag: 'span',
24111                 cls: 'month',
24112                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24113             };
24114             
24115             months.createChild(month);
24116         }
24117         
24118     },
24119     
24120     update: function()
24121     {
24122         var _this = this;
24123         
24124         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24125             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24126         }
24127         
24128         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24129             e.removeClass('active');
24130             
24131             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24132                 e.addClass('active');
24133             }
24134         })
24135     },
24136     
24137     place: function()
24138     {
24139         if(this.isInline) {
24140             return;
24141         }
24142         
24143         this.picker().removeClass(['bottom', 'top']);
24144         
24145         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24146             /*
24147              * place to the top of element!
24148              *
24149              */
24150             
24151             this.picker().addClass('top');
24152             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24153             
24154             return;
24155         }
24156         
24157         this.picker().addClass('bottom');
24158         
24159         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24160     },
24161     
24162     onFocus : function()
24163     {
24164         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24165         this.show();
24166     },
24167     
24168     onBlur : function()
24169     {
24170         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24171         
24172         var d = this.inputEl().getValue();
24173         
24174         this.setValue(d);
24175                 
24176         this.hide();
24177     },
24178     
24179     show : function()
24180     {
24181         this.picker().show();
24182         this.picker().select('>.datepicker-months', true).first().show();
24183         this.update();
24184         this.place();
24185         
24186         this.fireEvent('show', this, this.date);
24187     },
24188     
24189     hide : function()
24190     {
24191         if(this.isInline) {
24192             return;
24193         }
24194         this.picker().hide();
24195         this.fireEvent('hide', this, this.date);
24196         
24197     },
24198     
24199     onMousedown: function(e)
24200     {
24201         e.stopPropagation();
24202         e.preventDefault();
24203     },
24204     
24205     keyup: function(e)
24206     {
24207         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24208         this.update();
24209     },
24210
24211     fireKey: function(e)
24212     {
24213         if (!this.picker().isVisible()){
24214             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24215                 this.show();
24216             }
24217             return;
24218         }
24219         
24220         var dir;
24221         
24222         switch(e.keyCode){
24223             case 27: // escape
24224                 this.hide();
24225                 e.preventDefault();
24226                 break;
24227             case 37: // left
24228             case 39: // right
24229                 dir = e.keyCode == 37 ? -1 : 1;
24230                 
24231                 this.vIndex = this.vIndex + dir;
24232                 
24233                 if(this.vIndex < 0){
24234                     this.vIndex = 0;
24235                 }
24236                 
24237                 if(this.vIndex > 11){
24238                     this.vIndex = 11;
24239                 }
24240                 
24241                 if(isNaN(this.vIndex)){
24242                     this.vIndex = 0;
24243                 }
24244                 
24245                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24246                 
24247                 break;
24248             case 38: // up
24249             case 40: // down
24250                 
24251                 dir = e.keyCode == 38 ? -1 : 1;
24252                 
24253                 this.vIndex = this.vIndex + dir * 4;
24254                 
24255                 if(this.vIndex < 0){
24256                     this.vIndex = 0;
24257                 }
24258                 
24259                 if(this.vIndex > 11){
24260                     this.vIndex = 11;
24261                 }
24262                 
24263                 if(isNaN(this.vIndex)){
24264                     this.vIndex = 0;
24265                 }
24266                 
24267                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24268                 break;
24269                 
24270             case 13: // enter
24271                 
24272                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24273                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24274                 }
24275                 
24276                 this.hide();
24277                 e.preventDefault();
24278                 break;
24279             case 9: // tab
24280                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24281                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24282                 }
24283                 this.hide();
24284                 break;
24285             case 16: // shift
24286             case 17: // ctrl
24287             case 18: // alt
24288                 break;
24289             default :
24290                 this.hide();
24291                 
24292         }
24293     },
24294     
24295     remove: function() 
24296     {
24297         this.picker().remove();
24298     }
24299    
24300 });
24301
24302 Roo.apply(Roo.bootstrap.MonthField,  {
24303     
24304     content : {
24305         tag: 'tbody',
24306         cn: [
24307         {
24308             tag: 'tr',
24309             cn: [
24310             {
24311                 tag: 'td',
24312                 colspan: '7'
24313             }
24314             ]
24315         }
24316         ]
24317     },
24318     
24319     dates:{
24320         en: {
24321             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24322             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24323         }
24324     }
24325 });
24326
24327 Roo.apply(Roo.bootstrap.MonthField,  {
24328   
24329     template : {
24330         tag: 'div',
24331         cls: 'datepicker dropdown-menu roo-dynamic',
24332         cn: [
24333             {
24334                 tag: 'div',
24335                 cls: 'datepicker-months',
24336                 cn: [
24337                 {
24338                     tag: 'table',
24339                     cls: 'table-condensed',
24340                     cn:[
24341                         Roo.bootstrap.DateField.content
24342                     ]
24343                 }
24344                 ]
24345             }
24346         ]
24347     }
24348 });
24349
24350  
24351
24352  
24353  /*
24354  * - LGPL
24355  *
24356  * CheckBox
24357  * 
24358  */
24359
24360 /**
24361  * @class Roo.bootstrap.CheckBox
24362  * @extends Roo.bootstrap.Input
24363  * Bootstrap CheckBox class
24364  * 
24365  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24366  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24367  * @cfg {String} boxLabel The text that appears beside the checkbox
24368  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24369  * @cfg {Boolean} checked initnal the element
24370  * @cfg {Boolean} inline inline the element (default false)
24371  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24372  * @cfg {String} tooltip label tooltip
24373  * 
24374  * @constructor
24375  * Create a new CheckBox
24376  * @param {Object} config The config object
24377  */
24378
24379 Roo.bootstrap.CheckBox = function(config){
24380     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24381    
24382     this.addEvents({
24383         /**
24384         * @event check
24385         * Fires when the element is checked or unchecked.
24386         * @param {Roo.bootstrap.CheckBox} this This input
24387         * @param {Boolean} checked The new checked value
24388         */
24389        check : true,
24390        /**
24391         * @event click
24392         * Fires when the element is click.
24393         * @param {Roo.bootstrap.CheckBox} this This input
24394         */
24395        click : true
24396     });
24397     
24398 };
24399
24400 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24401   
24402     inputType: 'checkbox',
24403     inputValue: 1,
24404     valueOff: 0,
24405     boxLabel: false,
24406     checked: false,
24407     weight : false,
24408     inline: false,
24409     tooltip : '',
24410     
24411     // checkbox success does not make any sense really.. 
24412     invalidClass : "",
24413     validClass : "",
24414     
24415     
24416     getAutoCreate : function()
24417     {
24418         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24419         
24420         var id = Roo.id();
24421         
24422         var cfg = {};
24423         
24424         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24425         
24426         if(this.inline){
24427             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24428         }
24429         
24430         var input =  {
24431             tag: 'input',
24432             id : id,
24433             type : this.inputType,
24434             value : this.inputValue,
24435             cls : 'roo-' + this.inputType, //'form-box',
24436             placeholder : this.placeholder || ''
24437             
24438         };
24439         
24440         if(this.inputType != 'radio'){
24441             var hidden =  {
24442                 tag: 'input',
24443                 type : 'hidden',
24444                 cls : 'roo-hidden-value',
24445                 value : this.checked ? this.inputValue : this.valueOff
24446             };
24447         }
24448         
24449             
24450         if (this.weight) { // Validity check?
24451             cfg.cls += " " + this.inputType + "-" + this.weight;
24452         }
24453         
24454         if (this.disabled) {
24455             input.disabled=true;
24456         }
24457         
24458         if(this.checked){
24459             input.checked = this.checked;
24460         }
24461         
24462         if (this.name) {
24463             
24464             input.name = this.name;
24465             
24466             if(this.inputType != 'radio'){
24467                 hidden.name = this.name;
24468                 input.name = '_hidden_' + this.name;
24469             }
24470         }
24471         
24472         if (this.size) {
24473             input.cls += ' input-' + this.size;
24474         }
24475         
24476         var settings=this;
24477         
24478         ['xs','sm','md','lg'].map(function(size){
24479             if (settings[size]) {
24480                 cfg.cls += ' col-' + size + '-' + settings[size];
24481             }
24482         });
24483         
24484         var inputblock = input;
24485          
24486         if (this.before || this.after) {
24487             
24488             inputblock = {
24489                 cls : 'input-group',
24490                 cn :  [] 
24491             };
24492             
24493             if (this.before) {
24494                 inputblock.cn.push({
24495                     tag :'span',
24496                     cls : 'input-group-addon',
24497                     html : this.before
24498                 });
24499             }
24500             
24501             inputblock.cn.push(input);
24502             
24503             if(this.inputType != 'radio'){
24504                 inputblock.cn.push(hidden);
24505             }
24506             
24507             if (this.after) {
24508                 inputblock.cn.push({
24509                     tag :'span',
24510                     cls : 'input-group-addon',
24511                     html : this.after
24512                 });
24513             }
24514             
24515         }
24516         var boxLabelCfg = false;
24517         
24518         if(this.boxLabel){
24519            
24520             boxLabelCfg = {
24521                 tag: 'label',
24522                 //'for': id, // box label is handled by onclick - so no for...
24523                 cls: 'box-label',
24524                 html: this.boxLabel
24525             };
24526             if(this.tooltip){
24527                 boxLabelCfg.tooltip = this.tooltip;
24528             }
24529              
24530         }
24531         
24532         
24533         if (align ==='left' && this.fieldLabel.length) {
24534 //                Roo.log("left and has label");
24535             cfg.cn = [
24536                 {
24537                     tag: 'label',
24538                     'for' :  id,
24539                     cls : 'control-label',
24540                     html : this.fieldLabel
24541                 },
24542                 {
24543                     cls : "", 
24544                     cn: [
24545                         inputblock
24546                     ]
24547                 }
24548             ];
24549             
24550             if (boxLabelCfg) {
24551                 cfg.cn[1].cn.push(boxLabelCfg);
24552             }
24553             
24554             if(this.labelWidth > 12){
24555                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24556             }
24557             
24558             if(this.labelWidth < 13 && this.labelmd == 0){
24559                 this.labelmd = this.labelWidth;
24560             }
24561             
24562             if(this.labellg > 0){
24563                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24564                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24565             }
24566             
24567             if(this.labelmd > 0){
24568                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24569                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24570             }
24571             
24572             if(this.labelsm > 0){
24573                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24574                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24575             }
24576             
24577             if(this.labelxs > 0){
24578                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24579                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24580             }
24581             
24582         } else if ( this.fieldLabel.length) {
24583 //                Roo.log(" label");
24584                 cfg.cn = [
24585                    
24586                     {
24587                         tag: this.boxLabel ? 'span' : 'label',
24588                         'for': id,
24589                         cls: 'control-label box-input-label',
24590                         //cls : 'input-group-addon',
24591                         html : this.fieldLabel
24592                     },
24593                     
24594                     inputblock
24595                     
24596                 ];
24597                 if (boxLabelCfg) {
24598                     cfg.cn.push(boxLabelCfg);
24599                 }
24600
24601         } else {
24602             
24603 //                Roo.log(" no label && no align");
24604                 cfg.cn = [  inputblock ] ;
24605                 if (boxLabelCfg) {
24606                     cfg.cn.push(boxLabelCfg);
24607                 }
24608
24609                 
24610         }
24611         
24612        
24613         
24614         if(this.inputType != 'radio'){
24615             cfg.cn.push(hidden);
24616         }
24617         
24618         return cfg;
24619         
24620     },
24621     
24622     /**
24623      * return the real input element.
24624      */
24625     inputEl: function ()
24626     {
24627         return this.el.select('input.roo-' + this.inputType,true).first();
24628     },
24629     hiddenEl: function ()
24630     {
24631         return this.el.select('input.roo-hidden-value',true).first();
24632     },
24633     
24634     labelEl: function()
24635     {
24636         return this.el.select('label.control-label',true).first();
24637     },
24638     /* depricated... */
24639     
24640     label: function()
24641     {
24642         return this.labelEl();
24643     },
24644     
24645     boxLabelEl: function()
24646     {
24647         return this.el.select('label.box-label',true).first();
24648     },
24649     
24650     initEvents : function()
24651     {
24652 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24653         
24654         this.inputEl().on('click', this.onClick,  this);
24655         
24656         if (this.boxLabel) { 
24657             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24658         }
24659         
24660         this.startValue = this.getValue();
24661         
24662         if(this.groupId){
24663             Roo.bootstrap.CheckBox.register(this);
24664         }
24665     },
24666     
24667     onClick : function(e)
24668     {   
24669         if(this.fireEvent('click', this, e) !== false){
24670             this.setChecked(!this.checked);
24671         }
24672         
24673     },
24674     
24675     setChecked : function(state,suppressEvent)
24676     {
24677         this.startValue = this.getValue();
24678
24679         if(this.inputType == 'radio'){
24680             
24681             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24682                 e.dom.checked = false;
24683             });
24684             
24685             this.inputEl().dom.checked = true;
24686             
24687             this.inputEl().dom.value = this.inputValue;
24688             
24689             if(suppressEvent !== true){
24690                 this.fireEvent('check', this, true);
24691             }
24692             
24693             this.validate();
24694             
24695             return;
24696         }
24697         
24698         this.checked = state;
24699         
24700         this.inputEl().dom.checked = state;
24701         
24702         
24703         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24704         
24705         if(suppressEvent !== true){
24706             this.fireEvent('check', this, state);
24707         }
24708         
24709         this.validate();
24710     },
24711     
24712     getValue : function()
24713     {
24714         if(this.inputType == 'radio'){
24715             return this.getGroupValue();
24716         }
24717         
24718         return this.hiddenEl().dom.value;
24719         
24720     },
24721     
24722     getGroupValue : function()
24723     {
24724         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24725             return '';
24726         }
24727         
24728         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24729     },
24730     
24731     setValue : function(v,suppressEvent)
24732     {
24733         if(this.inputType == 'radio'){
24734             this.setGroupValue(v, suppressEvent);
24735             return;
24736         }
24737         
24738         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24739         
24740         this.validate();
24741     },
24742     
24743     setGroupValue : function(v, suppressEvent)
24744     {
24745         this.startValue = this.getValue();
24746         
24747         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24748             e.dom.checked = false;
24749             
24750             if(e.dom.value == v){
24751                 e.dom.checked = true;
24752             }
24753         });
24754         
24755         if(suppressEvent !== true){
24756             this.fireEvent('check', this, true);
24757         }
24758
24759         this.validate();
24760         
24761         return;
24762     },
24763     
24764     validate : function()
24765     {
24766         if(this.getVisibilityEl().hasClass('hidden')){
24767             return true;
24768         }
24769         
24770         if(
24771                 this.disabled || 
24772                 (this.inputType == 'radio' && this.validateRadio()) ||
24773                 (this.inputType == 'checkbox' && this.validateCheckbox())
24774         ){
24775             this.markValid();
24776             return true;
24777         }
24778         
24779         this.markInvalid();
24780         return false;
24781     },
24782     
24783     validateRadio : function()
24784     {
24785         if(this.getVisibilityEl().hasClass('hidden')){
24786             return true;
24787         }
24788         
24789         if(this.allowBlank){
24790             return true;
24791         }
24792         
24793         var valid = false;
24794         
24795         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24796             if(!e.dom.checked){
24797                 return;
24798             }
24799             
24800             valid = true;
24801             
24802             return false;
24803         });
24804         
24805         return valid;
24806     },
24807     
24808     validateCheckbox : function()
24809     {
24810         if(!this.groupId){
24811             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24812             //return (this.getValue() == this.inputValue) ? true : false;
24813         }
24814         
24815         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24816         
24817         if(!group){
24818             return false;
24819         }
24820         
24821         var r = false;
24822         
24823         for(var i in group){
24824             if(group[i].el.isVisible(true)){
24825                 r = false;
24826                 break;
24827             }
24828             
24829             r = true;
24830         }
24831         
24832         for(var i in group){
24833             if(r){
24834                 break;
24835             }
24836             
24837             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24838         }
24839         
24840         return r;
24841     },
24842     
24843     /**
24844      * Mark this field as valid
24845      */
24846     markValid : function()
24847     {
24848         var _this = this;
24849         
24850         this.fireEvent('valid', this);
24851         
24852         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24853         
24854         if(this.groupId){
24855             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24856         }
24857         
24858         if(label){
24859             label.markValid();
24860         }
24861
24862         if(this.inputType == 'radio'){
24863             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24864                 var fg = e.findParent('.form-group', false, true);
24865                 if (Roo.bootstrap.version == 3) {
24866                     fg.removeClass([_this.invalidClass, _this.validClass]);
24867                     fg.addClass(_this.validClass);
24868                 } else {
24869                     fg.removeClass(['is-valid', 'is-invalid']);
24870                     fg.addClass('is-valid');
24871                 }
24872             });
24873             
24874             return;
24875         }
24876
24877         if(!this.groupId){
24878             var fg = this.el.findParent('.form-group', false, true);
24879             if (Roo.bootstrap.version == 3) {
24880                 fg.removeClass([this.invalidClass, this.validClass]);
24881                 fg.addClass(this.validClass);
24882             } else {
24883                 fg.removeClass(['is-valid', 'is-invalid']);
24884                 fg.addClass('is-valid');
24885             }
24886             return;
24887         }
24888         
24889         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24890         
24891         if(!group){
24892             return;
24893         }
24894         
24895         for(var i in group){
24896             var fg = group[i].el.findParent('.form-group', false, true);
24897             if (Roo.bootstrap.version == 3) {
24898                 fg.removeClass([this.invalidClass, this.validClass]);
24899                 fg.addClass(this.validClass);
24900             } else {
24901                 fg.removeClass(['is-valid', 'is-invalid']);
24902                 fg.addClass('is-valid');
24903             }
24904         }
24905     },
24906     
24907      /**
24908      * Mark this field as invalid
24909      * @param {String} msg The validation message
24910      */
24911     markInvalid : function(msg)
24912     {
24913         if(this.allowBlank){
24914             return;
24915         }
24916         
24917         var _this = this;
24918         
24919         this.fireEvent('invalid', this, msg);
24920         
24921         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24922         
24923         if(this.groupId){
24924             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24925         }
24926         
24927         if(label){
24928             label.markInvalid();
24929         }
24930             
24931         if(this.inputType == 'radio'){
24932             
24933             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24934                 var fg = e.findParent('.form-group', false, true);
24935                 if (Roo.bootstrap.version == 3) {
24936                     fg.removeClass([_this.invalidClass, _this.validClass]);
24937                     fg.addClass(_this.invalidClass);
24938                 } else {
24939                     fg.removeClass(['is-invalid', 'is-valid']);
24940                     fg.addClass('is-invalid');
24941                 }
24942             });
24943             
24944             return;
24945         }
24946         
24947         if(!this.groupId){
24948             var fg = this.el.findParent('.form-group', false, true);
24949             if (Roo.bootstrap.version == 3) {
24950                 fg.removeClass([_this.invalidClass, _this.validClass]);
24951                 fg.addClass(_this.invalidClass);
24952             } else {
24953                 fg.removeClass(['is-invalid', 'is-valid']);
24954                 fg.addClass('is-invalid');
24955             }
24956             return;
24957         }
24958         
24959         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24960         
24961         if(!group){
24962             return;
24963         }
24964         
24965         for(var i in group){
24966             var fg = group[i].el.findParent('.form-group', false, true);
24967             if (Roo.bootstrap.version == 3) {
24968                 fg.removeClass([_this.invalidClass, _this.validClass]);
24969                 fg.addClass(_this.invalidClass);
24970             } else {
24971                 fg.removeClass(['is-invalid', 'is-valid']);
24972                 fg.addClass('is-invalid');
24973             }
24974         }
24975         
24976     },
24977     
24978     clearInvalid : function()
24979     {
24980         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24981         
24982         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24983         
24984         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24985         
24986         if (label && label.iconEl) {
24987             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24988             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24989         }
24990     },
24991     
24992     disable : function()
24993     {
24994         if(this.inputType != 'radio'){
24995             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24996             return;
24997         }
24998         
24999         var _this = this;
25000         
25001         if(this.rendered){
25002             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25003                 _this.getActionEl().addClass(this.disabledClass);
25004                 e.dom.disabled = true;
25005             });
25006         }
25007         
25008         this.disabled = true;
25009         this.fireEvent("disable", this);
25010         return this;
25011     },
25012
25013     enable : function()
25014     {
25015         if(this.inputType != 'radio'){
25016             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25017             return;
25018         }
25019         
25020         var _this = this;
25021         
25022         if(this.rendered){
25023             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25024                 _this.getActionEl().removeClass(this.disabledClass);
25025                 e.dom.disabled = false;
25026             });
25027         }
25028         
25029         this.disabled = false;
25030         this.fireEvent("enable", this);
25031         return this;
25032     },
25033     
25034     setBoxLabel : function(v)
25035     {
25036         this.boxLabel = v;
25037         
25038         if(this.rendered){
25039             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25040         }
25041     }
25042
25043 });
25044
25045 Roo.apply(Roo.bootstrap.CheckBox, {
25046     
25047     groups: {},
25048     
25049      /**
25050     * register a CheckBox Group
25051     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25052     */
25053     register : function(checkbox)
25054     {
25055         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25056             this.groups[checkbox.groupId] = {};
25057         }
25058         
25059         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25060             return;
25061         }
25062         
25063         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25064         
25065     },
25066     /**
25067     * fetch a CheckBox Group based on the group ID
25068     * @param {string} the group ID
25069     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25070     */
25071     get: function(groupId) {
25072         if (typeof(this.groups[groupId]) == 'undefined') {
25073             return false;
25074         }
25075         
25076         return this.groups[groupId] ;
25077     }
25078     
25079     
25080 });
25081 /*
25082  * - LGPL
25083  *
25084  * RadioItem
25085  * 
25086  */
25087
25088 /**
25089  * @class Roo.bootstrap.Radio
25090  * @extends Roo.bootstrap.Component
25091  * Bootstrap Radio class
25092  * @cfg {String} boxLabel - the label associated
25093  * @cfg {String} value - the value of radio
25094  * 
25095  * @constructor
25096  * Create a new Radio
25097  * @param {Object} config The config object
25098  */
25099 Roo.bootstrap.Radio = function(config){
25100     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25101     
25102 };
25103
25104 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25105     
25106     boxLabel : '',
25107     
25108     value : '',
25109     
25110     getAutoCreate : function()
25111     {
25112         var cfg = {
25113             tag : 'div',
25114             cls : 'form-group radio',
25115             cn : [
25116                 {
25117                     tag : 'label',
25118                     cls : 'box-label',
25119                     html : this.boxLabel
25120                 }
25121             ]
25122         };
25123         
25124         return cfg;
25125     },
25126     
25127     initEvents : function() 
25128     {
25129         this.parent().register(this);
25130         
25131         this.el.on('click', this.onClick, this);
25132         
25133     },
25134     
25135     onClick : function(e)
25136     {
25137         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25138             this.setChecked(true);
25139         }
25140     },
25141     
25142     setChecked : function(state, suppressEvent)
25143     {
25144         this.parent().setValue(this.value, suppressEvent);
25145         
25146     },
25147     
25148     setBoxLabel : function(v)
25149     {
25150         this.boxLabel = v;
25151         
25152         if(this.rendered){
25153             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25154         }
25155     }
25156     
25157 });
25158  
25159
25160  /*
25161  * - LGPL
25162  *
25163  * Input
25164  * 
25165  */
25166
25167 /**
25168  * @class Roo.bootstrap.SecurePass
25169  * @extends Roo.bootstrap.Input
25170  * Bootstrap SecurePass class
25171  *
25172  * 
25173  * @constructor
25174  * Create a new SecurePass
25175  * @param {Object} config The config object
25176  */
25177  
25178 Roo.bootstrap.SecurePass = function (config) {
25179     // these go here, so the translation tool can replace them..
25180     this.errors = {
25181         PwdEmpty: "Please type a password, and then retype it to confirm.",
25182         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25183         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25184         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25185         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25186         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25187         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25188         TooWeak: "Your password is Too Weak."
25189     },
25190     this.meterLabel = "Password strength:";
25191     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25192     this.meterClass = [
25193         "roo-password-meter-tooweak", 
25194         "roo-password-meter-weak", 
25195         "roo-password-meter-medium", 
25196         "roo-password-meter-strong", 
25197         "roo-password-meter-grey"
25198     ];
25199     
25200     this.errors = {};
25201     
25202     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25203 }
25204
25205 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25206     /**
25207      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25208      * {
25209      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25210      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25211      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25212      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25213      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25214      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25215      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25216      * })
25217      */
25218     // private
25219     
25220     meterWidth: 300,
25221     errorMsg :'',    
25222     errors: false,
25223     imageRoot: '/',
25224     /**
25225      * @cfg {String/Object} Label for the strength meter (defaults to
25226      * 'Password strength:')
25227      */
25228     // private
25229     meterLabel: '',
25230     /**
25231      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25232      * ['Weak', 'Medium', 'Strong'])
25233      */
25234     // private    
25235     pwdStrengths: false,    
25236     // private
25237     strength: 0,
25238     // private
25239     _lastPwd: null,
25240     // private
25241     kCapitalLetter: 0,
25242     kSmallLetter: 1,
25243     kDigit: 2,
25244     kPunctuation: 3,
25245     
25246     insecure: false,
25247     // private
25248     initEvents: function ()
25249     {
25250         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25251
25252         if (this.el.is('input[type=password]') && Roo.isSafari) {
25253             this.el.on('keydown', this.SafariOnKeyDown, this);
25254         }
25255
25256         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25257     },
25258     // private
25259     onRender: function (ct, position)
25260     {
25261         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25262         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25263         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25264
25265         this.trigger.createChild({
25266                    cn: [
25267                     {
25268                     //id: 'PwdMeter',
25269                     tag: 'div',
25270                     cls: 'roo-password-meter-grey col-xs-12',
25271                     style: {
25272                         //width: 0,
25273                         //width: this.meterWidth + 'px'                                                
25274                         }
25275                     },
25276                     {                            
25277                          cls: 'roo-password-meter-text'                          
25278                     }
25279                 ]            
25280         });
25281
25282          
25283         if (this.hideTrigger) {
25284             this.trigger.setDisplayed(false);
25285         }
25286         this.setSize(this.width || '', this.height || '');
25287     },
25288     // private
25289     onDestroy: function ()
25290     {
25291         if (this.trigger) {
25292             this.trigger.removeAllListeners();
25293             this.trigger.remove();
25294         }
25295         if (this.wrap) {
25296             this.wrap.remove();
25297         }
25298         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25299     },
25300     // private
25301     checkStrength: function ()
25302     {
25303         var pwd = this.inputEl().getValue();
25304         if (pwd == this._lastPwd) {
25305             return;
25306         }
25307
25308         var strength;
25309         if (this.ClientSideStrongPassword(pwd)) {
25310             strength = 3;
25311         } else if (this.ClientSideMediumPassword(pwd)) {
25312             strength = 2;
25313         } else if (this.ClientSideWeakPassword(pwd)) {
25314             strength = 1;
25315         } else {
25316             strength = 0;
25317         }
25318         
25319         Roo.log('strength1: ' + strength);
25320         
25321         //var pm = this.trigger.child('div/div/div').dom;
25322         var pm = this.trigger.child('div/div');
25323         pm.removeClass(this.meterClass);
25324         pm.addClass(this.meterClass[strength]);
25325                 
25326         
25327         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25328                 
25329         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25330         
25331         this._lastPwd = pwd;
25332     },
25333     reset: function ()
25334     {
25335         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25336         
25337         this._lastPwd = '';
25338         
25339         var pm = this.trigger.child('div/div');
25340         pm.removeClass(this.meterClass);
25341         pm.addClass('roo-password-meter-grey');        
25342         
25343         
25344         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25345         
25346         pt.innerHTML = '';
25347         this.inputEl().dom.type='password';
25348     },
25349     // private
25350     validateValue: function (value)
25351     {
25352         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25353             return false;
25354         }
25355         if (value.length == 0) {
25356             if (this.allowBlank) {
25357                 this.clearInvalid();
25358                 return true;
25359             }
25360
25361             this.markInvalid(this.errors.PwdEmpty);
25362             this.errorMsg = this.errors.PwdEmpty;
25363             return false;
25364         }
25365         
25366         if(this.insecure){
25367             return true;
25368         }
25369         
25370         if (!value.match(/[\x21-\x7e]+/)) {
25371             this.markInvalid(this.errors.PwdBadChar);
25372             this.errorMsg = this.errors.PwdBadChar;
25373             return false;
25374         }
25375         if (value.length < 6) {
25376             this.markInvalid(this.errors.PwdShort);
25377             this.errorMsg = this.errors.PwdShort;
25378             return false;
25379         }
25380         if (value.length > 16) {
25381             this.markInvalid(this.errors.PwdLong);
25382             this.errorMsg = this.errors.PwdLong;
25383             return false;
25384         }
25385         var strength;
25386         if (this.ClientSideStrongPassword(value)) {
25387             strength = 3;
25388         } else if (this.ClientSideMediumPassword(value)) {
25389             strength = 2;
25390         } else if (this.ClientSideWeakPassword(value)) {
25391             strength = 1;
25392         } else {
25393             strength = 0;
25394         }
25395
25396         
25397         if (strength < 2) {
25398             //this.markInvalid(this.errors.TooWeak);
25399             this.errorMsg = this.errors.TooWeak;
25400             //return false;
25401         }
25402         
25403         
25404         console.log('strength2: ' + strength);
25405         
25406         //var pm = this.trigger.child('div/div/div').dom;
25407         
25408         var pm = this.trigger.child('div/div');
25409         pm.removeClass(this.meterClass);
25410         pm.addClass(this.meterClass[strength]);
25411                 
25412         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25413                 
25414         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25415         
25416         this.errorMsg = ''; 
25417         return true;
25418     },
25419     // private
25420     CharacterSetChecks: function (type)
25421     {
25422         this.type = type;
25423         this.fResult = false;
25424     },
25425     // private
25426     isctype: function (character, type)
25427     {
25428         switch (type) {  
25429             case this.kCapitalLetter:
25430                 if (character >= 'A' && character <= 'Z') {
25431                     return true;
25432                 }
25433                 break;
25434             
25435             case this.kSmallLetter:
25436                 if (character >= 'a' && character <= 'z') {
25437                     return true;
25438                 }
25439                 break;
25440             
25441             case this.kDigit:
25442                 if (character >= '0' && character <= '9') {
25443                     return true;
25444                 }
25445                 break;
25446             
25447             case this.kPunctuation:
25448                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25449                     return true;
25450                 }
25451                 break;
25452             
25453             default:
25454                 return false;
25455         }
25456
25457     },
25458     // private
25459     IsLongEnough: function (pwd, size)
25460     {
25461         return !(pwd == null || isNaN(size) || pwd.length < size);
25462     },
25463     // private
25464     SpansEnoughCharacterSets: function (word, nb)
25465     {
25466         if (!this.IsLongEnough(word, nb))
25467         {
25468             return false;
25469         }
25470
25471         var characterSetChecks = new Array(
25472             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25473             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25474         );
25475         
25476         for (var index = 0; index < word.length; ++index) {
25477             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25478                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25479                     characterSetChecks[nCharSet].fResult = true;
25480                     break;
25481                 }
25482             }
25483         }
25484
25485         var nCharSets = 0;
25486         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25487             if (characterSetChecks[nCharSet].fResult) {
25488                 ++nCharSets;
25489             }
25490         }
25491
25492         if (nCharSets < nb) {
25493             return false;
25494         }
25495         return true;
25496     },
25497     // private
25498     ClientSideStrongPassword: function (pwd)
25499     {
25500         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25501     },
25502     // private
25503     ClientSideMediumPassword: function (pwd)
25504     {
25505         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25506     },
25507     // private
25508     ClientSideWeakPassword: function (pwd)
25509     {
25510         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25511     }
25512           
25513 })//<script type="text/javascript">
25514
25515 /*
25516  * Based  Ext JS Library 1.1.1
25517  * Copyright(c) 2006-2007, Ext JS, LLC.
25518  * LGPL
25519  *
25520  */
25521  
25522 /**
25523  * @class Roo.HtmlEditorCore
25524  * @extends Roo.Component
25525  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25526  *
25527  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25528  */
25529
25530 Roo.HtmlEditorCore = function(config){
25531     
25532     
25533     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25534     
25535     
25536     this.addEvents({
25537         /**
25538          * @event initialize
25539          * Fires when the editor is fully initialized (including the iframe)
25540          * @param {Roo.HtmlEditorCore} this
25541          */
25542         initialize: true,
25543         /**
25544          * @event activate
25545          * Fires when the editor is first receives the focus. Any insertion must wait
25546          * until after this event.
25547          * @param {Roo.HtmlEditorCore} this
25548          */
25549         activate: true,
25550          /**
25551          * @event beforesync
25552          * Fires before the textarea is updated with content from the editor iframe. Return false
25553          * to cancel the sync.
25554          * @param {Roo.HtmlEditorCore} this
25555          * @param {String} html
25556          */
25557         beforesync: true,
25558          /**
25559          * @event beforepush
25560          * Fires before the iframe editor is updated with content from the textarea. Return false
25561          * to cancel the push.
25562          * @param {Roo.HtmlEditorCore} this
25563          * @param {String} html
25564          */
25565         beforepush: true,
25566          /**
25567          * @event sync
25568          * Fires when the textarea is updated with content from the editor iframe.
25569          * @param {Roo.HtmlEditorCore} this
25570          * @param {String} html
25571          */
25572         sync: true,
25573          /**
25574          * @event push
25575          * Fires when the iframe editor is updated with content from the textarea.
25576          * @param {Roo.HtmlEditorCore} this
25577          * @param {String} html
25578          */
25579         push: true,
25580         
25581         /**
25582          * @event editorevent
25583          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25584          * @param {Roo.HtmlEditorCore} this
25585          */
25586         editorevent: true
25587         
25588     });
25589     
25590     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25591     
25592     // defaults : white / black...
25593     this.applyBlacklists();
25594     
25595     
25596     
25597 };
25598
25599
25600 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25601
25602
25603      /**
25604      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25605      */
25606     
25607     owner : false,
25608     
25609      /**
25610      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25611      *                        Roo.resizable.
25612      */
25613     resizable : false,
25614      /**
25615      * @cfg {Number} height (in pixels)
25616      */   
25617     height: 300,
25618    /**
25619      * @cfg {Number} width (in pixels)
25620      */   
25621     width: 500,
25622     
25623     /**
25624      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25625      * 
25626      */
25627     stylesheets: false,
25628     
25629     /**
25630      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25631      */
25632     allowComments: false,
25633     // id of frame..
25634     frameId: false,
25635     
25636     // private properties
25637     validationEvent : false,
25638     deferHeight: true,
25639     initialized : false,
25640     activated : false,
25641     sourceEditMode : false,
25642     onFocus : Roo.emptyFn,
25643     iframePad:3,
25644     hideMode:'offsets',
25645     
25646     clearUp: true,
25647     
25648     // blacklist + whitelisted elements..
25649     black: false,
25650     white: false,
25651      
25652     bodyCls : '',
25653
25654     /**
25655      * Protected method that will not generally be called directly. It
25656      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25657      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25658      */
25659     getDocMarkup : function(){
25660         // body styles..
25661         var st = '';
25662         
25663         // inherit styels from page...?? 
25664         if (this.stylesheets === false) {
25665             
25666             Roo.get(document.head).select('style').each(function(node) {
25667                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25668             });
25669             
25670             Roo.get(document.head).select('link').each(function(node) { 
25671                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25672             });
25673             
25674         } else if (!this.stylesheets.length) {
25675                 // simple..
25676                 st = '<style type="text/css">' +
25677                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25678                    '</style>';
25679         } else {
25680             for (var i in this.stylesheets) { 
25681                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25682             }
25683             
25684         }
25685         
25686         st +=  '<style type="text/css">' +
25687             'IMG { cursor: pointer } ' +
25688         '</style>';
25689
25690         var cls = 'roo-htmleditor-body';
25691         
25692         if(this.bodyCls.length){
25693             cls += ' ' + this.bodyCls;
25694         }
25695         
25696         return '<html><head>' + st  +
25697             //<style type="text/css">' +
25698             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25699             //'</style>' +
25700             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25701     },
25702
25703     // private
25704     onRender : function(ct, position)
25705     {
25706         var _t = this;
25707         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25708         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25709         
25710         
25711         this.el.dom.style.border = '0 none';
25712         this.el.dom.setAttribute('tabIndex', -1);
25713         this.el.addClass('x-hidden hide');
25714         
25715         
25716         
25717         if(Roo.isIE){ // fix IE 1px bogus margin
25718             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25719         }
25720        
25721         
25722         this.frameId = Roo.id();
25723         
25724          
25725         
25726         var iframe = this.owner.wrap.createChild({
25727             tag: 'iframe',
25728             cls: 'form-control', // bootstrap..
25729             id: this.frameId,
25730             name: this.frameId,
25731             frameBorder : 'no',
25732             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25733         }, this.el
25734         );
25735         
25736         
25737         this.iframe = iframe.dom;
25738
25739          this.assignDocWin();
25740         
25741         this.doc.designMode = 'on';
25742        
25743         this.doc.open();
25744         this.doc.write(this.getDocMarkup());
25745         this.doc.close();
25746
25747         
25748         var task = { // must defer to wait for browser to be ready
25749             run : function(){
25750                 //console.log("run task?" + this.doc.readyState);
25751                 this.assignDocWin();
25752                 if(this.doc.body || this.doc.readyState == 'complete'){
25753                     try {
25754                         this.doc.designMode="on";
25755                     } catch (e) {
25756                         return;
25757                     }
25758                     Roo.TaskMgr.stop(task);
25759                     this.initEditor.defer(10, this);
25760                 }
25761             },
25762             interval : 10,
25763             duration: 10000,
25764             scope: this
25765         };
25766         Roo.TaskMgr.start(task);
25767
25768     },
25769
25770     // private
25771     onResize : function(w, h)
25772     {
25773          Roo.log('resize: ' +w + ',' + h );
25774         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25775         if(!this.iframe){
25776             return;
25777         }
25778         if(typeof w == 'number'){
25779             
25780             this.iframe.style.width = w + 'px';
25781         }
25782         if(typeof h == 'number'){
25783             
25784             this.iframe.style.height = h + 'px';
25785             if(this.doc){
25786                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25787             }
25788         }
25789         
25790     },
25791
25792     /**
25793      * Toggles the editor between standard and source edit mode.
25794      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25795      */
25796     toggleSourceEdit : function(sourceEditMode){
25797         
25798         this.sourceEditMode = sourceEditMode === true;
25799         
25800         if(this.sourceEditMode){
25801  
25802             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25803             
25804         }else{
25805             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25806             //this.iframe.className = '';
25807             this.deferFocus();
25808         }
25809         //this.setSize(this.owner.wrap.getSize());
25810         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25811     },
25812
25813     
25814   
25815
25816     /**
25817      * Protected method that will not generally be called directly. If you need/want
25818      * custom HTML cleanup, this is the method you should override.
25819      * @param {String} html The HTML to be cleaned
25820      * return {String} The cleaned HTML
25821      */
25822     cleanHtml : function(html){
25823         html = String(html);
25824         if(html.length > 5){
25825             if(Roo.isSafari){ // strip safari nonsense
25826                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25827             }
25828         }
25829         if(html == '&nbsp;'){
25830             html = '';
25831         }
25832         return html;
25833     },
25834
25835     /**
25836      * HTML Editor -> Textarea
25837      * Protected method that will not generally be called directly. Syncs the contents
25838      * of the editor iframe with the textarea.
25839      */
25840     syncValue : function(){
25841         if(this.initialized){
25842             var bd = (this.doc.body || this.doc.documentElement);
25843             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25844             var html = bd.innerHTML;
25845             if(Roo.isSafari){
25846                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25847                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25848                 if(m && m[1]){
25849                     html = '<div style="'+m[0]+'">' + html + '</div>';
25850                 }
25851             }
25852             html = this.cleanHtml(html);
25853             // fix up the special chars.. normaly like back quotes in word...
25854             // however we do not want to do this with chinese..
25855             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25856                 
25857                 var cc = match.charCodeAt();
25858
25859                 // Get the character value, handling surrogate pairs
25860                 if (match.length == 2) {
25861                     // It's a surrogate pair, calculate the Unicode code point
25862                     var high = match.charCodeAt(0) - 0xD800;
25863                     var low  = match.charCodeAt(1) - 0xDC00;
25864                     cc = (high * 0x400) + low + 0x10000;
25865                 }  else if (
25866                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25867                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25868                     (cc >= 0xf900 && cc < 0xfb00 )
25869                 ) {
25870                         return match;
25871                 }  
25872          
25873                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25874                 return "&#" + cc + ";";
25875                 
25876                 
25877             });
25878             
25879             
25880              
25881             if(this.owner.fireEvent('beforesync', this, html) !== false){
25882                 this.el.dom.value = html;
25883                 this.owner.fireEvent('sync', this, html);
25884             }
25885         }
25886     },
25887
25888     /**
25889      * Protected method that will not generally be called directly. Pushes the value of the textarea
25890      * into the iframe editor.
25891      */
25892     pushValue : function(){
25893         if(this.initialized){
25894             var v = this.el.dom.value.trim();
25895             
25896 //            if(v.length < 1){
25897 //                v = '&#160;';
25898 //            }
25899             
25900             if(this.owner.fireEvent('beforepush', this, v) !== false){
25901                 var d = (this.doc.body || this.doc.documentElement);
25902                 d.innerHTML = v;
25903                 this.cleanUpPaste();
25904                 this.el.dom.value = d.innerHTML;
25905                 this.owner.fireEvent('push', this, v);
25906             }
25907         }
25908     },
25909
25910     // private
25911     deferFocus : function(){
25912         this.focus.defer(10, this);
25913     },
25914
25915     // doc'ed in Field
25916     focus : function(){
25917         if(this.win && !this.sourceEditMode){
25918             this.win.focus();
25919         }else{
25920             this.el.focus();
25921         }
25922     },
25923     
25924     assignDocWin: function()
25925     {
25926         var iframe = this.iframe;
25927         
25928          if(Roo.isIE){
25929             this.doc = iframe.contentWindow.document;
25930             this.win = iframe.contentWindow;
25931         } else {
25932 //            if (!Roo.get(this.frameId)) {
25933 //                return;
25934 //            }
25935 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25936 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25937             
25938             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25939                 return;
25940             }
25941             
25942             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25943             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25944         }
25945     },
25946     
25947     // private
25948     initEditor : function(){
25949         //console.log("INIT EDITOR");
25950         this.assignDocWin();
25951         
25952         
25953         
25954         this.doc.designMode="on";
25955         this.doc.open();
25956         this.doc.write(this.getDocMarkup());
25957         this.doc.close();
25958         
25959         var dbody = (this.doc.body || this.doc.documentElement);
25960         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25961         // this copies styles from the containing element into thsi one..
25962         // not sure why we need all of this..
25963         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25964         
25965         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25966         //ss['background-attachment'] = 'fixed'; // w3c
25967         dbody.bgProperties = 'fixed'; // ie
25968         //Roo.DomHelper.applyStyles(dbody, ss);
25969         Roo.EventManager.on(this.doc, {
25970             //'mousedown': this.onEditorEvent,
25971             'mouseup': this.onEditorEvent,
25972             'dblclick': this.onEditorEvent,
25973             'click': this.onEditorEvent,
25974             'keyup': this.onEditorEvent,
25975             buffer:100,
25976             scope: this
25977         });
25978         if(Roo.isGecko){
25979             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25980         }
25981         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25982             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25983         }
25984         this.initialized = true;
25985
25986         this.owner.fireEvent('initialize', this);
25987         this.pushValue();
25988     },
25989
25990     // private
25991     onDestroy : function(){
25992         
25993         
25994         
25995         if(this.rendered){
25996             
25997             //for (var i =0; i < this.toolbars.length;i++) {
25998             //    // fixme - ask toolbars for heights?
25999             //    this.toolbars[i].onDestroy();
26000            // }
26001             
26002             //this.wrap.dom.innerHTML = '';
26003             //this.wrap.remove();
26004         }
26005     },
26006
26007     // private
26008     onFirstFocus : function(){
26009         
26010         this.assignDocWin();
26011         
26012         
26013         this.activated = true;
26014          
26015     
26016         if(Roo.isGecko){ // prevent silly gecko errors
26017             this.win.focus();
26018             var s = this.win.getSelection();
26019             if(!s.focusNode || s.focusNode.nodeType != 3){
26020                 var r = s.getRangeAt(0);
26021                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26022                 r.collapse(true);
26023                 this.deferFocus();
26024             }
26025             try{
26026                 this.execCmd('useCSS', true);
26027                 this.execCmd('styleWithCSS', false);
26028             }catch(e){}
26029         }
26030         this.owner.fireEvent('activate', this);
26031     },
26032
26033     // private
26034     adjustFont: function(btn){
26035         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26036         //if(Roo.isSafari){ // safari
26037         //    adjust *= 2;
26038        // }
26039         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26040         if(Roo.isSafari){ // safari
26041             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26042             v =  (v < 10) ? 10 : v;
26043             v =  (v > 48) ? 48 : v;
26044             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26045             
26046         }
26047         
26048         
26049         v = Math.max(1, v+adjust);
26050         
26051         this.execCmd('FontSize', v  );
26052     },
26053
26054     onEditorEvent : function(e)
26055     {
26056         this.owner.fireEvent('editorevent', this, e);
26057       //  this.updateToolbar();
26058         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26059     },
26060
26061     insertTag : function(tg)
26062     {
26063         // could be a bit smarter... -> wrap the current selected tRoo..
26064         if (tg.toLowerCase() == 'span' ||
26065             tg.toLowerCase() == 'code' ||
26066             tg.toLowerCase() == 'sup' ||
26067             tg.toLowerCase() == 'sub' 
26068             ) {
26069             
26070             range = this.createRange(this.getSelection());
26071             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26072             wrappingNode.appendChild(range.extractContents());
26073             range.insertNode(wrappingNode);
26074
26075             return;
26076             
26077             
26078             
26079         }
26080         this.execCmd("formatblock",   tg);
26081         
26082     },
26083     
26084     insertText : function(txt)
26085     {
26086         
26087         
26088         var range = this.createRange();
26089         range.deleteContents();
26090                //alert(Sender.getAttribute('label'));
26091                
26092         range.insertNode(this.doc.createTextNode(txt));
26093     } ,
26094     
26095      
26096
26097     /**
26098      * Executes a Midas editor command on the editor document and performs necessary focus and
26099      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26100      * @param {String} cmd The Midas command
26101      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26102      */
26103     relayCmd : function(cmd, value){
26104         this.win.focus();
26105         this.execCmd(cmd, value);
26106         this.owner.fireEvent('editorevent', this);
26107         //this.updateToolbar();
26108         this.owner.deferFocus();
26109     },
26110
26111     /**
26112      * Executes a Midas editor command directly on the editor document.
26113      * For visual commands, you should use {@link #relayCmd} instead.
26114      * <b>This should only be called after the editor is initialized.</b>
26115      * @param {String} cmd The Midas command
26116      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26117      */
26118     execCmd : function(cmd, value){
26119         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26120         this.syncValue();
26121     },
26122  
26123  
26124    
26125     /**
26126      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26127      * to insert tRoo.
26128      * @param {String} text | dom node.. 
26129      */
26130     insertAtCursor : function(text)
26131     {
26132         
26133         if(!this.activated){
26134             return;
26135         }
26136         /*
26137         if(Roo.isIE){
26138             this.win.focus();
26139             var r = this.doc.selection.createRange();
26140             if(r){
26141                 r.collapse(true);
26142                 r.pasteHTML(text);
26143                 this.syncValue();
26144                 this.deferFocus();
26145             
26146             }
26147             return;
26148         }
26149         */
26150         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26151             this.win.focus();
26152             
26153             
26154             // from jquery ui (MIT licenced)
26155             var range, node;
26156             var win = this.win;
26157             
26158             if (win.getSelection && win.getSelection().getRangeAt) {
26159                 range = win.getSelection().getRangeAt(0);
26160                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26161                 range.insertNode(node);
26162             } else if (win.document.selection && win.document.selection.createRange) {
26163                 // no firefox support
26164                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26165                 win.document.selection.createRange().pasteHTML(txt);
26166             } else {
26167                 // no firefox support
26168                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26169                 this.execCmd('InsertHTML', txt);
26170             } 
26171             
26172             this.syncValue();
26173             
26174             this.deferFocus();
26175         }
26176     },
26177  // private
26178     mozKeyPress : function(e){
26179         if(e.ctrlKey){
26180             var c = e.getCharCode(), cmd;
26181           
26182             if(c > 0){
26183                 c = String.fromCharCode(c).toLowerCase();
26184                 switch(c){
26185                     case 'b':
26186                         cmd = 'bold';
26187                         break;
26188                     case 'i':
26189                         cmd = 'italic';
26190                         break;
26191                     
26192                     case 'u':
26193                         cmd = 'underline';
26194                         break;
26195                     
26196                     case 'v':
26197                         this.cleanUpPaste.defer(100, this);
26198                         return;
26199                         
26200                 }
26201                 if(cmd){
26202                     this.win.focus();
26203                     this.execCmd(cmd);
26204                     this.deferFocus();
26205                     e.preventDefault();
26206                 }
26207                 
26208             }
26209         }
26210     },
26211
26212     // private
26213     fixKeys : function(){ // load time branching for fastest keydown performance
26214         if(Roo.isIE){
26215             return function(e){
26216                 var k = e.getKey(), r;
26217                 if(k == e.TAB){
26218                     e.stopEvent();
26219                     r = this.doc.selection.createRange();
26220                     if(r){
26221                         r.collapse(true);
26222                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26223                         this.deferFocus();
26224                     }
26225                     return;
26226                 }
26227                 
26228                 if(k == e.ENTER){
26229                     r = this.doc.selection.createRange();
26230                     if(r){
26231                         var target = r.parentElement();
26232                         if(!target || target.tagName.toLowerCase() != 'li'){
26233                             e.stopEvent();
26234                             r.pasteHTML('<br />');
26235                             r.collapse(false);
26236                             r.select();
26237                         }
26238                     }
26239                 }
26240                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26241                     this.cleanUpPaste.defer(100, this);
26242                     return;
26243                 }
26244                 
26245                 
26246             };
26247         }else if(Roo.isOpera){
26248             return function(e){
26249                 var k = e.getKey();
26250                 if(k == e.TAB){
26251                     e.stopEvent();
26252                     this.win.focus();
26253                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26254                     this.deferFocus();
26255                 }
26256                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26257                     this.cleanUpPaste.defer(100, this);
26258                     return;
26259                 }
26260                 
26261             };
26262         }else if(Roo.isSafari){
26263             return function(e){
26264                 var k = e.getKey();
26265                 
26266                 if(k == e.TAB){
26267                     e.stopEvent();
26268                     this.execCmd('InsertText','\t');
26269                     this.deferFocus();
26270                     return;
26271                 }
26272                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26273                     this.cleanUpPaste.defer(100, this);
26274                     return;
26275                 }
26276                 
26277              };
26278         }
26279     }(),
26280     
26281     getAllAncestors: function()
26282     {
26283         var p = this.getSelectedNode();
26284         var a = [];
26285         if (!p) {
26286             a.push(p); // push blank onto stack..
26287             p = this.getParentElement();
26288         }
26289         
26290         
26291         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26292             a.push(p);
26293             p = p.parentNode;
26294         }
26295         a.push(this.doc.body);
26296         return a;
26297     },
26298     lastSel : false,
26299     lastSelNode : false,
26300     
26301     
26302     getSelection : function() 
26303     {
26304         this.assignDocWin();
26305         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26306     },
26307     
26308     getSelectedNode: function() 
26309     {
26310         // this may only work on Gecko!!!
26311         
26312         // should we cache this!!!!
26313         
26314         
26315         
26316          
26317         var range = this.createRange(this.getSelection()).cloneRange();
26318         
26319         if (Roo.isIE) {
26320             var parent = range.parentElement();
26321             while (true) {
26322                 var testRange = range.duplicate();
26323                 testRange.moveToElementText(parent);
26324                 if (testRange.inRange(range)) {
26325                     break;
26326                 }
26327                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26328                     break;
26329                 }
26330                 parent = parent.parentElement;
26331             }
26332             return parent;
26333         }
26334         
26335         // is ancestor a text element.
26336         var ac =  range.commonAncestorContainer;
26337         if (ac.nodeType == 3) {
26338             ac = ac.parentNode;
26339         }
26340         
26341         var ar = ac.childNodes;
26342          
26343         var nodes = [];
26344         var other_nodes = [];
26345         var has_other_nodes = false;
26346         for (var i=0;i<ar.length;i++) {
26347             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26348                 continue;
26349             }
26350             // fullly contained node.
26351             
26352             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26353                 nodes.push(ar[i]);
26354                 continue;
26355             }
26356             
26357             // probably selected..
26358             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26359                 other_nodes.push(ar[i]);
26360                 continue;
26361             }
26362             // outer..
26363             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26364                 continue;
26365             }
26366             
26367             
26368             has_other_nodes = true;
26369         }
26370         if (!nodes.length && other_nodes.length) {
26371             nodes= other_nodes;
26372         }
26373         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26374             return false;
26375         }
26376         
26377         return nodes[0];
26378     },
26379     createRange: function(sel)
26380     {
26381         // this has strange effects when using with 
26382         // top toolbar - not sure if it's a great idea.
26383         //this.editor.contentWindow.focus();
26384         if (typeof sel != "undefined") {
26385             try {
26386                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26387             } catch(e) {
26388                 return this.doc.createRange();
26389             }
26390         } else {
26391             return this.doc.createRange();
26392         }
26393     },
26394     getParentElement: function()
26395     {
26396         
26397         this.assignDocWin();
26398         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26399         
26400         var range = this.createRange(sel);
26401          
26402         try {
26403             var p = range.commonAncestorContainer;
26404             while (p.nodeType == 3) { // text node
26405                 p = p.parentNode;
26406             }
26407             return p;
26408         } catch (e) {
26409             return null;
26410         }
26411     
26412     },
26413     /***
26414      *
26415      * Range intersection.. the hard stuff...
26416      *  '-1' = before
26417      *  '0' = hits..
26418      *  '1' = after.
26419      *         [ -- selected range --- ]
26420      *   [fail]                        [fail]
26421      *
26422      *    basically..
26423      *      if end is before start or  hits it. fail.
26424      *      if start is after end or hits it fail.
26425      *
26426      *   if either hits (but other is outside. - then it's not 
26427      *   
26428      *    
26429      **/
26430     
26431     
26432     // @see http://www.thismuchiknow.co.uk/?p=64.
26433     rangeIntersectsNode : function(range, node)
26434     {
26435         var nodeRange = node.ownerDocument.createRange();
26436         try {
26437             nodeRange.selectNode(node);
26438         } catch (e) {
26439             nodeRange.selectNodeContents(node);
26440         }
26441     
26442         var rangeStartRange = range.cloneRange();
26443         rangeStartRange.collapse(true);
26444     
26445         var rangeEndRange = range.cloneRange();
26446         rangeEndRange.collapse(false);
26447     
26448         var nodeStartRange = nodeRange.cloneRange();
26449         nodeStartRange.collapse(true);
26450     
26451         var nodeEndRange = nodeRange.cloneRange();
26452         nodeEndRange.collapse(false);
26453     
26454         return rangeStartRange.compareBoundaryPoints(
26455                  Range.START_TO_START, nodeEndRange) == -1 &&
26456                rangeEndRange.compareBoundaryPoints(
26457                  Range.START_TO_START, nodeStartRange) == 1;
26458         
26459          
26460     },
26461     rangeCompareNode : function(range, node)
26462     {
26463         var nodeRange = node.ownerDocument.createRange();
26464         try {
26465             nodeRange.selectNode(node);
26466         } catch (e) {
26467             nodeRange.selectNodeContents(node);
26468         }
26469         
26470         
26471         range.collapse(true);
26472     
26473         nodeRange.collapse(true);
26474      
26475         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26476         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26477          
26478         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26479         
26480         var nodeIsBefore   =  ss == 1;
26481         var nodeIsAfter    = ee == -1;
26482         
26483         if (nodeIsBefore && nodeIsAfter) {
26484             return 0; // outer
26485         }
26486         if (!nodeIsBefore && nodeIsAfter) {
26487             return 1; //right trailed.
26488         }
26489         
26490         if (nodeIsBefore && !nodeIsAfter) {
26491             return 2;  // left trailed.
26492         }
26493         // fully contined.
26494         return 3;
26495     },
26496
26497     // private? - in a new class?
26498     cleanUpPaste :  function()
26499     {
26500         // cleans up the whole document..
26501         Roo.log('cleanuppaste');
26502         
26503         this.cleanUpChildren(this.doc.body);
26504         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26505         if (clean != this.doc.body.innerHTML) {
26506             this.doc.body.innerHTML = clean;
26507         }
26508         
26509     },
26510     
26511     cleanWordChars : function(input) {// change the chars to hex code
26512         var he = Roo.HtmlEditorCore;
26513         
26514         var output = input;
26515         Roo.each(he.swapCodes, function(sw) { 
26516             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26517             
26518             output = output.replace(swapper, sw[1]);
26519         });
26520         
26521         return output;
26522     },
26523     
26524     
26525     cleanUpChildren : function (n)
26526     {
26527         if (!n.childNodes.length) {
26528             return;
26529         }
26530         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26531            this.cleanUpChild(n.childNodes[i]);
26532         }
26533     },
26534     
26535     
26536         
26537     
26538     cleanUpChild : function (node)
26539     {
26540         var ed = this;
26541         //console.log(node);
26542         if (node.nodeName == "#text") {
26543             // clean up silly Windows -- stuff?
26544             return; 
26545         }
26546         if (node.nodeName == "#comment") {
26547             if (!this.allowComments) {
26548                 node.parentNode.removeChild(node);
26549             }
26550             // clean up silly Windows -- stuff?
26551             return; 
26552         }
26553         var lcname = node.tagName.toLowerCase();
26554         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26555         // whitelist of tags..
26556         
26557         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26558             // remove node.
26559             node.parentNode.removeChild(node);
26560             return;
26561             
26562         }
26563         
26564         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26565         
26566         // spans with no attributes - just remove them..
26567         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26568             remove_keep_children = true;
26569         }
26570         
26571         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26572         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26573         
26574         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26575         //    remove_keep_children = true;
26576         //}
26577         
26578         if (remove_keep_children) {
26579             this.cleanUpChildren(node);
26580             // inserts everything just before this node...
26581             while (node.childNodes.length) {
26582                 var cn = node.childNodes[0];
26583                 node.removeChild(cn);
26584                 node.parentNode.insertBefore(cn, node);
26585             }
26586             node.parentNode.removeChild(node);
26587             return;
26588         }
26589         
26590         if (!node.attributes || !node.attributes.length) {
26591             
26592           
26593             
26594             
26595             this.cleanUpChildren(node);
26596             return;
26597         }
26598         
26599         function cleanAttr(n,v)
26600         {
26601             
26602             if (v.match(/^\./) || v.match(/^\//)) {
26603                 return;
26604             }
26605             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26606                 return;
26607             }
26608             if (v.match(/^#/)) {
26609                 return;
26610             }
26611             if (v.match(/^\{/)) { // allow template editing.
26612                 return;
26613             }
26614 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26615             node.removeAttribute(n);
26616             
26617         }
26618         
26619         var cwhite = this.cwhite;
26620         var cblack = this.cblack;
26621             
26622         function cleanStyle(n,v)
26623         {
26624             if (v.match(/expression/)) { //XSS?? should we even bother..
26625                 node.removeAttribute(n);
26626                 return;
26627             }
26628             
26629             var parts = v.split(/;/);
26630             var clean = [];
26631             
26632             Roo.each(parts, function(p) {
26633                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26634                 if (!p.length) {
26635                     return true;
26636                 }
26637                 var l = p.split(':').shift().replace(/\s+/g,'');
26638                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26639                 
26640                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26641 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26642                     //node.removeAttribute(n);
26643                     return true;
26644                 }
26645                 //Roo.log()
26646                 // only allow 'c whitelisted system attributes'
26647                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26648 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26649                     //node.removeAttribute(n);
26650                     return true;
26651                 }
26652                 
26653                 
26654                  
26655                 
26656                 clean.push(p);
26657                 return true;
26658             });
26659             if (clean.length) { 
26660                 node.setAttribute(n, clean.join(';'));
26661             } else {
26662                 node.removeAttribute(n);
26663             }
26664             
26665         }
26666         
26667         
26668         for (var i = node.attributes.length-1; i > -1 ; i--) {
26669             var a = node.attributes[i];
26670             //console.log(a);
26671             
26672             if (a.name.toLowerCase().substr(0,2)=='on')  {
26673                 node.removeAttribute(a.name);
26674                 continue;
26675             }
26676             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26677                 node.removeAttribute(a.name);
26678                 continue;
26679             }
26680             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26681                 cleanAttr(a.name,a.value); // fixme..
26682                 continue;
26683             }
26684             if (a.name == 'style') {
26685                 cleanStyle(a.name,a.value);
26686                 continue;
26687             }
26688             /// clean up MS crap..
26689             // tecnically this should be a list of valid class'es..
26690             
26691             
26692             if (a.name == 'class') {
26693                 if (a.value.match(/^Mso/)) {
26694                     node.removeAttribute('class');
26695                 }
26696                 
26697                 if (a.value.match(/^body$/)) {
26698                     node.removeAttribute('class');
26699                 }
26700                 continue;
26701             }
26702             
26703             // style cleanup!?
26704             // class cleanup?
26705             
26706         }
26707         
26708         
26709         this.cleanUpChildren(node);
26710         
26711         
26712     },
26713     
26714     /**
26715      * Clean up MS wordisms...
26716      */
26717     cleanWord : function(node)
26718     {
26719         if (!node) {
26720             this.cleanWord(this.doc.body);
26721             return;
26722         }
26723         
26724         if(
26725                 node.nodeName == 'SPAN' &&
26726                 !node.hasAttributes() &&
26727                 node.childNodes.length == 1 &&
26728                 node.firstChild.nodeName == "#text"  
26729         ) {
26730             var textNode = node.firstChild;
26731             node.removeChild(textNode);
26732             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26733                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26734             }
26735             node.parentNode.insertBefore(textNode, node);
26736             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26737                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26738             }
26739             node.parentNode.removeChild(node);
26740         }
26741         
26742         if (node.nodeName == "#text") {
26743             // clean up silly Windows -- stuff?
26744             return; 
26745         }
26746         if (node.nodeName == "#comment") {
26747             node.parentNode.removeChild(node);
26748             // clean up silly Windows -- stuff?
26749             return; 
26750         }
26751         
26752         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26753             node.parentNode.removeChild(node);
26754             return;
26755         }
26756         //Roo.log(node.tagName);
26757         // remove - but keep children..
26758         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26759             //Roo.log('-- removed');
26760             while (node.childNodes.length) {
26761                 var cn = node.childNodes[0];
26762                 node.removeChild(cn);
26763                 node.parentNode.insertBefore(cn, node);
26764                 // move node to parent - and clean it..
26765                 this.cleanWord(cn);
26766             }
26767             node.parentNode.removeChild(node);
26768             /// no need to iterate chidlren = it's got none..
26769             //this.iterateChildren(node, this.cleanWord);
26770             return;
26771         }
26772         // clean styles
26773         if (node.className.length) {
26774             
26775             var cn = node.className.split(/\W+/);
26776             var cna = [];
26777             Roo.each(cn, function(cls) {
26778                 if (cls.match(/Mso[a-zA-Z]+/)) {
26779                     return;
26780                 }
26781                 cna.push(cls);
26782             });
26783             node.className = cna.length ? cna.join(' ') : '';
26784             if (!cna.length) {
26785                 node.removeAttribute("class");
26786             }
26787         }
26788         
26789         if (node.hasAttribute("lang")) {
26790             node.removeAttribute("lang");
26791         }
26792         
26793         if (node.hasAttribute("style")) {
26794             
26795             var styles = node.getAttribute("style").split(";");
26796             var nstyle = [];
26797             Roo.each(styles, function(s) {
26798                 if (!s.match(/:/)) {
26799                     return;
26800                 }
26801                 var kv = s.split(":");
26802                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26803                     return;
26804                 }
26805                 // what ever is left... we allow.
26806                 nstyle.push(s);
26807             });
26808             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26809             if (!nstyle.length) {
26810                 node.removeAttribute('style');
26811             }
26812         }
26813         this.iterateChildren(node, this.cleanWord);
26814         
26815         
26816         
26817     },
26818     /**
26819      * iterateChildren of a Node, calling fn each time, using this as the scole..
26820      * @param {DomNode} node node to iterate children of.
26821      * @param {Function} fn method of this class to call on each item.
26822      */
26823     iterateChildren : function(node, fn)
26824     {
26825         if (!node.childNodes.length) {
26826                 return;
26827         }
26828         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26829            fn.call(this, node.childNodes[i])
26830         }
26831     },
26832     
26833     
26834     /**
26835      * cleanTableWidths.
26836      *
26837      * Quite often pasting from word etc.. results in tables with column and widths.
26838      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26839      *
26840      */
26841     cleanTableWidths : function(node)
26842     {
26843          
26844          
26845         if (!node) {
26846             this.cleanTableWidths(this.doc.body);
26847             return;
26848         }
26849         
26850         // ignore list...
26851         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26852             return; 
26853         }
26854         Roo.log(node.tagName);
26855         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26856             this.iterateChildren(node, this.cleanTableWidths);
26857             return;
26858         }
26859         if (node.hasAttribute('width')) {
26860             node.removeAttribute('width');
26861         }
26862         
26863          
26864         if (node.hasAttribute("style")) {
26865             // pretty basic...
26866             
26867             var styles = node.getAttribute("style").split(";");
26868             var nstyle = [];
26869             Roo.each(styles, function(s) {
26870                 if (!s.match(/:/)) {
26871                     return;
26872                 }
26873                 var kv = s.split(":");
26874                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26875                     return;
26876                 }
26877                 // what ever is left... we allow.
26878                 nstyle.push(s);
26879             });
26880             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26881             if (!nstyle.length) {
26882                 node.removeAttribute('style');
26883             }
26884         }
26885         
26886         this.iterateChildren(node, this.cleanTableWidths);
26887         
26888         
26889     },
26890     
26891     
26892     
26893     
26894     domToHTML : function(currentElement, depth, nopadtext) {
26895         
26896         depth = depth || 0;
26897         nopadtext = nopadtext || false;
26898     
26899         if (!currentElement) {
26900             return this.domToHTML(this.doc.body);
26901         }
26902         
26903         //Roo.log(currentElement);
26904         var j;
26905         var allText = false;
26906         var nodeName = currentElement.nodeName;
26907         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26908         
26909         if  (nodeName == '#text') {
26910             
26911             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26912         }
26913         
26914         
26915         var ret = '';
26916         if (nodeName != 'BODY') {
26917              
26918             var i = 0;
26919             // Prints the node tagName, such as <A>, <IMG>, etc
26920             if (tagName) {
26921                 var attr = [];
26922                 for(i = 0; i < currentElement.attributes.length;i++) {
26923                     // quoting?
26924                     var aname = currentElement.attributes.item(i).name;
26925                     if (!currentElement.attributes.item(i).value.length) {
26926                         continue;
26927                     }
26928                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26929                 }
26930                 
26931                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26932             } 
26933             else {
26934                 
26935                 // eack
26936             }
26937         } else {
26938             tagName = false;
26939         }
26940         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26941             return ret;
26942         }
26943         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26944             nopadtext = true;
26945         }
26946         
26947         
26948         // Traverse the tree
26949         i = 0;
26950         var currentElementChild = currentElement.childNodes.item(i);
26951         var allText = true;
26952         var innerHTML  = '';
26953         lastnode = '';
26954         while (currentElementChild) {
26955             // Formatting code (indent the tree so it looks nice on the screen)
26956             var nopad = nopadtext;
26957             if (lastnode == 'SPAN') {
26958                 nopad  = true;
26959             }
26960             // text
26961             if  (currentElementChild.nodeName == '#text') {
26962                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26963                 toadd = nopadtext ? toadd : toadd.trim();
26964                 if (!nopad && toadd.length > 80) {
26965                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26966                 }
26967                 innerHTML  += toadd;
26968                 
26969                 i++;
26970                 currentElementChild = currentElement.childNodes.item(i);
26971                 lastNode = '';
26972                 continue;
26973             }
26974             allText = false;
26975             
26976             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26977                 
26978             // Recursively traverse the tree structure of the child node
26979             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26980             lastnode = currentElementChild.nodeName;
26981             i++;
26982             currentElementChild=currentElement.childNodes.item(i);
26983         }
26984         
26985         ret += innerHTML;
26986         
26987         if (!allText) {
26988                 // The remaining code is mostly for formatting the tree
26989             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26990         }
26991         
26992         
26993         if (tagName) {
26994             ret+= "</"+tagName+">";
26995         }
26996         return ret;
26997         
26998     },
26999         
27000     applyBlacklists : function()
27001     {
27002         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27003         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27004         
27005         this.white = [];
27006         this.black = [];
27007         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27008             if (b.indexOf(tag) > -1) {
27009                 return;
27010             }
27011             this.white.push(tag);
27012             
27013         }, this);
27014         
27015         Roo.each(w, function(tag) {
27016             if (b.indexOf(tag) > -1) {
27017                 return;
27018             }
27019             if (this.white.indexOf(tag) > -1) {
27020                 return;
27021             }
27022             this.white.push(tag);
27023             
27024         }, this);
27025         
27026         
27027         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27028             if (w.indexOf(tag) > -1) {
27029                 return;
27030             }
27031             this.black.push(tag);
27032             
27033         }, this);
27034         
27035         Roo.each(b, function(tag) {
27036             if (w.indexOf(tag) > -1) {
27037                 return;
27038             }
27039             if (this.black.indexOf(tag) > -1) {
27040                 return;
27041             }
27042             this.black.push(tag);
27043             
27044         }, this);
27045         
27046         
27047         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27048         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27049         
27050         this.cwhite = [];
27051         this.cblack = [];
27052         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27053             if (b.indexOf(tag) > -1) {
27054                 return;
27055             }
27056             this.cwhite.push(tag);
27057             
27058         }, this);
27059         
27060         Roo.each(w, function(tag) {
27061             if (b.indexOf(tag) > -1) {
27062                 return;
27063             }
27064             if (this.cwhite.indexOf(tag) > -1) {
27065                 return;
27066             }
27067             this.cwhite.push(tag);
27068             
27069         }, this);
27070         
27071         
27072         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27073             if (w.indexOf(tag) > -1) {
27074                 return;
27075             }
27076             this.cblack.push(tag);
27077             
27078         }, this);
27079         
27080         Roo.each(b, function(tag) {
27081             if (w.indexOf(tag) > -1) {
27082                 return;
27083             }
27084             if (this.cblack.indexOf(tag) > -1) {
27085                 return;
27086             }
27087             this.cblack.push(tag);
27088             
27089         }, this);
27090     },
27091     
27092     setStylesheets : function(stylesheets)
27093     {
27094         if(typeof(stylesheets) == 'string'){
27095             Roo.get(this.iframe.contentDocument.head).createChild({
27096                 tag : 'link',
27097                 rel : 'stylesheet',
27098                 type : 'text/css',
27099                 href : stylesheets
27100             });
27101             
27102             return;
27103         }
27104         var _this = this;
27105      
27106         Roo.each(stylesheets, function(s) {
27107             if(!s.length){
27108                 return;
27109             }
27110             
27111             Roo.get(_this.iframe.contentDocument.head).createChild({
27112                 tag : 'link',
27113                 rel : 'stylesheet',
27114                 type : 'text/css',
27115                 href : s
27116             });
27117         });
27118
27119         
27120     },
27121     
27122     removeStylesheets : function()
27123     {
27124         var _this = this;
27125         
27126         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27127             s.remove();
27128         });
27129     },
27130     
27131     setStyle : function(style)
27132     {
27133         Roo.get(this.iframe.contentDocument.head).createChild({
27134             tag : 'style',
27135             type : 'text/css',
27136             html : style
27137         });
27138
27139         return;
27140     }
27141     
27142     // hide stuff that is not compatible
27143     /**
27144      * @event blur
27145      * @hide
27146      */
27147     /**
27148      * @event change
27149      * @hide
27150      */
27151     /**
27152      * @event focus
27153      * @hide
27154      */
27155     /**
27156      * @event specialkey
27157      * @hide
27158      */
27159     /**
27160      * @cfg {String} fieldClass @hide
27161      */
27162     /**
27163      * @cfg {String} focusClass @hide
27164      */
27165     /**
27166      * @cfg {String} autoCreate @hide
27167      */
27168     /**
27169      * @cfg {String} inputType @hide
27170      */
27171     /**
27172      * @cfg {String} invalidClass @hide
27173      */
27174     /**
27175      * @cfg {String} invalidText @hide
27176      */
27177     /**
27178      * @cfg {String} msgFx @hide
27179      */
27180     /**
27181      * @cfg {String} validateOnBlur @hide
27182      */
27183 });
27184
27185 Roo.HtmlEditorCore.white = [
27186         'area', 'br', 'img', 'input', 'hr', 'wbr',
27187         
27188        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27189        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27190        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27191        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27192        'table',   'ul',         'xmp', 
27193        
27194        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27195       'thead',   'tr', 
27196      
27197       'dir', 'menu', 'ol', 'ul', 'dl',
27198        
27199       'embed',  'object'
27200 ];
27201
27202
27203 Roo.HtmlEditorCore.black = [
27204     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27205         'applet', // 
27206         'base',   'basefont', 'bgsound', 'blink',  'body', 
27207         'frame',  'frameset', 'head',    'html',   'ilayer', 
27208         'iframe', 'layer',  'link',     'meta',    'object',   
27209         'script', 'style' ,'title',  'xml' // clean later..
27210 ];
27211 Roo.HtmlEditorCore.clean = [
27212     'script', 'style', 'title', 'xml'
27213 ];
27214 Roo.HtmlEditorCore.remove = [
27215     'font'
27216 ];
27217 // attributes..
27218
27219 Roo.HtmlEditorCore.ablack = [
27220     'on'
27221 ];
27222     
27223 Roo.HtmlEditorCore.aclean = [ 
27224     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27225 ];
27226
27227 // protocols..
27228 Roo.HtmlEditorCore.pwhite= [
27229         'http',  'https',  'mailto'
27230 ];
27231
27232 // white listed style attributes.
27233 Roo.HtmlEditorCore.cwhite= [
27234       //  'text-align', /// default is to allow most things..
27235       
27236          
27237 //        'font-size'//??
27238 ];
27239
27240 // black listed style attributes.
27241 Roo.HtmlEditorCore.cblack= [
27242       //  'font-size' -- this can be set by the project 
27243 ];
27244
27245
27246 Roo.HtmlEditorCore.swapCodes   =[ 
27247     [    8211, "&#8211;" ], 
27248     [    8212, "&#8212;" ], 
27249     [    8216,  "'" ],  
27250     [    8217, "'" ],  
27251     [    8220, '"' ],  
27252     [    8221, '"' ],  
27253     [    8226, "*" ],  
27254     [    8230, "..." ]
27255 ]; 
27256
27257     /*
27258  * - LGPL
27259  *
27260  * HtmlEditor
27261  * 
27262  */
27263
27264 /**
27265  * @class Roo.bootstrap.HtmlEditor
27266  * @extends Roo.bootstrap.TextArea
27267  * Bootstrap HtmlEditor class
27268
27269  * @constructor
27270  * Create a new HtmlEditor
27271  * @param {Object} config The config object
27272  */
27273
27274 Roo.bootstrap.HtmlEditor = function(config){
27275     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27276     if (!this.toolbars) {
27277         this.toolbars = [];
27278     }
27279     
27280     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27281     this.addEvents({
27282             /**
27283              * @event initialize
27284              * Fires when the editor is fully initialized (including the iframe)
27285              * @param {HtmlEditor} this
27286              */
27287             initialize: true,
27288             /**
27289              * @event activate
27290              * Fires when the editor is first receives the focus. Any insertion must wait
27291              * until after this event.
27292              * @param {HtmlEditor} this
27293              */
27294             activate: true,
27295              /**
27296              * @event beforesync
27297              * Fires before the textarea is updated with content from the editor iframe. Return false
27298              * to cancel the sync.
27299              * @param {HtmlEditor} this
27300              * @param {String} html
27301              */
27302             beforesync: true,
27303              /**
27304              * @event beforepush
27305              * Fires before the iframe editor is updated with content from the textarea. Return false
27306              * to cancel the push.
27307              * @param {HtmlEditor} this
27308              * @param {String} html
27309              */
27310             beforepush: true,
27311              /**
27312              * @event sync
27313              * Fires when the textarea is updated with content from the editor iframe.
27314              * @param {HtmlEditor} this
27315              * @param {String} html
27316              */
27317             sync: true,
27318              /**
27319              * @event push
27320              * Fires when the iframe editor is updated with content from the textarea.
27321              * @param {HtmlEditor} this
27322              * @param {String} html
27323              */
27324             push: true,
27325              /**
27326              * @event editmodechange
27327              * Fires when the editor switches edit modes
27328              * @param {HtmlEditor} this
27329              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27330              */
27331             editmodechange: true,
27332             /**
27333              * @event editorevent
27334              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27335              * @param {HtmlEditor} this
27336              */
27337             editorevent: true,
27338             /**
27339              * @event firstfocus
27340              * Fires when on first focus - needed by toolbars..
27341              * @param {HtmlEditor} this
27342              */
27343             firstfocus: true,
27344             /**
27345              * @event autosave
27346              * Auto save the htmlEditor value as a file into Events
27347              * @param {HtmlEditor} this
27348              */
27349             autosave: true,
27350             /**
27351              * @event savedpreview
27352              * preview the saved version of htmlEditor
27353              * @param {HtmlEditor} this
27354              */
27355             savedpreview: true
27356         });
27357 };
27358
27359
27360 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27361     
27362     
27363       /**
27364      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27365      */
27366     toolbars : false,
27367     
27368      /**
27369     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27370     */
27371     btns : [],
27372    
27373      /**
27374      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27375      *                        Roo.resizable.
27376      */
27377     resizable : false,
27378      /**
27379      * @cfg {Number} height (in pixels)
27380      */   
27381     height: 300,
27382    /**
27383      * @cfg {Number} width (in pixels)
27384      */   
27385     width: false,
27386     
27387     /**
27388      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27389      * 
27390      */
27391     stylesheets: false,
27392     
27393     // id of frame..
27394     frameId: false,
27395     
27396     // private properties
27397     validationEvent : false,
27398     deferHeight: true,
27399     initialized : false,
27400     activated : false,
27401     
27402     onFocus : Roo.emptyFn,
27403     iframePad:3,
27404     hideMode:'offsets',
27405     
27406     tbContainer : false,
27407     
27408     bodyCls : '',
27409     
27410     toolbarContainer :function() {
27411         return this.wrap.select('.x-html-editor-tb',true).first();
27412     },
27413
27414     /**
27415      * Protected method that will not generally be called directly. It
27416      * is called when the editor creates its toolbar. Override this method if you need to
27417      * add custom toolbar buttons.
27418      * @param {HtmlEditor} editor
27419      */
27420     createToolbar : function(){
27421         Roo.log('renewing');
27422         Roo.log("create toolbars");
27423         
27424         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27425         this.toolbars[0].render(this.toolbarContainer());
27426         
27427         return;
27428         
27429 //        if (!editor.toolbars || !editor.toolbars.length) {
27430 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27431 //        }
27432 //        
27433 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27434 //            editor.toolbars[i] = Roo.factory(
27435 //                    typeof(editor.toolbars[i]) == 'string' ?
27436 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27437 //                Roo.bootstrap.HtmlEditor);
27438 //            editor.toolbars[i].init(editor);
27439 //        }
27440     },
27441
27442      
27443     // private
27444     onRender : function(ct, position)
27445     {
27446        // Roo.log("Call onRender: " + this.xtype);
27447         var _t = this;
27448         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27449       
27450         this.wrap = this.inputEl().wrap({
27451             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27452         });
27453         
27454         this.editorcore.onRender(ct, position);
27455          
27456         if (this.resizable) {
27457             this.resizeEl = new Roo.Resizable(this.wrap, {
27458                 pinned : true,
27459                 wrap: true,
27460                 dynamic : true,
27461                 minHeight : this.height,
27462                 height: this.height,
27463                 handles : this.resizable,
27464                 width: this.width,
27465                 listeners : {
27466                     resize : function(r, w, h) {
27467                         _t.onResize(w,h); // -something
27468                     }
27469                 }
27470             });
27471             
27472         }
27473         this.createToolbar(this);
27474        
27475         
27476         if(!this.width && this.resizable){
27477             this.setSize(this.wrap.getSize());
27478         }
27479         if (this.resizeEl) {
27480             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27481             // should trigger onReize..
27482         }
27483         
27484     },
27485
27486     // private
27487     onResize : function(w, h)
27488     {
27489         Roo.log('resize: ' +w + ',' + h );
27490         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27491         var ew = false;
27492         var eh = false;
27493         
27494         if(this.inputEl() ){
27495             if(typeof w == 'number'){
27496                 var aw = w - this.wrap.getFrameWidth('lr');
27497                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27498                 ew = aw;
27499             }
27500             if(typeof h == 'number'){
27501                  var tbh = -11;  // fixme it needs to tool bar size!
27502                 for (var i =0; i < this.toolbars.length;i++) {
27503                     // fixme - ask toolbars for heights?
27504                     tbh += this.toolbars[i].el.getHeight();
27505                     //if (this.toolbars[i].footer) {
27506                     //    tbh += this.toolbars[i].footer.el.getHeight();
27507                     //}
27508                 }
27509               
27510                 
27511                 
27512                 
27513                 
27514                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27515                 ah -= 5; // knock a few pixes off for look..
27516                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27517                 var eh = ah;
27518             }
27519         }
27520         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27521         this.editorcore.onResize(ew,eh);
27522         
27523     },
27524
27525     /**
27526      * Toggles the editor between standard and source edit mode.
27527      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27528      */
27529     toggleSourceEdit : function(sourceEditMode)
27530     {
27531         this.editorcore.toggleSourceEdit(sourceEditMode);
27532         
27533         if(this.editorcore.sourceEditMode){
27534             Roo.log('editor - showing textarea');
27535             
27536 //            Roo.log('in');
27537 //            Roo.log(this.syncValue());
27538             this.syncValue();
27539             this.inputEl().removeClass(['hide', 'x-hidden']);
27540             this.inputEl().dom.removeAttribute('tabIndex');
27541             this.inputEl().focus();
27542         }else{
27543             Roo.log('editor - hiding textarea');
27544 //            Roo.log('out')
27545 //            Roo.log(this.pushValue()); 
27546             this.pushValue();
27547             
27548             this.inputEl().addClass(['hide', 'x-hidden']);
27549             this.inputEl().dom.setAttribute('tabIndex', -1);
27550             //this.deferFocus();
27551         }
27552          
27553         if(this.resizable){
27554             this.setSize(this.wrap.getSize());
27555         }
27556         
27557         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27558     },
27559  
27560     // private (for BoxComponent)
27561     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27562
27563     // private (for BoxComponent)
27564     getResizeEl : function(){
27565         return this.wrap;
27566     },
27567
27568     // private (for BoxComponent)
27569     getPositionEl : function(){
27570         return this.wrap;
27571     },
27572
27573     // private
27574     initEvents : function(){
27575         this.originalValue = this.getValue();
27576     },
27577
27578 //    /**
27579 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27580 //     * @method
27581 //     */
27582 //    markInvalid : Roo.emptyFn,
27583 //    /**
27584 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27585 //     * @method
27586 //     */
27587 //    clearInvalid : Roo.emptyFn,
27588
27589     setValue : function(v){
27590         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27591         this.editorcore.pushValue();
27592     },
27593
27594      
27595     // private
27596     deferFocus : function(){
27597         this.focus.defer(10, this);
27598     },
27599
27600     // doc'ed in Field
27601     focus : function(){
27602         this.editorcore.focus();
27603         
27604     },
27605       
27606
27607     // private
27608     onDestroy : function(){
27609         
27610         
27611         
27612         if(this.rendered){
27613             
27614             for (var i =0; i < this.toolbars.length;i++) {
27615                 // fixme - ask toolbars for heights?
27616                 this.toolbars[i].onDestroy();
27617             }
27618             
27619             this.wrap.dom.innerHTML = '';
27620             this.wrap.remove();
27621         }
27622     },
27623
27624     // private
27625     onFirstFocus : function(){
27626         //Roo.log("onFirstFocus");
27627         this.editorcore.onFirstFocus();
27628          for (var i =0; i < this.toolbars.length;i++) {
27629             this.toolbars[i].onFirstFocus();
27630         }
27631         
27632     },
27633     
27634     // private
27635     syncValue : function()
27636     {   
27637         this.editorcore.syncValue();
27638     },
27639     
27640     pushValue : function()
27641     {   
27642         this.editorcore.pushValue();
27643     }
27644      
27645     
27646     // hide stuff that is not compatible
27647     /**
27648      * @event blur
27649      * @hide
27650      */
27651     /**
27652      * @event change
27653      * @hide
27654      */
27655     /**
27656      * @event focus
27657      * @hide
27658      */
27659     /**
27660      * @event specialkey
27661      * @hide
27662      */
27663     /**
27664      * @cfg {String} fieldClass @hide
27665      */
27666     /**
27667      * @cfg {String} focusClass @hide
27668      */
27669     /**
27670      * @cfg {String} autoCreate @hide
27671      */
27672     /**
27673      * @cfg {String} inputType @hide
27674      */
27675      
27676     /**
27677      * @cfg {String} invalidText @hide
27678      */
27679     /**
27680      * @cfg {String} msgFx @hide
27681      */
27682     /**
27683      * @cfg {String} validateOnBlur @hide
27684      */
27685 });
27686  
27687     
27688    
27689    
27690    
27691       
27692 Roo.namespace('Roo.bootstrap.htmleditor');
27693 /**
27694  * @class Roo.bootstrap.HtmlEditorToolbar1
27695  * Basic Toolbar
27696  * 
27697  * @example
27698  * Usage:
27699  *
27700  new Roo.bootstrap.HtmlEditor({
27701     ....
27702     toolbars : [
27703         new Roo.bootstrap.HtmlEditorToolbar1({
27704             disable : { fonts: 1 , format: 1, ..., ... , ...],
27705             btns : [ .... ]
27706         })
27707     }
27708      
27709  * 
27710  * @cfg {Object} disable List of elements to disable..
27711  * @cfg {Array} btns List of additional buttons.
27712  * 
27713  * 
27714  * NEEDS Extra CSS? 
27715  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27716  */
27717  
27718 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27719 {
27720     
27721     Roo.apply(this, config);
27722     
27723     // default disabled, based on 'good practice'..
27724     this.disable = this.disable || {};
27725     Roo.applyIf(this.disable, {
27726         fontSize : true,
27727         colors : true,
27728         specialElements : true
27729     });
27730     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27731     
27732     this.editor = config.editor;
27733     this.editorcore = config.editor.editorcore;
27734     
27735     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27736     
27737     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27738     // dont call parent... till later.
27739 }
27740 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27741      
27742     bar : true,
27743     
27744     editor : false,
27745     editorcore : false,
27746     
27747     
27748     formats : [
27749         "p" ,  
27750         "h1","h2","h3","h4","h5","h6", 
27751         "pre", "code", 
27752         "abbr", "acronym", "address", "cite", "samp", "var",
27753         'div','span'
27754     ],
27755     
27756     onRender : function(ct, position)
27757     {
27758        // Roo.log("Call onRender: " + this.xtype);
27759         
27760        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27761        Roo.log(this.el);
27762        this.el.dom.style.marginBottom = '0';
27763        var _this = this;
27764        var editorcore = this.editorcore;
27765        var editor= this.editor;
27766        
27767        var children = [];
27768        var btn = function(id,cmd , toggle, handler, html){
27769        
27770             var  event = toggle ? 'toggle' : 'click';
27771        
27772             var a = {
27773                 size : 'sm',
27774                 xtype: 'Button',
27775                 xns: Roo.bootstrap,
27776                 //glyphicon : id,
27777                 fa: id,
27778                 cmd : id || cmd,
27779                 enableToggle:toggle !== false,
27780                 html : html || '',
27781                 pressed : toggle ? false : null,
27782                 listeners : {}
27783             };
27784             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27785                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27786             };
27787             children.push(a);
27788             return a;
27789        }
27790        
27791     //    var cb_box = function...
27792         
27793         var style = {
27794                 xtype: 'Button',
27795                 size : 'sm',
27796                 xns: Roo.bootstrap,
27797                 fa : 'font',
27798                 //html : 'submit'
27799                 menu : {
27800                     xtype: 'Menu',
27801                     xns: Roo.bootstrap,
27802                     items:  []
27803                 }
27804         };
27805         Roo.each(this.formats, function(f) {
27806             style.menu.items.push({
27807                 xtype :'MenuItem',
27808                 xns: Roo.bootstrap,
27809                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27810                 tagname : f,
27811                 listeners : {
27812                     click : function()
27813                     {
27814                         editorcore.insertTag(this.tagname);
27815                         editor.focus();
27816                     }
27817                 }
27818                 
27819             });
27820         });
27821         children.push(style);   
27822         
27823         btn('bold',false,true);
27824         btn('italic',false,true);
27825         btn('align-left', 'justifyleft',true);
27826         btn('align-center', 'justifycenter',true);
27827         btn('align-right' , 'justifyright',true);
27828         btn('link', false, false, function(btn) {
27829             //Roo.log("create link?");
27830             var url = prompt(this.createLinkText, this.defaultLinkValue);
27831             if(url && url != 'http:/'+'/'){
27832                 this.editorcore.relayCmd('createlink', url);
27833             }
27834         }),
27835         btn('list','insertunorderedlist',true);
27836         btn('pencil', false,true, function(btn){
27837                 Roo.log(this);
27838                 this.toggleSourceEdit(btn.pressed);
27839         });
27840         
27841         if (this.editor.btns.length > 0) {
27842             for (var i = 0; i<this.editor.btns.length; i++) {
27843                 children.push(this.editor.btns[i]);
27844             }
27845         }
27846         
27847         /*
27848         var cog = {
27849                 xtype: 'Button',
27850                 size : 'sm',
27851                 xns: Roo.bootstrap,
27852                 glyphicon : 'cog',
27853                 //html : 'submit'
27854                 menu : {
27855                     xtype: 'Menu',
27856                     xns: Roo.bootstrap,
27857                     items:  []
27858                 }
27859         };
27860         
27861         cog.menu.items.push({
27862             xtype :'MenuItem',
27863             xns: Roo.bootstrap,
27864             html : Clean styles,
27865             tagname : f,
27866             listeners : {
27867                 click : function()
27868                 {
27869                     editorcore.insertTag(this.tagname);
27870                     editor.focus();
27871                 }
27872             }
27873             
27874         });
27875        */
27876         
27877          
27878        this.xtype = 'NavSimplebar';
27879         
27880         for(var i=0;i< children.length;i++) {
27881             
27882             this.buttons.add(this.addxtypeChild(children[i]));
27883             
27884         }
27885         
27886         editor.on('editorevent', this.updateToolbar, this);
27887     },
27888     onBtnClick : function(id)
27889     {
27890        this.editorcore.relayCmd(id);
27891        this.editorcore.focus();
27892     },
27893     
27894     /**
27895      * Protected method that will not generally be called directly. It triggers
27896      * a toolbar update by reading the markup state of the current selection in the editor.
27897      */
27898     updateToolbar: function(){
27899
27900         if(!this.editorcore.activated){
27901             this.editor.onFirstFocus(); // is this neeed?
27902             return;
27903         }
27904
27905         var btns = this.buttons; 
27906         var doc = this.editorcore.doc;
27907         btns.get('bold').setActive(doc.queryCommandState('bold'));
27908         btns.get('italic').setActive(doc.queryCommandState('italic'));
27909         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27910         
27911         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27912         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27913         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27914         
27915         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27916         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27917          /*
27918         
27919         var ans = this.editorcore.getAllAncestors();
27920         if (this.formatCombo) {
27921             
27922             
27923             var store = this.formatCombo.store;
27924             this.formatCombo.setValue("");
27925             for (var i =0; i < ans.length;i++) {
27926                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27927                     // select it..
27928                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27929                     break;
27930                 }
27931             }
27932         }
27933         
27934         
27935         
27936         // hides menus... - so this cant be on a menu...
27937         Roo.bootstrap.MenuMgr.hideAll();
27938         */
27939         Roo.bootstrap.MenuMgr.hideAll();
27940         //this.editorsyncValue();
27941     },
27942     onFirstFocus: function() {
27943         this.buttons.each(function(item){
27944            item.enable();
27945         });
27946     },
27947     toggleSourceEdit : function(sourceEditMode){
27948         
27949           
27950         if(sourceEditMode){
27951             Roo.log("disabling buttons");
27952            this.buttons.each( function(item){
27953                 if(item.cmd != 'pencil'){
27954                     item.disable();
27955                 }
27956             });
27957           
27958         }else{
27959             Roo.log("enabling buttons");
27960             if(this.editorcore.initialized){
27961                 this.buttons.each( function(item){
27962                     item.enable();
27963                 });
27964             }
27965             
27966         }
27967         Roo.log("calling toggole on editor");
27968         // tell the editor that it's been pressed..
27969         this.editor.toggleSourceEdit(sourceEditMode);
27970        
27971     }
27972 });
27973
27974
27975
27976
27977  
27978 /*
27979  * - LGPL
27980  */
27981
27982 /**
27983  * @class Roo.bootstrap.Markdown
27984  * @extends Roo.bootstrap.TextArea
27985  * Bootstrap Showdown editable area
27986  * @cfg {string} content
27987  * 
27988  * @constructor
27989  * Create a new Showdown
27990  */
27991
27992 Roo.bootstrap.Markdown = function(config){
27993     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27994    
27995 };
27996
27997 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27998     
27999     editing :false,
28000     
28001     initEvents : function()
28002     {
28003         
28004         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28005         this.markdownEl = this.el.createChild({
28006             cls : 'roo-markdown-area'
28007         });
28008         this.inputEl().addClass('d-none');
28009         if (this.getValue() == '') {
28010             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28011             
28012         } else {
28013             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28014         }
28015         this.markdownEl.on('click', this.toggleTextEdit, this);
28016         this.on('blur', this.toggleTextEdit, this);
28017         this.on('specialkey', this.resizeTextArea, this);
28018     },
28019     
28020     toggleTextEdit : function()
28021     {
28022         var sh = this.markdownEl.getHeight();
28023         this.inputEl().addClass('d-none');
28024         this.markdownEl.addClass('d-none');
28025         if (!this.editing) {
28026             // show editor?
28027             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28028             this.inputEl().removeClass('d-none');
28029             this.inputEl().focus();
28030             this.editing = true;
28031             return;
28032         }
28033         // show showdown...
28034         this.updateMarkdown();
28035         this.markdownEl.removeClass('d-none');
28036         this.editing = false;
28037         return;
28038     },
28039     updateMarkdown : function()
28040     {
28041         if (this.getValue() == '') {
28042             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28043             return;
28044         }
28045  
28046         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28047     },
28048     
28049     resizeTextArea: function () {
28050         
28051         var sh = 100;
28052         Roo.log([sh, this.getValue().split("\n").length * 30]);
28053         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28054     },
28055     setValue : function(val)
28056     {
28057         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28058         if (!this.editing) {
28059             this.updateMarkdown();
28060         }
28061         
28062     },
28063     focus : function()
28064     {
28065         if (!this.editing) {
28066             this.toggleTextEdit();
28067         }
28068         
28069     }
28070
28071
28072 });/*
28073  * Based on:
28074  * Ext JS Library 1.1.1
28075  * Copyright(c) 2006-2007, Ext JS, LLC.
28076  *
28077  * Originally Released Under LGPL - original licence link has changed is not relivant.
28078  *
28079  * Fork - LGPL
28080  * <script type="text/javascript">
28081  */
28082  
28083 /**
28084  * @class Roo.bootstrap.PagingToolbar
28085  * @extends Roo.bootstrap.NavSimplebar
28086  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28087  * @constructor
28088  * Create a new PagingToolbar
28089  * @param {Object} config The config object
28090  * @param {Roo.data.Store} store
28091  */
28092 Roo.bootstrap.PagingToolbar = function(config)
28093 {
28094     // old args format still supported... - xtype is prefered..
28095         // created from xtype...
28096     
28097     this.ds = config.dataSource;
28098     
28099     if (config.store && !this.ds) {
28100         this.store= Roo.factory(config.store, Roo.data);
28101         this.ds = this.store;
28102         this.ds.xmodule = this.xmodule || false;
28103     }
28104     
28105     this.toolbarItems = [];
28106     if (config.items) {
28107         this.toolbarItems = config.items;
28108     }
28109     
28110     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28111     
28112     this.cursor = 0;
28113     
28114     if (this.ds) { 
28115         this.bind(this.ds);
28116     }
28117     
28118     if (Roo.bootstrap.version == 4) {
28119         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28120     } else {
28121         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28122     }
28123     
28124 };
28125
28126 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28127     /**
28128      * @cfg {Roo.data.Store} dataSource
28129      * The underlying data store providing the paged data
28130      */
28131     /**
28132      * @cfg {String/HTMLElement/Element} container
28133      * container The id or element that will contain the toolbar
28134      */
28135     /**
28136      * @cfg {Boolean} displayInfo
28137      * True to display the displayMsg (defaults to false)
28138      */
28139     /**
28140      * @cfg {Number} pageSize
28141      * The number of records to display per page (defaults to 20)
28142      */
28143     pageSize: 20,
28144     /**
28145      * @cfg {String} displayMsg
28146      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28147      */
28148     displayMsg : 'Displaying {0} - {1} of {2}',
28149     /**
28150      * @cfg {String} emptyMsg
28151      * The message to display when no records are found (defaults to "No data to display")
28152      */
28153     emptyMsg : 'No data to display',
28154     /**
28155      * Customizable piece of the default paging text (defaults to "Page")
28156      * @type String
28157      */
28158     beforePageText : "Page",
28159     /**
28160      * Customizable piece of the default paging text (defaults to "of %0")
28161      * @type String
28162      */
28163     afterPageText : "of {0}",
28164     /**
28165      * Customizable piece of the default paging text (defaults to "First Page")
28166      * @type String
28167      */
28168     firstText : "First Page",
28169     /**
28170      * Customizable piece of the default paging text (defaults to "Previous Page")
28171      * @type String
28172      */
28173     prevText : "Previous Page",
28174     /**
28175      * Customizable piece of the default paging text (defaults to "Next Page")
28176      * @type String
28177      */
28178     nextText : "Next Page",
28179     /**
28180      * Customizable piece of the default paging text (defaults to "Last Page")
28181      * @type String
28182      */
28183     lastText : "Last Page",
28184     /**
28185      * Customizable piece of the default paging text (defaults to "Refresh")
28186      * @type String
28187      */
28188     refreshText : "Refresh",
28189
28190     buttons : false,
28191     // private
28192     onRender : function(ct, position) 
28193     {
28194         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28195         this.navgroup.parentId = this.id;
28196         this.navgroup.onRender(this.el, null);
28197         // add the buttons to the navgroup
28198         
28199         if(this.displayInfo){
28200             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28201             this.displayEl = this.el.select('.x-paging-info', true).first();
28202 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28203 //            this.displayEl = navel.el.select('span',true).first();
28204         }
28205         
28206         var _this = this;
28207         
28208         if(this.buttons){
28209             Roo.each(_this.buttons, function(e){ // this might need to use render????
28210                Roo.factory(e).render(_this.el);
28211             });
28212         }
28213             
28214         Roo.each(_this.toolbarItems, function(e) {
28215             _this.navgroup.addItem(e);
28216         });
28217         
28218         
28219         this.first = this.navgroup.addItem({
28220             tooltip: this.firstText,
28221             cls: "prev btn-outline-secondary",
28222             html : ' <i class="fa fa-step-backward"></i>',
28223             disabled: true,
28224             preventDefault: true,
28225             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28226         });
28227         
28228         this.prev =  this.navgroup.addItem({
28229             tooltip: this.prevText,
28230             cls: "prev btn-outline-secondary",
28231             html : ' <i class="fa fa-backward"></i>',
28232             disabled: true,
28233             preventDefault: true,
28234             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28235         });
28236     //this.addSeparator();
28237         
28238         
28239         var field = this.navgroup.addItem( {
28240             tagtype : 'span',
28241             cls : 'x-paging-position  btn-outline-secondary',
28242              disabled: true,
28243             html : this.beforePageText  +
28244                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28245                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28246          } ); //?? escaped?
28247         
28248         this.field = field.el.select('input', true).first();
28249         this.field.on("keydown", this.onPagingKeydown, this);
28250         this.field.on("focus", function(){this.dom.select();});
28251     
28252     
28253         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28254         //this.field.setHeight(18);
28255         //this.addSeparator();
28256         this.next = this.navgroup.addItem({
28257             tooltip: this.nextText,
28258             cls: "next btn-outline-secondary",
28259             html : ' <i class="fa fa-forward"></i>',
28260             disabled: true,
28261             preventDefault: true,
28262             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28263         });
28264         this.last = this.navgroup.addItem({
28265             tooltip: this.lastText,
28266             html : ' <i class="fa fa-step-forward"></i>',
28267             cls: "next btn-outline-secondary",
28268             disabled: true,
28269             preventDefault: true,
28270             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28271         });
28272     //this.addSeparator();
28273         this.loading = this.navgroup.addItem({
28274             tooltip: this.refreshText,
28275             cls: "btn-outline-secondary",
28276             html : ' <i class="fa fa-refresh"></i>',
28277             preventDefault: true,
28278             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28279         });
28280         
28281     },
28282
28283     // private
28284     updateInfo : function(){
28285         if(this.displayEl){
28286             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28287             var msg = count == 0 ?
28288                 this.emptyMsg :
28289                 String.format(
28290                     this.displayMsg,
28291                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28292                 );
28293             this.displayEl.update(msg);
28294         }
28295     },
28296
28297     // private
28298     onLoad : function(ds, r, o)
28299     {
28300         this.cursor = o.params && o.params.start ? o.params.start : 0;
28301         
28302         var d = this.getPageData(),
28303             ap = d.activePage,
28304             ps = d.pages;
28305         
28306         
28307         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28308         this.field.dom.value = ap;
28309         this.first.setDisabled(ap == 1);
28310         this.prev.setDisabled(ap == 1);
28311         this.next.setDisabled(ap == ps);
28312         this.last.setDisabled(ap == ps);
28313         this.loading.enable();
28314         this.updateInfo();
28315     },
28316
28317     // private
28318     getPageData : function(){
28319         var total = this.ds.getTotalCount();
28320         return {
28321             total : total,
28322             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28323             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28324         };
28325     },
28326
28327     // private
28328     onLoadError : function(){
28329         this.loading.enable();
28330     },
28331
28332     // private
28333     onPagingKeydown : function(e){
28334         var k = e.getKey();
28335         var d = this.getPageData();
28336         if(k == e.RETURN){
28337             var v = this.field.dom.value, pageNum;
28338             if(!v || isNaN(pageNum = parseInt(v, 10))){
28339                 this.field.dom.value = d.activePage;
28340                 return;
28341             }
28342             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28343             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28344             e.stopEvent();
28345         }
28346         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))
28347         {
28348           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28349           this.field.dom.value = pageNum;
28350           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28351           e.stopEvent();
28352         }
28353         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28354         {
28355           var v = this.field.dom.value, pageNum; 
28356           var increment = (e.shiftKey) ? 10 : 1;
28357           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28358                 increment *= -1;
28359           }
28360           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28361             this.field.dom.value = d.activePage;
28362             return;
28363           }
28364           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28365           {
28366             this.field.dom.value = parseInt(v, 10) + increment;
28367             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28368             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28369           }
28370           e.stopEvent();
28371         }
28372     },
28373
28374     // private
28375     beforeLoad : function(){
28376         if(this.loading){
28377             this.loading.disable();
28378         }
28379     },
28380
28381     // private
28382     onClick : function(which){
28383         
28384         var ds = this.ds;
28385         if (!ds) {
28386             return;
28387         }
28388         
28389         switch(which){
28390             case "first":
28391                 ds.load({params:{start: 0, limit: this.pageSize}});
28392             break;
28393             case "prev":
28394                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28395             break;
28396             case "next":
28397                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28398             break;
28399             case "last":
28400                 var total = ds.getTotalCount();
28401                 var extra = total % this.pageSize;
28402                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28403                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28404             break;
28405             case "refresh":
28406                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28407             break;
28408         }
28409     },
28410
28411     /**
28412      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28413      * @param {Roo.data.Store} store The data store to unbind
28414      */
28415     unbind : function(ds){
28416         ds.un("beforeload", this.beforeLoad, this);
28417         ds.un("load", this.onLoad, this);
28418         ds.un("loadexception", this.onLoadError, this);
28419         ds.un("remove", this.updateInfo, this);
28420         ds.un("add", this.updateInfo, this);
28421         this.ds = undefined;
28422     },
28423
28424     /**
28425      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28426      * @param {Roo.data.Store} store The data store to bind
28427      */
28428     bind : function(ds){
28429         ds.on("beforeload", this.beforeLoad, this);
28430         ds.on("load", this.onLoad, this);
28431         ds.on("loadexception", this.onLoadError, this);
28432         ds.on("remove", this.updateInfo, this);
28433         ds.on("add", this.updateInfo, this);
28434         this.ds = ds;
28435     }
28436 });/*
28437  * - LGPL
28438  *
28439  * element
28440  * 
28441  */
28442
28443 /**
28444  * @class Roo.bootstrap.MessageBar
28445  * @extends Roo.bootstrap.Component
28446  * Bootstrap MessageBar class
28447  * @cfg {String} html contents of the MessageBar
28448  * @cfg {String} weight (info | success | warning | danger) default info
28449  * @cfg {String} beforeClass insert the bar before the given class
28450  * @cfg {Boolean} closable (true | false) default false
28451  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28452  * 
28453  * @constructor
28454  * Create a new Element
28455  * @param {Object} config The config object
28456  */
28457
28458 Roo.bootstrap.MessageBar = function(config){
28459     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28460 };
28461
28462 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28463     
28464     html: '',
28465     weight: 'info',
28466     closable: false,
28467     fixed: false,
28468     beforeClass: 'bootstrap-sticky-wrap',
28469     
28470     getAutoCreate : function(){
28471         
28472         var cfg = {
28473             tag: 'div',
28474             cls: 'alert alert-dismissable alert-' + this.weight,
28475             cn: [
28476                 {
28477                     tag: 'span',
28478                     cls: 'message',
28479                     html: this.html || ''
28480                 }
28481             ]
28482         };
28483         
28484         if(this.fixed){
28485             cfg.cls += ' alert-messages-fixed';
28486         }
28487         
28488         if(this.closable){
28489             cfg.cn.push({
28490                 tag: 'button',
28491                 cls: 'close',
28492                 html: 'x'
28493             });
28494         }
28495         
28496         return cfg;
28497     },
28498     
28499     onRender : function(ct, position)
28500     {
28501         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28502         
28503         if(!this.el){
28504             var cfg = Roo.apply({},  this.getAutoCreate());
28505             cfg.id = Roo.id();
28506             
28507             if (this.cls) {
28508                 cfg.cls += ' ' + this.cls;
28509             }
28510             if (this.style) {
28511                 cfg.style = this.style;
28512             }
28513             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28514             
28515             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28516         }
28517         
28518         this.el.select('>button.close').on('click', this.hide, this);
28519         
28520     },
28521     
28522     show : function()
28523     {
28524         if (!this.rendered) {
28525             this.render();
28526         }
28527         
28528         this.el.show();
28529         
28530         this.fireEvent('show', this);
28531         
28532     },
28533     
28534     hide : function()
28535     {
28536         if (!this.rendered) {
28537             this.render();
28538         }
28539         
28540         this.el.hide();
28541         
28542         this.fireEvent('hide', this);
28543     },
28544     
28545     update : function()
28546     {
28547 //        var e = this.el.dom.firstChild;
28548 //        
28549 //        if(this.closable){
28550 //            e = e.nextSibling;
28551 //        }
28552 //        
28553 //        e.data = this.html || '';
28554
28555         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28556     }
28557    
28558 });
28559
28560  
28561
28562      /*
28563  * - LGPL
28564  *
28565  * Graph
28566  * 
28567  */
28568
28569
28570 /**
28571  * @class Roo.bootstrap.Graph
28572  * @extends Roo.bootstrap.Component
28573  * Bootstrap Graph class
28574 > Prameters
28575  -sm {number} sm 4
28576  -md {number} md 5
28577  @cfg {String} graphtype  bar | vbar | pie
28578  @cfg {number} g_x coodinator | centre x (pie)
28579  @cfg {number} g_y coodinator | centre y (pie)
28580  @cfg {number} g_r radius (pie)
28581  @cfg {number} g_height height of the chart (respected by all elements in the set)
28582  @cfg {number} g_width width of the chart (respected by all elements in the set)
28583  @cfg {Object} title The title of the chart
28584     
28585  -{Array}  values
28586  -opts (object) options for the chart 
28587      o {
28588      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28589      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28590      o vgutter (number)
28591      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.
28592      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28593      o to
28594      o stretch (boolean)
28595      o }
28596  -opts (object) options for the pie
28597      o{
28598      o cut
28599      o startAngle (number)
28600      o endAngle (number)
28601      } 
28602  *
28603  * @constructor
28604  * Create a new Input
28605  * @param {Object} config The config object
28606  */
28607
28608 Roo.bootstrap.Graph = function(config){
28609     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28610     
28611     this.addEvents({
28612         // img events
28613         /**
28614          * @event click
28615          * The img click event for the img.
28616          * @param {Roo.EventObject} e
28617          */
28618         "click" : true
28619     });
28620 };
28621
28622 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28623     
28624     sm: 4,
28625     md: 5,
28626     graphtype: 'bar',
28627     g_height: 250,
28628     g_width: 400,
28629     g_x: 50,
28630     g_y: 50,
28631     g_r: 30,
28632     opts:{
28633         //g_colors: this.colors,
28634         g_type: 'soft',
28635         g_gutter: '20%'
28636
28637     },
28638     title : false,
28639
28640     getAutoCreate : function(){
28641         
28642         var cfg = {
28643             tag: 'div',
28644             html : null
28645         };
28646         
28647         
28648         return  cfg;
28649     },
28650
28651     onRender : function(ct,position){
28652         
28653         
28654         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28655         
28656         if (typeof(Raphael) == 'undefined') {
28657             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28658             return;
28659         }
28660         
28661         this.raphael = Raphael(this.el.dom);
28662         
28663                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28664                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28665                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28666                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28667                 /*
28668                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28669                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28670                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28671                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28672                 
28673                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28674                 r.barchart(330, 10, 300, 220, data1);
28675                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28676                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28677                 */
28678                 
28679                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28680                 // r.barchart(30, 30, 560, 250,  xdata, {
28681                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28682                 //     axis : "0 0 1 1",
28683                 //     axisxlabels :  xdata
28684                 //     //yvalues : cols,
28685                    
28686                 // });
28687 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28688 //        
28689 //        this.load(null,xdata,{
28690 //                axis : "0 0 1 1",
28691 //                axisxlabels :  xdata
28692 //                });
28693
28694     },
28695
28696     load : function(graphtype,xdata,opts)
28697     {
28698         this.raphael.clear();
28699         if(!graphtype) {
28700             graphtype = this.graphtype;
28701         }
28702         if(!opts){
28703             opts = this.opts;
28704         }
28705         var r = this.raphael,
28706             fin = function () {
28707                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28708             },
28709             fout = function () {
28710                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28711             },
28712             pfin = function() {
28713                 this.sector.stop();
28714                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28715
28716                 if (this.label) {
28717                     this.label[0].stop();
28718                     this.label[0].attr({ r: 7.5 });
28719                     this.label[1].attr({ "font-weight": 800 });
28720                 }
28721             },
28722             pfout = function() {
28723                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28724
28725                 if (this.label) {
28726                     this.label[0].animate({ r: 5 }, 500, "bounce");
28727                     this.label[1].attr({ "font-weight": 400 });
28728                 }
28729             };
28730
28731         switch(graphtype){
28732             case 'bar':
28733                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28734                 break;
28735             case 'hbar':
28736                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28737                 break;
28738             case 'pie':
28739 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28740 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28741 //            
28742                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28743                 
28744                 break;
28745
28746         }
28747         
28748         if(this.title){
28749             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28750         }
28751         
28752     },
28753     
28754     setTitle: function(o)
28755     {
28756         this.title = o;
28757     },
28758     
28759     initEvents: function() {
28760         
28761         if(!this.href){
28762             this.el.on('click', this.onClick, this);
28763         }
28764     },
28765     
28766     onClick : function(e)
28767     {
28768         Roo.log('img onclick');
28769         this.fireEvent('click', this, e);
28770     }
28771    
28772 });
28773
28774  
28775 /*
28776  * - LGPL
28777  *
28778  * numberBox
28779  * 
28780  */
28781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28782
28783 /**
28784  * @class Roo.bootstrap.dash.NumberBox
28785  * @extends Roo.bootstrap.Component
28786  * Bootstrap NumberBox class
28787  * @cfg {String} headline Box headline
28788  * @cfg {String} content Box content
28789  * @cfg {String} icon Box icon
28790  * @cfg {String} footer Footer text
28791  * @cfg {String} fhref Footer href
28792  * 
28793  * @constructor
28794  * Create a new NumberBox
28795  * @param {Object} config The config object
28796  */
28797
28798
28799 Roo.bootstrap.dash.NumberBox = function(config){
28800     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28801     
28802 };
28803
28804 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28805     
28806     headline : '',
28807     content : '',
28808     icon : '',
28809     footer : '',
28810     fhref : '',
28811     ficon : '',
28812     
28813     getAutoCreate : function(){
28814         
28815         var cfg = {
28816             tag : 'div',
28817             cls : 'small-box ',
28818             cn : [
28819                 {
28820                     tag : 'div',
28821                     cls : 'inner',
28822                     cn :[
28823                         {
28824                             tag : 'h3',
28825                             cls : 'roo-headline',
28826                             html : this.headline
28827                         },
28828                         {
28829                             tag : 'p',
28830                             cls : 'roo-content',
28831                             html : this.content
28832                         }
28833                     ]
28834                 }
28835             ]
28836         };
28837         
28838         if(this.icon){
28839             cfg.cn.push({
28840                 tag : 'div',
28841                 cls : 'icon',
28842                 cn :[
28843                     {
28844                         tag : 'i',
28845                         cls : 'ion ' + this.icon
28846                     }
28847                 ]
28848             });
28849         }
28850         
28851         if(this.footer){
28852             var footer = {
28853                 tag : 'a',
28854                 cls : 'small-box-footer',
28855                 href : this.fhref || '#',
28856                 html : this.footer
28857             };
28858             
28859             cfg.cn.push(footer);
28860             
28861         }
28862         
28863         return  cfg;
28864     },
28865
28866     onRender : function(ct,position){
28867         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28868
28869
28870        
28871                 
28872     },
28873
28874     setHeadline: function (value)
28875     {
28876         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28877     },
28878     
28879     setFooter: function (value, href)
28880     {
28881         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28882         
28883         if(href){
28884             this.el.select('a.small-box-footer',true).first().attr('href', href);
28885         }
28886         
28887     },
28888
28889     setContent: function (value)
28890     {
28891         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28892     },
28893
28894     initEvents: function() 
28895     {   
28896         
28897     }
28898     
28899 });
28900
28901  
28902 /*
28903  * - LGPL
28904  *
28905  * TabBox
28906  * 
28907  */
28908 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28909
28910 /**
28911  * @class Roo.bootstrap.dash.TabBox
28912  * @extends Roo.bootstrap.Component
28913  * Bootstrap TabBox class
28914  * @cfg {String} title Title of the TabBox
28915  * @cfg {String} icon Icon of the TabBox
28916  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28917  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28918  * 
28919  * @constructor
28920  * Create a new TabBox
28921  * @param {Object} config The config object
28922  */
28923
28924
28925 Roo.bootstrap.dash.TabBox = function(config){
28926     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28927     this.addEvents({
28928         // raw events
28929         /**
28930          * @event addpane
28931          * When a pane is added
28932          * @param {Roo.bootstrap.dash.TabPane} pane
28933          */
28934         "addpane" : true,
28935         /**
28936          * @event activatepane
28937          * When a pane is activated
28938          * @param {Roo.bootstrap.dash.TabPane} pane
28939          */
28940         "activatepane" : true
28941         
28942          
28943     });
28944     
28945     this.panes = [];
28946 };
28947
28948 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28949
28950     title : '',
28951     icon : false,
28952     showtabs : true,
28953     tabScrollable : false,
28954     
28955     getChildContainer : function()
28956     {
28957         return this.el.select('.tab-content', true).first();
28958     },
28959     
28960     getAutoCreate : function(){
28961         
28962         var header = {
28963             tag: 'li',
28964             cls: 'pull-left header',
28965             html: this.title,
28966             cn : []
28967         };
28968         
28969         if(this.icon){
28970             header.cn.push({
28971                 tag: 'i',
28972                 cls: 'fa ' + this.icon
28973             });
28974         }
28975         
28976         var h = {
28977             tag: 'ul',
28978             cls: 'nav nav-tabs pull-right',
28979             cn: [
28980                 header
28981             ]
28982         };
28983         
28984         if(this.tabScrollable){
28985             h = {
28986                 tag: 'div',
28987                 cls: 'tab-header',
28988                 cn: [
28989                     {
28990                         tag: 'ul',
28991                         cls: 'nav nav-tabs pull-right',
28992                         cn: [
28993                             header
28994                         ]
28995                     }
28996                 ]
28997             };
28998         }
28999         
29000         var cfg = {
29001             tag: 'div',
29002             cls: 'nav-tabs-custom',
29003             cn: [
29004                 h,
29005                 {
29006                     tag: 'div',
29007                     cls: 'tab-content no-padding',
29008                     cn: []
29009                 }
29010             ]
29011         };
29012
29013         return  cfg;
29014     },
29015     initEvents : function()
29016     {
29017         //Roo.log('add add pane handler');
29018         this.on('addpane', this.onAddPane, this);
29019     },
29020      /**
29021      * Updates the box title
29022      * @param {String} html to set the title to.
29023      */
29024     setTitle : function(value)
29025     {
29026         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29027     },
29028     onAddPane : function(pane)
29029     {
29030         this.panes.push(pane);
29031         //Roo.log('addpane');
29032         //Roo.log(pane);
29033         // tabs are rendere left to right..
29034         if(!this.showtabs){
29035             return;
29036         }
29037         
29038         var ctr = this.el.select('.nav-tabs', true).first();
29039          
29040          
29041         var existing = ctr.select('.nav-tab',true);
29042         var qty = existing.getCount();;
29043         
29044         
29045         var tab = ctr.createChild({
29046             tag : 'li',
29047             cls : 'nav-tab' + (qty ? '' : ' active'),
29048             cn : [
29049                 {
29050                     tag : 'a',
29051                     href:'#',
29052                     html : pane.title
29053                 }
29054             ]
29055         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29056         pane.tab = tab;
29057         
29058         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29059         if (!qty) {
29060             pane.el.addClass('active');
29061         }
29062         
29063                 
29064     },
29065     onTabClick : function(ev,un,ob,pane)
29066     {
29067         //Roo.log('tab - prev default');
29068         ev.preventDefault();
29069         
29070         
29071         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29072         pane.tab.addClass('active');
29073         //Roo.log(pane.title);
29074         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29075         // technically we should have a deactivate event.. but maybe add later.
29076         // and it should not de-activate the selected tab...
29077         this.fireEvent('activatepane', pane);
29078         pane.el.addClass('active');
29079         pane.fireEvent('activate');
29080         
29081         
29082     },
29083     
29084     getActivePane : function()
29085     {
29086         var r = false;
29087         Roo.each(this.panes, function(p) {
29088             if(p.el.hasClass('active')){
29089                 r = p;
29090                 return false;
29091             }
29092             
29093             return;
29094         });
29095         
29096         return r;
29097     }
29098     
29099     
29100 });
29101
29102  
29103 /*
29104  * - LGPL
29105  *
29106  * Tab pane
29107  * 
29108  */
29109 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29110 /**
29111  * @class Roo.bootstrap.TabPane
29112  * @extends Roo.bootstrap.Component
29113  * Bootstrap TabPane class
29114  * @cfg {Boolean} active (false | true) Default false
29115  * @cfg {String} title title of panel
29116
29117  * 
29118  * @constructor
29119  * Create a new TabPane
29120  * @param {Object} config The config object
29121  */
29122
29123 Roo.bootstrap.dash.TabPane = function(config){
29124     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29125     
29126     this.addEvents({
29127         // raw events
29128         /**
29129          * @event activate
29130          * When a pane is activated
29131          * @param {Roo.bootstrap.dash.TabPane} pane
29132          */
29133         "activate" : true
29134          
29135     });
29136 };
29137
29138 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29139     
29140     active : false,
29141     title : '',
29142     
29143     // the tabBox that this is attached to.
29144     tab : false,
29145      
29146     getAutoCreate : function() 
29147     {
29148         var cfg = {
29149             tag: 'div',
29150             cls: 'tab-pane'
29151         };
29152         
29153         if(this.active){
29154             cfg.cls += ' active';
29155         }
29156         
29157         return cfg;
29158     },
29159     initEvents  : function()
29160     {
29161         //Roo.log('trigger add pane handler');
29162         this.parent().fireEvent('addpane', this)
29163     },
29164     
29165      /**
29166      * Updates the tab title 
29167      * @param {String} html to set the title to.
29168      */
29169     setTitle: function(str)
29170     {
29171         if (!this.tab) {
29172             return;
29173         }
29174         this.title = str;
29175         this.tab.select('a', true).first().dom.innerHTML = str;
29176         
29177     }
29178     
29179     
29180     
29181 });
29182
29183  
29184
29185
29186  /*
29187  * - LGPL
29188  *
29189  * menu
29190  * 
29191  */
29192 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29193
29194 /**
29195  * @class Roo.bootstrap.menu.Menu
29196  * @extends Roo.bootstrap.Component
29197  * Bootstrap Menu class - container for Menu
29198  * @cfg {String} html Text of the menu
29199  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29200  * @cfg {String} icon Font awesome icon
29201  * @cfg {String} pos Menu align to (top | bottom) default bottom
29202  * 
29203  * 
29204  * @constructor
29205  * Create a new Menu
29206  * @param {Object} config The config object
29207  */
29208
29209
29210 Roo.bootstrap.menu.Menu = function(config){
29211     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29212     
29213     this.addEvents({
29214         /**
29215          * @event beforeshow
29216          * Fires before this menu is displayed
29217          * @param {Roo.bootstrap.menu.Menu} this
29218          */
29219         beforeshow : true,
29220         /**
29221          * @event beforehide
29222          * Fires before this menu is hidden
29223          * @param {Roo.bootstrap.menu.Menu} this
29224          */
29225         beforehide : true,
29226         /**
29227          * @event show
29228          * Fires after this menu is displayed
29229          * @param {Roo.bootstrap.menu.Menu} this
29230          */
29231         show : true,
29232         /**
29233          * @event hide
29234          * Fires after this menu is hidden
29235          * @param {Roo.bootstrap.menu.Menu} this
29236          */
29237         hide : true,
29238         /**
29239          * @event click
29240          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29241          * @param {Roo.bootstrap.menu.Menu} this
29242          * @param {Roo.EventObject} e
29243          */
29244         click : true
29245     });
29246     
29247 };
29248
29249 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29250     
29251     submenu : false,
29252     html : '',
29253     weight : 'default',
29254     icon : false,
29255     pos : 'bottom',
29256     
29257     
29258     getChildContainer : function() {
29259         if(this.isSubMenu){
29260             return this.el;
29261         }
29262         
29263         return this.el.select('ul.dropdown-menu', true).first();  
29264     },
29265     
29266     getAutoCreate : function()
29267     {
29268         var text = [
29269             {
29270                 tag : 'span',
29271                 cls : 'roo-menu-text',
29272                 html : this.html
29273             }
29274         ];
29275         
29276         if(this.icon){
29277             text.unshift({
29278                 tag : 'i',
29279                 cls : 'fa ' + this.icon
29280             })
29281         }
29282         
29283         
29284         var cfg = {
29285             tag : 'div',
29286             cls : 'btn-group',
29287             cn : [
29288                 {
29289                     tag : 'button',
29290                     cls : 'dropdown-button btn btn-' + this.weight,
29291                     cn : text
29292                 },
29293                 {
29294                     tag : 'button',
29295                     cls : 'dropdown-toggle btn btn-' + this.weight,
29296                     cn : [
29297                         {
29298                             tag : 'span',
29299                             cls : 'caret'
29300                         }
29301                     ]
29302                 },
29303                 {
29304                     tag : 'ul',
29305                     cls : 'dropdown-menu'
29306                 }
29307             ]
29308             
29309         };
29310         
29311         if(this.pos == 'top'){
29312             cfg.cls += ' dropup';
29313         }
29314         
29315         if(this.isSubMenu){
29316             cfg = {
29317                 tag : 'ul',
29318                 cls : 'dropdown-menu'
29319             }
29320         }
29321         
29322         return cfg;
29323     },
29324     
29325     onRender : function(ct, position)
29326     {
29327         this.isSubMenu = ct.hasClass('dropdown-submenu');
29328         
29329         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29330     },
29331     
29332     initEvents : function() 
29333     {
29334         if(this.isSubMenu){
29335             return;
29336         }
29337         
29338         this.hidden = true;
29339         
29340         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29341         this.triggerEl.on('click', this.onTriggerPress, this);
29342         
29343         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29344         this.buttonEl.on('click', this.onClick, this);
29345         
29346     },
29347     
29348     list : function()
29349     {
29350         if(this.isSubMenu){
29351             return this.el;
29352         }
29353         
29354         return this.el.select('ul.dropdown-menu', true).first();
29355     },
29356     
29357     onClick : function(e)
29358     {
29359         this.fireEvent("click", this, e);
29360     },
29361     
29362     onTriggerPress  : function(e)
29363     {   
29364         if (this.isVisible()) {
29365             this.hide();
29366         } else {
29367             this.show();
29368         }
29369     },
29370     
29371     isVisible : function(){
29372         return !this.hidden;
29373     },
29374     
29375     show : function()
29376     {
29377         this.fireEvent("beforeshow", this);
29378         
29379         this.hidden = false;
29380         this.el.addClass('open');
29381         
29382         Roo.get(document).on("mouseup", this.onMouseUp, this);
29383         
29384         this.fireEvent("show", this);
29385         
29386         
29387     },
29388     
29389     hide : function()
29390     {
29391         this.fireEvent("beforehide", this);
29392         
29393         this.hidden = true;
29394         this.el.removeClass('open');
29395         
29396         Roo.get(document).un("mouseup", this.onMouseUp);
29397         
29398         this.fireEvent("hide", this);
29399     },
29400     
29401     onMouseUp : function()
29402     {
29403         this.hide();
29404     }
29405     
29406 });
29407
29408  
29409  /*
29410  * - LGPL
29411  *
29412  * menu item
29413  * 
29414  */
29415 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29416
29417 /**
29418  * @class Roo.bootstrap.menu.Item
29419  * @extends Roo.bootstrap.Component
29420  * Bootstrap MenuItem class
29421  * @cfg {Boolean} submenu (true | false) default false
29422  * @cfg {String} html text of the item
29423  * @cfg {String} href the link
29424  * @cfg {Boolean} disable (true | false) default false
29425  * @cfg {Boolean} preventDefault (true | false) default true
29426  * @cfg {String} icon Font awesome icon
29427  * @cfg {String} pos Submenu align to (left | right) default right 
29428  * 
29429  * 
29430  * @constructor
29431  * Create a new Item
29432  * @param {Object} config The config object
29433  */
29434
29435
29436 Roo.bootstrap.menu.Item = function(config){
29437     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29438     this.addEvents({
29439         /**
29440          * @event mouseover
29441          * Fires when the mouse is hovering over this menu
29442          * @param {Roo.bootstrap.menu.Item} this
29443          * @param {Roo.EventObject} e
29444          */
29445         mouseover : true,
29446         /**
29447          * @event mouseout
29448          * Fires when the mouse exits this menu
29449          * @param {Roo.bootstrap.menu.Item} this
29450          * @param {Roo.EventObject} e
29451          */
29452         mouseout : true,
29453         // raw events
29454         /**
29455          * @event click
29456          * The raw click event for the entire grid.
29457          * @param {Roo.EventObject} e
29458          */
29459         click : true
29460     });
29461 };
29462
29463 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29464     
29465     submenu : false,
29466     href : '',
29467     html : '',
29468     preventDefault: true,
29469     disable : false,
29470     icon : false,
29471     pos : 'right',
29472     
29473     getAutoCreate : function()
29474     {
29475         var text = [
29476             {
29477                 tag : 'span',
29478                 cls : 'roo-menu-item-text',
29479                 html : this.html
29480             }
29481         ];
29482         
29483         if(this.icon){
29484             text.unshift({
29485                 tag : 'i',
29486                 cls : 'fa ' + this.icon
29487             })
29488         }
29489         
29490         var cfg = {
29491             tag : 'li',
29492             cn : [
29493                 {
29494                     tag : 'a',
29495                     href : this.href || '#',
29496                     cn : text
29497                 }
29498             ]
29499         };
29500         
29501         if(this.disable){
29502             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29503         }
29504         
29505         if(this.submenu){
29506             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29507             
29508             if(this.pos == 'left'){
29509                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29510             }
29511         }
29512         
29513         return cfg;
29514     },
29515     
29516     initEvents : function() 
29517     {
29518         this.el.on('mouseover', this.onMouseOver, this);
29519         this.el.on('mouseout', this.onMouseOut, this);
29520         
29521         this.el.select('a', true).first().on('click', this.onClick, this);
29522         
29523     },
29524     
29525     onClick : function(e)
29526     {
29527         if(this.preventDefault){
29528             e.preventDefault();
29529         }
29530         
29531         this.fireEvent("click", this, e);
29532     },
29533     
29534     onMouseOver : function(e)
29535     {
29536         if(this.submenu && this.pos == 'left'){
29537             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29538         }
29539         
29540         this.fireEvent("mouseover", this, e);
29541     },
29542     
29543     onMouseOut : function(e)
29544     {
29545         this.fireEvent("mouseout", this, e);
29546     }
29547 });
29548
29549  
29550
29551  /*
29552  * - LGPL
29553  *
29554  * menu separator
29555  * 
29556  */
29557 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29558
29559 /**
29560  * @class Roo.bootstrap.menu.Separator
29561  * @extends Roo.bootstrap.Component
29562  * Bootstrap Separator class
29563  * 
29564  * @constructor
29565  * Create a new Separator
29566  * @param {Object} config The config object
29567  */
29568
29569
29570 Roo.bootstrap.menu.Separator = function(config){
29571     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29572 };
29573
29574 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29575     
29576     getAutoCreate : function(){
29577         var cfg = {
29578             tag : 'li',
29579             cls: 'dropdown-divider divider'
29580         };
29581         
29582         return cfg;
29583     }
29584    
29585 });
29586
29587  
29588
29589  /*
29590  * - LGPL
29591  *
29592  * Tooltip
29593  * 
29594  */
29595
29596 /**
29597  * @class Roo.bootstrap.Tooltip
29598  * Bootstrap Tooltip class
29599  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29600  * to determine which dom element triggers the tooltip.
29601  * 
29602  * It needs to add support for additional attributes like tooltip-position
29603  * 
29604  * @constructor
29605  * Create a new Toolti
29606  * @param {Object} config The config object
29607  */
29608
29609 Roo.bootstrap.Tooltip = function(config){
29610     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29611     
29612     this.alignment = Roo.bootstrap.Tooltip.alignment;
29613     
29614     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29615         this.alignment = config.alignment;
29616     }
29617     
29618 };
29619
29620 Roo.apply(Roo.bootstrap.Tooltip, {
29621     /**
29622      * @function init initialize tooltip monitoring.
29623      * @static
29624      */
29625     currentEl : false,
29626     currentTip : false,
29627     currentRegion : false,
29628     
29629     //  init : delay?
29630     
29631     init : function()
29632     {
29633         Roo.get(document).on('mouseover', this.enter ,this);
29634         Roo.get(document).on('mouseout', this.leave, this);
29635          
29636         
29637         this.currentTip = new Roo.bootstrap.Tooltip();
29638     },
29639     
29640     enter : function(ev)
29641     {
29642         var dom = ev.getTarget();
29643         
29644         //Roo.log(['enter',dom]);
29645         var el = Roo.fly(dom);
29646         if (this.currentEl) {
29647             //Roo.log(dom);
29648             //Roo.log(this.currentEl);
29649             //Roo.log(this.currentEl.contains(dom));
29650             if (this.currentEl == el) {
29651                 return;
29652             }
29653             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29654                 return;
29655             }
29656
29657         }
29658         
29659         if (this.currentTip.el) {
29660             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29661         }    
29662         //Roo.log(ev);
29663         
29664         if(!el || el.dom == document){
29665             return;
29666         }
29667         
29668         var bindEl = el; 
29669         var pel = false;
29670         if (!el.attr('tooltip')) {
29671             pel = el.findParent("[tooltip]");
29672             if (pel) {
29673                 bindEl = Roo.get(pel);
29674             }
29675         }
29676         
29677        
29678         
29679         // you can not look for children, as if el is the body.. then everythign is the child..
29680         if (!pel && !el.attr('tooltip')) { //
29681             if (!el.select("[tooltip]").elements.length) {
29682                 return;
29683             }
29684             // is the mouse over this child...?
29685             bindEl = el.select("[tooltip]").first();
29686             var xy = ev.getXY();
29687             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29688                 //Roo.log("not in region.");
29689                 return;
29690             }
29691             //Roo.log("child element over..");
29692             
29693         }
29694         this.currentEl = el;
29695         this.currentTip.bind(bindEl);
29696         this.currentRegion = Roo.lib.Region.getRegion(dom);
29697         this.currentTip.enter();
29698         
29699     },
29700     leave : function(ev)
29701     {
29702         var dom = ev.getTarget();
29703         //Roo.log(['leave',dom]);
29704         if (!this.currentEl) {
29705             return;
29706         }
29707         
29708         
29709         if (dom != this.currentEl.dom) {
29710             return;
29711         }
29712         var xy = ev.getXY();
29713         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29714             return;
29715         }
29716         // only activate leave if mouse cursor is outside... bounding box..
29717         
29718         
29719         
29720         
29721         if (this.currentTip) {
29722             this.currentTip.leave();
29723         }
29724         //Roo.log('clear currentEl');
29725         this.currentEl = false;
29726         
29727         
29728     },
29729     alignment : {
29730         'left' : ['r-l', [-2,0], 'right'],
29731         'right' : ['l-r', [2,0], 'left'],
29732         'bottom' : ['t-b', [0,2], 'top'],
29733         'top' : [ 'b-t', [0,-2], 'bottom']
29734     }
29735     
29736 });
29737
29738
29739 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29740     
29741     
29742     bindEl : false,
29743     
29744     delay : null, // can be { show : 300 , hide: 500}
29745     
29746     timeout : null,
29747     
29748     hoverState : null, //???
29749     
29750     placement : 'bottom', 
29751     
29752     alignment : false,
29753     
29754     getAutoCreate : function(){
29755     
29756         var cfg = {
29757            cls : 'tooltip',   
29758            role : 'tooltip',
29759            cn : [
29760                 {
29761                     cls : 'tooltip-arrow arrow'
29762                 },
29763                 {
29764                     cls : 'tooltip-inner'
29765                 }
29766            ]
29767         };
29768         
29769         return cfg;
29770     },
29771     bind : function(el)
29772     {
29773         this.bindEl = el;
29774     },
29775     
29776     initEvents : function()
29777     {
29778         this.arrowEl = this.el.select('.arrow', true).first();
29779         this.innerEl = this.el.select('.tooltip-inner', true).first();
29780     },
29781     
29782     enter : function () {
29783        
29784         if (this.timeout != null) {
29785             clearTimeout(this.timeout);
29786         }
29787         
29788         this.hoverState = 'in';
29789          //Roo.log("enter - show");
29790         if (!this.delay || !this.delay.show) {
29791             this.show();
29792             return;
29793         }
29794         var _t = this;
29795         this.timeout = setTimeout(function () {
29796             if (_t.hoverState == 'in') {
29797                 _t.show();
29798             }
29799         }, this.delay.show);
29800     },
29801     leave : function()
29802     {
29803         clearTimeout(this.timeout);
29804     
29805         this.hoverState = 'out';
29806          if (!this.delay || !this.delay.hide) {
29807             this.hide();
29808             return;
29809         }
29810        
29811         var _t = this;
29812         this.timeout = setTimeout(function () {
29813             //Roo.log("leave - timeout");
29814             
29815             if (_t.hoverState == 'out') {
29816                 _t.hide();
29817                 Roo.bootstrap.Tooltip.currentEl = false;
29818             }
29819         }, delay);
29820     },
29821     
29822     show : function (msg)
29823     {
29824         if (!this.el) {
29825             this.render(document.body);
29826         }
29827         // set content.
29828         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29829         
29830         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29831         
29832         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29833         
29834         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29835                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29836         
29837         var placement = typeof this.placement == 'function' ?
29838             this.placement.call(this, this.el, on_el) :
29839             this.placement;
29840             
29841         var autoToken = /\s?auto?\s?/i;
29842         var autoPlace = autoToken.test(placement);
29843         if (autoPlace) {
29844             placement = placement.replace(autoToken, '') || 'top';
29845         }
29846         
29847         //this.el.detach()
29848         //this.el.setXY([0,0]);
29849         this.el.show();
29850         //this.el.dom.style.display='block';
29851         
29852         //this.el.appendTo(on_el);
29853         
29854         var p = this.getPosition();
29855         var box = this.el.getBox();
29856         
29857         if (autoPlace) {
29858             // fixme..
29859         }
29860         
29861         var align = this.alignment[placement];
29862         
29863         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29864         
29865         if(placement == 'top' || placement == 'bottom'){
29866             if(xy[0] < 0){
29867                 placement = 'right';
29868             }
29869             
29870             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29871                 placement = 'left';
29872             }
29873             
29874             var scroll = Roo.select('body', true).first().getScroll();
29875             
29876             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29877                 placement = 'top';
29878             }
29879             
29880             align = this.alignment[placement];
29881             
29882             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29883             
29884         }
29885         
29886         var elems = document.getElementsByTagName('div');
29887         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29888         for (var i = 0; i < elems.length; i++) {
29889           var zindex = Number.parseInt(
29890                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29891                 10
29892           );
29893           if (zindex > highest) {
29894             highest = zindex;
29895           }
29896         }
29897         
29898         
29899         
29900         this.el.dom.style.zIndex = highest;
29901         
29902         this.el.alignTo(this.bindEl, align[0],align[1]);
29903         //var arrow = this.el.select('.arrow',true).first();
29904         //arrow.set(align[2], 
29905         
29906         this.el.addClass(placement);
29907         this.el.addClass("bs-tooltip-"+ placement);
29908         
29909         this.el.addClass('in fade show');
29910         
29911         this.hoverState = null;
29912         
29913         if (this.el.hasClass('fade')) {
29914             // fade it?
29915         }
29916         
29917         
29918         
29919         
29920         
29921     },
29922     hide : function()
29923     {
29924          
29925         if (!this.el) {
29926             return;
29927         }
29928         //this.el.setXY([0,0]);
29929         this.el.removeClass(['show', 'in']);
29930         //this.el.hide();
29931         
29932     }
29933     
29934 });
29935  
29936
29937  /*
29938  * - LGPL
29939  *
29940  * Location Picker
29941  * 
29942  */
29943
29944 /**
29945  * @class Roo.bootstrap.LocationPicker
29946  * @extends Roo.bootstrap.Component
29947  * Bootstrap LocationPicker class
29948  * @cfg {Number} latitude Position when init default 0
29949  * @cfg {Number} longitude Position when init default 0
29950  * @cfg {Number} zoom default 15
29951  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29952  * @cfg {Boolean} mapTypeControl default false
29953  * @cfg {Boolean} disableDoubleClickZoom default false
29954  * @cfg {Boolean} scrollwheel default true
29955  * @cfg {Boolean} streetViewControl default false
29956  * @cfg {Number} radius default 0
29957  * @cfg {String} locationName
29958  * @cfg {Boolean} draggable default true
29959  * @cfg {Boolean} enableAutocomplete default false
29960  * @cfg {Boolean} enableReverseGeocode default true
29961  * @cfg {String} markerTitle
29962  * 
29963  * @constructor
29964  * Create a new LocationPicker
29965  * @param {Object} config The config object
29966  */
29967
29968
29969 Roo.bootstrap.LocationPicker = function(config){
29970     
29971     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29972     
29973     this.addEvents({
29974         /**
29975          * @event initial
29976          * Fires when the picker initialized.
29977          * @param {Roo.bootstrap.LocationPicker} this
29978          * @param {Google Location} location
29979          */
29980         initial : true,
29981         /**
29982          * @event positionchanged
29983          * Fires when the picker position changed.
29984          * @param {Roo.bootstrap.LocationPicker} this
29985          * @param {Google Location} location
29986          */
29987         positionchanged : true,
29988         /**
29989          * @event resize
29990          * Fires when the map resize.
29991          * @param {Roo.bootstrap.LocationPicker} this
29992          */
29993         resize : true,
29994         /**
29995          * @event show
29996          * Fires when the map show.
29997          * @param {Roo.bootstrap.LocationPicker} this
29998          */
29999         show : true,
30000         /**
30001          * @event hide
30002          * Fires when the map hide.
30003          * @param {Roo.bootstrap.LocationPicker} this
30004          */
30005         hide : true,
30006         /**
30007          * @event mapClick
30008          * Fires when click the map.
30009          * @param {Roo.bootstrap.LocationPicker} this
30010          * @param {Map event} e
30011          */
30012         mapClick : true,
30013         /**
30014          * @event mapRightClick
30015          * Fires when right click the map.
30016          * @param {Roo.bootstrap.LocationPicker} this
30017          * @param {Map event} e
30018          */
30019         mapRightClick : true,
30020         /**
30021          * @event markerClick
30022          * Fires when click the marker.
30023          * @param {Roo.bootstrap.LocationPicker} this
30024          * @param {Map event} e
30025          */
30026         markerClick : true,
30027         /**
30028          * @event markerRightClick
30029          * Fires when right click the marker.
30030          * @param {Roo.bootstrap.LocationPicker} this
30031          * @param {Map event} e
30032          */
30033         markerRightClick : true,
30034         /**
30035          * @event OverlayViewDraw
30036          * Fires when OverlayView Draw
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          */
30039         OverlayViewDraw : true,
30040         /**
30041          * @event OverlayViewOnAdd
30042          * Fires when OverlayView Draw
30043          * @param {Roo.bootstrap.LocationPicker} this
30044          */
30045         OverlayViewOnAdd : true,
30046         /**
30047          * @event OverlayViewOnRemove
30048          * Fires when OverlayView Draw
30049          * @param {Roo.bootstrap.LocationPicker} this
30050          */
30051         OverlayViewOnRemove : true,
30052         /**
30053          * @event OverlayViewShow
30054          * Fires when OverlayView Draw
30055          * @param {Roo.bootstrap.LocationPicker} this
30056          * @param {Pixel} cpx
30057          */
30058         OverlayViewShow : true,
30059         /**
30060          * @event OverlayViewHide
30061          * Fires when OverlayView Draw
30062          * @param {Roo.bootstrap.LocationPicker} this
30063          */
30064         OverlayViewHide : true,
30065         /**
30066          * @event loadexception
30067          * Fires when load google lib failed.
30068          * @param {Roo.bootstrap.LocationPicker} this
30069          */
30070         loadexception : true
30071     });
30072         
30073 };
30074
30075 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30076     
30077     gMapContext: false,
30078     
30079     latitude: 0,
30080     longitude: 0,
30081     zoom: 15,
30082     mapTypeId: false,
30083     mapTypeControl: false,
30084     disableDoubleClickZoom: false,
30085     scrollwheel: true,
30086     streetViewControl: false,
30087     radius: 0,
30088     locationName: '',
30089     draggable: true,
30090     enableAutocomplete: false,
30091     enableReverseGeocode: true,
30092     markerTitle: '',
30093     
30094     getAutoCreate: function()
30095     {
30096
30097         var cfg = {
30098             tag: 'div',
30099             cls: 'roo-location-picker'
30100         };
30101         
30102         return cfg
30103     },
30104     
30105     initEvents: function(ct, position)
30106     {       
30107         if(!this.el.getWidth() || this.isApplied()){
30108             return;
30109         }
30110         
30111         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30112         
30113         this.initial();
30114     },
30115     
30116     initial: function()
30117     {
30118         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30119             this.fireEvent('loadexception', this);
30120             return;
30121         }
30122         
30123         if(!this.mapTypeId){
30124             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30125         }
30126         
30127         this.gMapContext = this.GMapContext();
30128         
30129         this.initOverlayView();
30130         
30131         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30132         
30133         var _this = this;
30134                 
30135         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30136             _this.setPosition(_this.gMapContext.marker.position);
30137         });
30138         
30139         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30140             _this.fireEvent('mapClick', this, event);
30141             
30142         });
30143
30144         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30145             _this.fireEvent('mapRightClick', this, event);
30146             
30147         });
30148         
30149         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30150             _this.fireEvent('markerClick', this, event);
30151             
30152         });
30153
30154         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30155             _this.fireEvent('markerRightClick', this, event);
30156             
30157         });
30158         
30159         this.setPosition(this.gMapContext.location);
30160         
30161         this.fireEvent('initial', this, this.gMapContext.location);
30162     },
30163     
30164     initOverlayView: function()
30165     {
30166         var _this = this;
30167         
30168         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30169             
30170             draw: function()
30171             {
30172                 _this.fireEvent('OverlayViewDraw', _this);
30173             },
30174             
30175             onAdd: function()
30176             {
30177                 _this.fireEvent('OverlayViewOnAdd', _this);
30178             },
30179             
30180             onRemove: function()
30181             {
30182                 _this.fireEvent('OverlayViewOnRemove', _this);
30183             },
30184             
30185             show: function(cpx)
30186             {
30187                 _this.fireEvent('OverlayViewShow', _this, cpx);
30188             },
30189             
30190             hide: function()
30191             {
30192                 _this.fireEvent('OverlayViewHide', _this);
30193             }
30194             
30195         });
30196     },
30197     
30198     fromLatLngToContainerPixel: function(event)
30199     {
30200         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30201     },
30202     
30203     isApplied: function() 
30204     {
30205         return this.getGmapContext() == false ? false : true;
30206     },
30207     
30208     getGmapContext: function() 
30209     {
30210         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30211     },
30212     
30213     GMapContext: function() 
30214     {
30215         var position = new google.maps.LatLng(this.latitude, this.longitude);
30216         
30217         var _map = new google.maps.Map(this.el.dom, {
30218             center: position,
30219             zoom: this.zoom,
30220             mapTypeId: this.mapTypeId,
30221             mapTypeControl: this.mapTypeControl,
30222             disableDoubleClickZoom: this.disableDoubleClickZoom,
30223             scrollwheel: this.scrollwheel,
30224             streetViewControl: this.streetViewControl,
30225             locationName: this.locationName,
30226             draggable: this.draggable,
30227             enableAutocomplete: this.enableAutocomplete,
30228             enableReverseGeocode: this.enableReverseGeocode
30229         });
30230         
30231         var _marker = new google.maps.Marker({
30232             position: position,
30233             map: _map,
30234             title: this.markerTitle,
30235             draggable: this.draggable
30236         });
30237         
30238         return {
30239             map: _map,
30240             marker: _marker,
30241             circle: null,
30242             location: position,
30243             radius: this.radius,
30244             locationName: this.locationName,
30245             addressComponents: {
30246                 formatted_address: null,
30247                 addressLine1: null,
30248                 addressLine2: null,
30249                 streetName: null,
30250                 streetNumber: null,
30251                 city: null,
30252                 district: null,
30253                 state: null,
30254                 stateOrProvince: null
30255             },
30256             settings: this,
30257             domContainer: this.el.dom,
30258             geodecoder: new google.maps.Geocoder()
30259         };
30260     },
30261     
30262     drawCircle: function(center, radius, options) 
30263     {
30264         if (this.gMapContext.circle != null) {
30265             this.gMapContext.circle.setMap(null);
30266         }
30267         if (radius > 0) {
30268             radius *= 1;
30269             options = Roo.apply({}, options, {
30270                 strokeColor: "#0000FF",
30271                 strokeOpacity: .35,
30272                 strokeWeight: 2,
30273                 fillColor: "#0000FF",
30274                 fillOpacity: .2
30275             });
30276             
30277             options.map = this.gMapContext.map;
30278             options.radius = radius;
30279             options.center = center;
30280             this.gMapContext.circle = new google.maps.Circle(options);
30281             return this.gMapContext.circle;
30282         }
30283         
30284         return null;
30285     },
30286     
30287     setPosition: function(location) 
30288     {
30289         this.gMapContext.location = location;
30290         this.gMapContext.marker.setPosition(location);
30291         this.gMapContext.map.panTo(location);
30292         this.drawCircle(location, this.gMapContext.radius, {});
30293         
30294         var _this = this;
30295         
30296         if (this.gMapContext.settings.enableReverseGeocode) {
30297             this.gMapContext.geodecoder.geocode({
30298                 latLng: this.gMapContext.location
30299             }, function(results, status) {
30300                 
30301                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30302                     _this.gMapContext.locationName = results[0].formatted_address;
30303                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30304                     
30305                     _this.fireEvent('positionchanged', this, location);
30306                 }
30307             });
30308             
30309             return;
30310         }
30311         
30312         this.fireEvent('positionchanged', this, location);
30313     },
30314     
30315     resize: function()
30316     {
30317         google.maps.event.trigger(this.gMapContext.map, "resize");
30318         
30319         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30320         
30321         this.fireEvent('resize', this);
30322     },
30323     
30324     setPositionByLatLng: function(latitude, longitude)
30325     {
30326         this.setPosition(new google.maps.LatLng(latitude, longitude));
30327     },
30328     
30329     getCurrentPosition: function() 
30330     {
30331         return {
30332             latitude: this.gMapContext.location.lat(),
30333             longitude: this.gMapContext.location.lng()
30334         };
30335     },
30336     
30337     getAddressName: function() 
30338     {
30339         return this.gMapContext.locationName;
30340     },
30341     
30342     getAddressComponents: function() 
30343     {
30344         return this.gMapContext.addressComponents;
30345     },
30346     
30347     address_component_from_google_geocode: function(address_components) 
30348     {
30349         var result = {};
30350         
30351         for (var i = 0; i < address_components.length; i++) {
30352             var component = address_components[i];
30353             if (component.types.indexOf("postal_code") >= 0) {
30354                 result.postalCode = component.short_name;
30355             } else if (component.types.indexOf("street_number") >= 0) {
30356                 result.streetNumber = component.short_name;
30357             } else if (component.types.indexOf("route") >= 0) {
30358                 result.streetName = component.short_name;
30359             } else if (component.types.indexOf("neighborhood") >= 0) {
30360                 result.city = component.short_name;
30361             } else if (component.types.indexOf("locality") >= 0) {
30362                 result.city = component.short_name;
30363             } else if (component.types.indexOf("sublocality") >= 0) {
30364                 result.district = component.short_name;
30365             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30366                 result.stateOrProvince = component.short_name;
30367             } else if (component.types.indexOf("country") >= 0) {
30368                 result.country = component.short_name;
30369             }
30370         }
30371         
30372         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30373         result.addressLine2 = "";
30374         return result;
30375     },
30376     
30377     setZoomLevel: function(zoom)
30378     {
30379         this.gMapContext.map.setZoom(zoom);
30380     },
30381     
30382     show: function()
30383     {
30384         if(!this.el){
30385             return;
30386         }
30387         
30388         this.el.show();
30389         
30390         this.resize();
30391         
30392         this.fireEvent('show', this);
30393     },
30394     
30395     hide: function()
30396     {
30397         if(!this.el){
30398             return;
30399         }
30400         
30401         this.el.hide();
30402         
30403         this.fireEvent('hide', this);
30404     }
30405     
30406 });
30407
30408 Roo.apply(Roo.bootstrap.LocationPicker, {
30409     
30410     OverlayView : function(map, options)
30411     {
30412         options = options || {};
30413         
30414         this.setMap(map);
30415     }
30416     
30417     
30418 });/**
30419  * @class Roo.bootstrap.Alert
30420  * @extends Roo.bootstrap.Component
30421  * Bootstrap Alert class - shows an alert area box
30422  * eg
30423  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30424   Enter a valid email address
30425 </div>
30426  * @licence LGPL
30427  * @cfg {String} title The title of alert
30428  * @cfg {String} html The content of alert
30429  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30430  * @cfg {String} fa font-awesomeicon
30431  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30432  * @cfg {Boolean} close true to show a x closer
30433  * 
30434  * 
30435  * @constructor
30436  * Create a new alert
30437  * @param {Object} config The config object
30438  */
30439
30440
30441 Roo.bootstrap.Alert = function(config){
30442     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30443     
30444 };
30445
30446 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30447     
30448     title: '',
30449     html: '',
30450     weight: false,
30451     fa: false,
30452     faicon: false, // BC
30453     close : false,
30454     
30455     
30456     getAutoCreate : function()
30457     {
30458         
30459         var cfg = {
30460             tag : 'div',
30461             cls : 'alert',
30462             cn : [
30463                 {
30464                     tag: 'button',
30465                     type :  "button",
30466                     cls: "close",
30467                     html : '×',
30468                     style : this.close ? '' : 'display:none'
30469                 },
30470                 {
30471                     tag : 'i',
30472                     cls : 'roo-alert-icon'
30473                     
30474                 },
30475                 {
30476                     tag : 'b',
30477                     cls : 'roo-alert-title',
30478                     html : this.title
30479                 },
30480                 {
30481                     tag : 'span',
30482                     cls : 'roo-alert-text',
30483                     html : this.html
30484                 }
30485             ]
30486         };
30487         
30488         if(this.faicon){
30489             cfg.cn[0].cls += ' fa ' + this.faicon;
30490         }
30491         if(this.fa){
30492             cfg.cn[0].cls += ' fa ' + this.fa;
30493         }
30494         
30495         if(this.weight){
30496             cfg.cls += ' alert-' + this.weight;
30497         }
30498         
30499         return cfg;
30500     },
30501     
30502     initEvents: function() 
30503     {
30504         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30505         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30506         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30507         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30508         if (this.seconds > 0) {
30509             this.hide.defer(this.seconds, this);
30510         }
30511     },
30512     /**
30513      * Set the Title Message HTML
30514      * @param {String} html
30515      */
30516     setTitle : function(str)
30517     {
30518         this.titleEl.dom.innerHTML = str;
30519     },
30520      
30521      /**
30522      * Set the Body Message HTML
30523      * @param {String} html
30524      */
30525     setHtml : function(str)
30526     {
30527         this.htmlEl.dom.innerHTML = str;
30528     },
30529     /**
30530      * Set the Weight of the alert
30531      * @param {String} (success|info|warning|danger) weight
30532      */
30533     
30534     setWeight : function(weight)
30535     {
30536         if(this.weight){
30537             this.el.removeClass('alert-' + this.weight);
30538         }
30539         
30540         this.weight = weight;
30541         
30542         this.el.addClass('alert-' + this.weight);
30543     },
30544       /**
30545      * Set the Icon of the alert
30546      * @param {String} see fontawsome names (name without the 'fa-' bit)
30547      */
30548     setIcon : function(icon)
30549     {
30550         if(this.faicon){
30551             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30552         }
30553         
30554         this.faicon = icon;
30555         
30556         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30557     },
30558     /**
30559      * Hide the Alert
30560      */
30561     hide: function() 
30562     {
30563         this.el.hide();   
30564     },
30565     /**
30566      * Show the Alert
30567      */
30568     show: function() 
30569     {  
30570         this.el.show();   
30571     }
30572     
30573 });
30574
30575  
30576 /*
30577 * Licence: LGPL
30578 */
30579
30580 /**
30581  * @class Roo.bootstrap.UploadCropbox
30582  * @extends Roo.bootstrap.Component
30583  * Bootstrap UploadCropbox class
30584  * @cfg {String} emptyText show when image has been loaded
30585  * @cfg {String} rotateNotify show when image too small to rotate
30586  * @cfg {Number} errorTimeout default 3000
30587  * @cfg {Number} minWidth default 300
30588  * @cfg {Number} minHeight default 300
30589  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30590  * @cfg {Boolean} isDocument (true|false) default false
30591  * @cfg {String} url action url
30592  * @cfg {String} paramName default 'imageUpload'
30593  * @cfg {String} method default POST
30594  * @cfg {Boolean} loadMask (true|false) default true
30595  * @cfg {Boolean} loadingText default 'Loading...'
30596  * 
30597  * @constructor
30598  * Create a new UploadCropbox
30599  * @param {Object} config The config object
30600  */
30601
30602 Roo.bootstrap.UploadCropbox = function(config){
30603     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30604     
30605     this.addEvents({
30606         /**
30607          * @event beforeselectfile
30608          * Fire before select file
30609          * @param {Roo.bootstrap.UploadCropbox} this
30610          */
30611         "beforeselectfile" : true,
30612         /**
30613          * @event initial
30614          * Fire after initEvent
30615          * @param {Roo.bootstrap.UploadCropbox} this
30616          */
30617         "initial" : true,
30618         /**
30619          * @event crop
30620          * Fire after initEvent
30621          * @param {Roo.bootstrap.UploadCropbox} this
30622          * @param {String} data
30623          */
30624         "crop" : true,
30625         /**
30626          * @event prepare
30627          * Fire when preparing the file data
30628          * @param {Roo.bootstrap.UploadCropbox} this
30629          * @param {Object} file
30630          */
30631         "prepare" : true,
30632         /**
30633          * @event exception
30634          * Fire when get exception
30635          * @param {Roo.bootstrap.UploadCropbox} this
30636          * @param {XMLHttpRequest} xhr
30637          */
30638         "exception" : true,
30639         /**
30640          * @event beforeloadcanvas
30641          * Fire before load the canvas
30642          * @param {Roo.bootstrap.UploadCropbox} this
30643          * @param {String} src
30644          */
30645         "beforeloadcanvas" : true,
30646         /**
30647          * @event trash
30648          * Fire when trash image
30649          * @param {Roo.bootstrap.UploadCropbox} this
30650          */
30651         "trash" : true,
30652         /**
30653          * @event download
30654          * Fire when download the image
30655          * @param {Roo.bootstrap.UploadCropbox} this
30656          */
30657         "download" : true,
30658         /**
30659          * @event footerbuttonclick
30660          * Fire when footerbuttonclick
30661          * @param {Roo.bootstrap.UploadCropbox} this
30662          * @param {String} type
30663          */
30664         "footerbuttonclick" : true,
30665         /**
30666          * @event resize
30667          * Fire when resize
30668          * @param {Roo.bootstrap.UploadCropbox} this
30669          */
30670         "resize" : true,
30671         /**
30672          * @event rotate
30673          * Fire when rotate the image
30674          * @param {Roo.bootstrap.UploadCropbox} this
30675          * @param {String} pos
30676          */
30677         "rotate" : true,
30678         /**
30679          * @event inspect
30680          * Fire when inspect the file
30681          * @param {Roo.bootstrap.UploadCropbox} this
30682          * @param {Object} file
30683          */
30684         "inspect" : true,
30685         /**
30686          * @event upload
30687          * Fire when xhr upload the file
30688          * @param {Roo.bootstrap.UploadCropbox} this
30689          * @param {Object} data
30690          */
30691         "upload" : true,
30692         /**
30693          * @event arrange
30694          * Fire when arrange the file data
30695          * @param {Roo.bootstrap.UploadCropbox} this
30696          * @param {Object} formData
30697          */
30698         "arrange" : true
30699     });
30700     
30701     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30702 };
30703
30704 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30705     
30706     emptyText : 'Click to upload image',
30707     rotateNotify : 'Image is too small to rotate',
30708     errorTimeout : 3000,
30709     scale : 0,
30710     baseScale : 1,
30711     rotate : 0,
30712     dragable : false,
30713     pinching : false,
30714     mouseX : 0,
30715     mouseY : 0,
30716     cropData : false,
30717     minWidth : 300,
30718     minHeight : 300,
30719     file : false,
30720     exif : {},
30721     baseRotate : 1,
30722     cropType : 'image/jpeg',
30723     buttons : false,
30724     canvasLoaded : false,
30725     isDocument : false,
30726     method : 'POST',
30727     paramName : 'imageUpload',
30728     loadMask : true,
30729     loadingText : 'Loading...',
30730     maskEl : false,
30731     
30732     getAutoCreate : function()
30733     {
30734         var cfg = {
30735             tag : 'div',
30736             cls : 'roo-upload-cropbox',
30737             cn : [
30738                 {
30739                     tag : 'input',
30740                     cls : 'roo-upload-cropbox-selector',
30741                     type : 'file'
30742                 },
30743                 {
30744                     tag : 'div',
30745                     cls : 'roo-upload-cropbox-body',
30746                     style : 'cursor:pointer',
30747                     cn : [
30748                         {
30749                             tag : 'div',
30750                             cls : 'roo-upload-cropbox-preview'
30751                         },
30752                         {
30753                             tag : 'div',
30754                             cls : 'roo-upload-cropbox-thumb'
30755                         },
30756                         {
30757                             tag : 'div',
30758                             cls : 'roo-upload-cropbox-empty-notify',
30759                             html : this.emptyText
30760                         },
30761                         {
30762                             tag : 'div',
30763                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30764                             html : this.rotateNotify
30765                         }
30766                     ]
30767                 },
30768                 {
30769                     tag : 'div',
30770                     cls : 'roo-upload-cropbox-footer',
30771                     cn : {
30772                         tag : 'div',
30773                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30774                         cn : []
30775                     }
30776                 }
30777             ]
30778         };
30779         
30780         return cfg;
30781     },
30782     
30783     onRender : function(ct, position)
30784     {
30785         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30786         
30787         if (this.buttons.length) {
30788             
30789             Roo.each(this.buttons, function(bb) {
30790                 
30791                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30792                 
30793                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30794                 
30795             }, this);
30796         }
30797         
30798         if(this.loadMask){
30799             this.maskEl = this.el;
30800         }
30801     },
30802     
30803     initEvents : function()
30804     {
30805         this.urlAPI = (window.createObjectURL && window) || 
30806                                 (window.URL && URL.revokeObjectURL && URL) || 
30807                                 (window.webkitURL && webkitURL);
30808                         
30809         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30810         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30811         
30812         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30813         this.selectorEl.hide();
30814         
30815         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30816         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817         
30818         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30819         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820         this.thumbEl.hide();
30821         
30822         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30823         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824         
30825         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30826         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30827         this.errorEl.hide();
30828         
30829         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30830         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30831         this.footerEl.hide();
30832         
30833         this.setThumbBoxSize();
30834         
30835         this.bind();
30836         
30837         this.resize();
30838         
30839         this.fireEvent('initial', this);
30840     },
30841
30842     bind : function()
30843     {
30844         var _this = this;
30845         
30846         window.addEventListener("resize", function() { _this.resize(); } );
30847         
30848         this.bodyEl.on('click', this.beforeSelectFile, this);
30849         
30850         if(Roo.isTouch){
30851             this.bodyEl.on('touchstart', this.onTouchStart, this);
30852             this.bodyEl.on('touchmove', this.onTouchMove, this);
30853             this.bodyEl.on('touchend', this.onTouchEnd, this);
30854         }
30855         
30856         if(!Roo.isTouch){
30857             this.bodyEl.on('mousedown', this.onMouseDown, this);
30858             this.bodyEl.on('mousemove', this.onMouseMove, this);
30859             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30860             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30861             Roo.get(document).on('mouseup', this.onMouseUp, this);
30862         }
30863         
30864         this.selectorEl.on('change', this.onFileSelected, this);
30865     },
30866     
30867     reset : function()
30868     {    
30869         this.scale = 0;
30870         this.baseScale = 1;
30871         this.rotate = 0;
30872         this.baseRotate = 1;
30873         this.dragable = false;
30874         this.pinching = false;
30875         this.mouseX = 0;
30876         this.mouseY = 0;
30877         this.cropData = false;
30878         this.notifyEl.dom.innerHTML = this.emptyText;
30879         
30880         this.selectorEl.dom.value = '';
30881         
30882     },
30883     
30884     resize : function()
30885     {
30886         if(this.fireEvent('resize', this) != false){
30887             this.setThumbBoxPosition();
30888             this.setCanvasPosition();
30889         }
30890     },
30891     
30892     onFooterButtonClick : function(e, el, o, type)
30893     {
30894         switch (type) {
30895             case 'rotate-left' :
30896                 this.onRotateLeft(e);
30897                 break;
30898             case 'rotate-right' :
30899                 this.onRotateRight(e);
30900                 break;
30901             case 'picture' :
30902                 this.beforeSelectFile(e);
30903                 break;
30904             case 'trash' :
30905                 this.trash(e);
30906                 break;
30907             case 'crop' :
30908                 this.crop(e);
30909                 break;
30910             case 'download' :
30911                 this.download(e);
30912                 break;
30913             default :
30914                 break;
30915         }
30916         
30917         this.fireEvent('footerbuttonclick', this, type);
30918     },
30919     
30920     beforeSelectFile : function(e)
30921     {
30922         e.preventDefault();
30923         
30924         if(this.fireEvent('beforeselectfile', this) != false){
30925             this.selectorEl.dom.click();
30926         }
30927     },
30928     
30929     onFileSelected : function(e)
30930     {
30931         e.preventDefault();
30932         
30933         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30934             return;
30935         }
30936         
30937         var file = this.selectorEl.dom.files[0];
30938         
30939         if(this.fireEvent('inspect', this, file) != false){
30940             this.prepare(file);
30941         }
30942         
30943     },
30944     
30945     trash : function(e)
30946     {
30947         this.fireEvent('trash', this);
30948     },
30949     
30950     download : function(e)
30951     {
30952         this.fireEvent('download', this);
30953     },
30954     
30955     loadCanvas : function(src)
30956     {   
30957         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30958             
30959             this.reset();
30960             
30961             this.imageEl = document.createElement('img');
30962             
30963             var _this = this;
30964             
30965             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30966             
30967             this.imageEl.src = src;
30968         }
30969     },
30970     
30971     onLoadCanvas : function()
30972     {   
30973         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30974         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30975         
30976         this.bodyEl.un('click', this.beforeSelectFile, this);
30977         
30978         this.notifyEl.hide();
30979         this.thumbEl.show();
30980         this.footerEl.show();
30981         
30982         this.baseRotateLevel();
30983         
30984         if(this.isDocument){
30985             this.setThumbBoxSize();
30986         }
30987         
30988         this.setThumbBoxPosition();
30989         
30990         this.baseScaleLevel();
30991         
30992         this.draw();
30993         
30994         this.resize();
30995         
30996         this.canvasLoaded = true;
30997         
30998         if(this.loadMask){
30999             this.maskEl.unmask();
31000         }
31001         
31002     },
31003     
31004     setCanvasPosition : function()
31005     {   
31006         if(!this.canvasEl){
31007             return;
31008         }
31009         
31010         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31011         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31012         
31013         this.previewEl.setLeft(pw);
31014         this.previewEl.setTop(ph);
31015         
31016     },
31017     
31018     onMouseDown : function(e)
31019     {   
31020         e.stopEvent();
31021         
31022         this.dragable = true;
31023         this.pinching = false;
31024         
31025         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31026             this.dragable = false;
31027             return;
31028         }
31029         
31030         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31031         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31032         
31033     },
31034     
31035     onMouseMove : function(e)
31036     {   
31037         e.stopEvent();
31038         
31039         if(!this.canvasLoaded){
31040             return;
31041         }
31042         
31043         if (!this.dragable){
31044             return;
31045         }
31046         
31047         var minX = Math.ceil(this.thumbEl.getLeft(true));
31048         var minY = Math.ceil(this.thumbEl.getTop(true));
31049         
31050         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31051         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31052         
31053         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31054         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31055         
31056         x = x - this.mouseX;
31057         y = y - this.mouseY;
31058         
31059         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31060         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31061         
31062         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31063         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31064         
31065         this.previewEl.setLeft(bgX);
31066         this.previewEl.setTop(bgY);
31067         
31068         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31069         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31070     },
31071     
31072     onMouseUp : function(e)
31073     {   
31074         e.stopEvent();
31075         
31076         this.dragable = false;
31077     },
31078     
31079     onMouseWheel : function(e)
31080     {   
31081         e.stopEvent();
31082         
31083         this.startScale = this.scale;
31084         
31085         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31086         
31087         if(!this.zoomable()){
31088             this.scale = this.startScale;
31089             return;
31090         }
31091         
31092         this.draw();
31093         
31094         return;
31095     },
31096     
31097     zoomable : function()
31098     {
31099         var minScale = this.thumbEl.getWidth() / this.minWidth;
31100         
31101         if(this.minWidth < this.minHeight){
31102             minScale = this.thumbEl.getHeight() / this.minHeight;
31103         }
31104         
31105         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31106         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31107         
31108         if(
31109                 this.isDocument &&
31110                 (this.rotate == 0 || this.rotate == 180) && 
31111                 (
31112                     width > this.imageEl.OriginWidth || 
31113                     height > this.imageEl.OriginHeight ||
31114                     (width < this.minWidth && height < this.minHeight)
31115                 )
31116         ){
31117             return false;
31118         }
31119         
31120         if(
31121                 this.isDocument &&
31122                 (this.rotate == 90 || this.rotate == 270) && 
31123                 (
31124                     width > this.imageEl.OriginWidth || 
31125                     height > this.imageEl.OriginHeight ||
31126                     (width < this.minHeight && height < this.minWidth)
31127                 )
31128         ){
31129             return false;
31130         }
31131         
31132         if(
31133                 !this.isDocument &&
31134                 (this.rotate == 0 || this.rotate == 180) && 
31135                 (
31136                     width < this.minWidth || 
31137                     width > this.imageEl.OriginWidth || 
31138                     height < this.minHeight || 
31139                     height > this.imageEl.OriginHeight
31140                 )
31141         ){
31142             return false;
31143         }
31144         
31145         if(
31146                 !this.isDocument &&
31147                 (this.rotate == 90 || this.rotate == 270) && 
31148                 (
31149                     width < this.minHeight || 
31150                     width > this.imageEl.OriginWidth || 
31151                     height < this.minWidth || 
31152                     height > this.imageEl.OriginHeight
31153                 )
31154         ){
31155             return false;
31156         }
31157         
31158         return true;
31159         
31160     },
31161     
31162     onRotateLeft : function(e)
31163     {   
31164         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31165             
31166             var minScale = this.thumbEl.getWidth() / this.minWidth;
31167             
31168             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31169             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31170             
31171             this.startScale = this.scale;
31172             
31173             while (this.getScaleLevel() < minScale){
31174             
31175                 this.scale = this.scale + 1;
31176                 
31177                 if(!this.zoomable()){
31178                     break;
31179                 }
31180                 
31181                 if(
31182                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31183                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31184                 ){
31185                     continue;
31186                 }
31187                 
31188                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31189
31190                 this.draw();
31191                 
31192                 return;
31193             }
31194             
31195             this.scale = this.startScale;
31196             
31197             this.onRotateFail();
31198             
31199             return false;
31200         }
31201         
31202         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31203
31204         if(this.isDocument){
31205             this.setThumbBoxSize();
31206             this.setThumbBoxPosition();
31207             this.setCanvasPosition();
31208         }
31209         
31210         this.draw();
31211         
31212         this.fireEvent('rotate', this, 'left');
31213         
31214     },
31215     
31216     onRotateRight : function(e)
31217     {
31218         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31219             
31220             var minScale = this.thumbEl.getWidth() / this.minWidth;
31221         
31222             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31223             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31224             
31225             this.startScale = this.scale;
31226             
31227             while (this.getScaleLevel() < minScale){
31228             
31229                 this.scale = this.scale + 1;
31230                 
31231                 if(!this.zoomable()){
31232                     break;
31233                 }
31234                 
31235                 if(
31236                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31237                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31238                 ){
31239                     continue;
31240                 }
31241                 
31242                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31243
31244                 this.draw();
31245                 
31246                 return;
31247             }
31248             
31249             this.scale = this.startScale;
31250             
31251             this.onRotateFail();
31252             
31253             return false;
31254         }
31255         
31256         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31257
31258         if(this.isDocument){
31259             this.setThumbBoxSize();
31260             this.setThumbBoxPosition();
31261             this.setCanvasPosition();
31262         }
31263         
31264         this.draw();
31265         
31266         this.fireEvent('rotate', this, 'right');
31267     },
31268     
31269     onRotateFail : function()
31270     {
31271         this.errorEl.show(true);
31272         
31273         var _this = this;
31274         
31275         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31276     },
31277     
31278     draw : function()
31279     {
31280         this.previewEl.dom.innerHTML = '';
31281         
31282         var canvasEl = document.createElement("canvas");
31283         
31284         var contextEl = canvasEl.getContext("2d");
31285         
31286         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31287         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31288         var center = this.imageEl.OriginWidth / 2;
31289         
31290         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31291             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31292             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31293             center = this.imageEl.OriginHeight / 2;
31294         }
31295         
31296         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31297         
31298         contextEl.translate(center, center);
31299         contextEl.rotate(this.rotate * Math.PI / 180);
31300
31301         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31302         
31303         this.canvasEl = document.createElement("canvas");
31304         
31305         this.contextEl = this.canvasEl.getContext("2d");
31306         
31307         switch (this.rotate) {
31308             case 0 :
31309                 
31310                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31311                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31312                 
31313                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31314                 
31315                 break;
31316             case 90 : 
31317                 
31318                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31319                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31320                 
31321                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31322                     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);
31323                     break;
31324                 }
31325                 
31326                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31327                 
31328                 break;
31329             case 180 :
31330                 
31331                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31332                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31333                 
31334                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31335                     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);
31336                     break;
31337                 }
31338                 
31339                 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);
31340                 
31341                 break;
31342             case 270 :
31343                 
31344                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31345                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31346         
31347                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31348                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31349                     break;
31350                 }
31351                 
31352                 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);
31353                 
31354                 break;
31355             default : 
31356                 break;
31357         }
31358         
31359         this.previewEl.appendChild(this.canvasEl);
31360         
31361         this.setCanvasPosition();
31362     },
31363     
31364     crop : function()
31365     {
31366         if(!this.canvasLoaded){
31367             return;
31368         }
31369         
31370         var imageCanvas = document.createElement("canvas");
31371         
31372         var imageContext = imageCanvas.getContext("2d");
31373         
31374         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31375         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31376         
31377         var center = imageCanvas.width / 2;
31378         
31379         imageContext.translate(center, center);
31380         
31381         imageContext.rotate(this.rotate * Math.PI / 180);
31382         
31383         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31384         
31385         var canvas = document.createElement("canvas");
31386         
31387         var context = canvas.getContext("2d");
31388                 
31389         canvas.width = this.minWidth;
31390         canvas.height = this.minHeight;
31391
31392         switch (this.rotate) {
31393             case 0 :
31394                 
31395                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31396                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31397                 
31398                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31399                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31400                 
31401                 var targetWidth = this.minWidth - 2 * x;
31402                 var targetHeight = this.minHeight - 2 * y;
31403                 
31404                 var scale = 1;
31405                 
31406                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31407                     scale = targetWidth / width;
31408                 }
31409                 
31410                 if(x > 0 && y == 0){
31411                     scale = targetHeight / height;
31412                 }
31413                 
31414                 if(x > 0 && y > 0){
31415                     scale = targetWidth / width;
31416                     
31417                     if(width < height){
31418                         scale = targetHeight / height;
31419                     }
31420                 }
31421                 
31422                 context.scale(scale, scale);
31423                 
31424                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31425                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31426
31427                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31428                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31429
31430                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31431                 
31432                 break;
31433             case 90 : 
31434                 
31435                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31436                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31437                 
31438                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31439                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31440                 
31441                 var targetWidth = this.minWidth - 2 * x;
31442                 var targetHeight = this.minHeight - 2 * y;
31443                 
31444                 var scale = 1;
31445                 
31446                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31447                     scale = targetWidth / width;
31448                 }
31449                 
31450                 if(x > 0 && y == 0){
31451                     scale = targetHeight / height;
31452                 }
31453                 
31454                 if(x > 0 && y > 0){
31455                     scale = targetWidth / width;
31456                     
31457                     if(width < height){
31458                         scale = targetHeight / height;
31459                     }
31460                 }
31461                 
31462                 context.scale(scale, scale);
31463                 
31464                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31465                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31466
31467                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31468                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31469                 
31470                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31471                 
31472                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31473                 
31474                 break;
31475             case 180 :
31476                 
31477                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31478                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31479                 
31480                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31481                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31482                 
31483                 var targetWidth = this.minWidth - 2 * x;
31484                 var targetHeight = this.minHeight - 2 * y;
31485                 
31486                 var scale = 1;
31487                 
31488                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31489                     scale = targetWidth / width;
31490                 }
31491                 
31492                 if(x > 0 && y == 0){
31493                     scale = targetHeight / height;
31494                 }
31495                 
31496                 if(x > 0 && y > 0){
31497                     scale = targetWidth / width;
31498                     
31499                     if(width < height){
31500                         scale = targetHeight / height;
31501                     }
31502                 }
31503                 
31504                 context.scale(scale, scale);
31505                 
31506                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31507                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31508
31509                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31510                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31511
31512                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31513                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31514                 
31515                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31516                 
31517                 break;
31518             case 270 :
31519                 
31520                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31521                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31522                 
31523                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31524                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31525                 
31526                 var targetWidth = this.minWidth - 2 * x;
31527                 var targetHeight = this.minHeight - 2 * y;
31528                 
31529                 var scale = 1;
31530                 
31531                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31532                     scale = targetWidth / width;
31533                 }
31534                 
31535                 if(x > 0 && y == 0){
31536                     scale = targetHeight / height;
31537                 }
31538                 
31539                 if(x > 0 && y > 0){
31540                     scale = targetWidth / width;
31541                     
31542                     if(width < height){
31543                         scale = targetHeight / height;
31544                     }
31545                 }
31546                 
31547                 context.scale(scale, scale);
31548                 
31549                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31550                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31551
31552                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31553                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31554                 
31555                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31556                 
31557                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31558                 
31559                 break;
31560             default : 
31561                 break;
31562         }
31563         
31564         this.cropData = canvas.toDataURL(this.cropType);
31565         
31566         if(this.fireEvent('crop', this, this.cropData) !== false){
31567             this.process(this.file, this.cropData);
31568         }
31569         
31570         return;
31571         
31572     },
31573     
31574     setThumbBoxSize : function()
31575     {
31576         var width, height;
31577         
31578         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31579             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31580             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31581             
31582             this.minWidth = width;
31583             this.minHeight = height;
31584             
31585             if(this.rotate == 90 || this.rotate == 270){
31586                 this.minWidth = height;
31587                 this.minHeight = width;
31588             }
31589         }
31590         
31591         height = 300;
31592         width = Math.ceil(this.minWidth * height / this.minHeight);
31593         
31594         if(this.minWidth > this.minHeight){
31595             width = 300;
31596             height = Math.ceil(this.minHeight * width / this.minWidth);
31597         }
31598         
31599         this.thumbEl.setStyle({
31600             width : width + 'px',
31601             height : height + 'px'
31602         });
31603
31604         return;
31605             
31606     },
31607     
31608     setThumbBoxPosition : function()
31609     {
31610         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31611         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31612         
31613         this.thumbEl.setLeft(x);
31614         this.thumbEl.setTop(y);
31615         
31616     },
31617     
31618     baseRotateLevel : function()
31619     {
31620         this.baseRotate = 1;
31621         
31622         if(
31623                 typeof(this.exif) != 'undefined' &&
31624                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31625                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31626         ){
31627             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31628         }
31629         
31630         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31631         
31632     },
31633     
31634     baseScaleLevel : function()
31635     {
31636         var width, height;
31637         
31638         if(this.isDocument){
31639             
31640             if(this.baseRotate == 6 || this.baseRotate == 8){
31641             
31642                 height = this.thumbEl.getHeight();
31643                 this.baseScale = height / this.imageEl.OriginWidth;
31644
31645                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31646                     width = this.thumbEl.getWidth();
31647                     this.baseScale = width / this.imageEl.OriginHeight;
31648                 }
31649
31650                 return;
31651             }
31652
31653             height = this.thumbEl.getHeight();
31654             this.baseScale = height / this.imageEl.OriginHeight;
31655
31656             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31657                 width = this.thumbEl.getWidth();
31658                 this.baseScale = width / this.imageEl.OriginWidth;
31659             }
31660
31661             return;
31662         }
31663         
31664         if(this.baseRotate == 6 || this.baseRotate == 8){
31665             
31666             width = this.thumbEl.getHeight();
31667             this.baseScale = width / this.imageEl.OriginHeight;
31668             
31669             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31670                 height = this.thumbEl.getWidth();
31671                 this.baseScale = height / this.imageEl.OriginHeight;
31672             }
31673             
31674             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31675                 height = this.thumbEl.getWidth();
31676                 this.baseScale = height / this.imageEl.OriginHeight;
31677                 
31678                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31679                     width = this.thumbEl.getHeight();
31680                     this.baseScale = width / this.imageEl.OriginWidth;
31681                 }
31682             }
31683             
31684             return;
31685         }
31686         
31687         width = this.thumbEl.getWidth();
31688         this.baseScale = width / this.imageEl.OriginWidth;
31689         
31690         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31691             height = this.thumbEl.getHeight();
31692             this.baseScale = height / this.imageEl.OriginHeight;
31693         }
31694         
31695         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31696             
31697             height = this.thumbEl.getHeight();
31698             this.baseScale = height / this.imageEl.OriginHeight;
31699             
31700             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31701                 width = this.thumbEl.getWidth();
31702                 this.baseScale = width / this.imageEl.OriginWidth;
31703             }
31704             
31705         }
31706         
31707         return;
31708     },
31709     
31710     getScaleLevel : function()
31711     {
31712         return this.baseScale * Math.pow(1.1, this.scale);
31713     },
31714     
31715     onTouchStart : function(e)
31716     {
31717         if(!this.canvasLoaded){
31718             this.beforeSelectFile(e);
31719             return;
31720         }
31721         
31722         var touches = e.browserEvent.touches;
31723         
31724         if(!touches){
31725             return;
31726         }
31727         
31728         if(touches.length == 1){
31729             this.onMouseDown(e);
31730             return;
31731         }
31732         
31733         if(touches.length != 2){
31734             return;
31735         }
31736         
31737         var coords = [];
31738         
31739         for(var i = 0, finger; finger = touches[i]; i++){
31740             coords.push(finger.pageX, finger.pageY);
31741         }
31742         
31743         var x = Math.pow(coords[0] - coords[2], 2);
31744         var y = Math.pow(coords[1] - coords[3], 2);
31745         
31746         this.startDistance = Math.sqrt(x + y);
31747         
31748         this.startScale = this.scale;
31749         
31750         this.pinching = true;
31751         this.dragable = false;
31752         
31753     },
31754     
31755     onTouchMove : function(e)
31756     {
31757         if(!this.pinching && !this.dragable){
31758             return;
31759         }
31760         
31761         var touches = e.browserEvent.touches;
31762         
31763         if(!touches){
31764             return;
31765         }
31766         
31767         if(this.dragable){
31768             this.onMouseMove(e);
31769             return;
31770         }
31771         
31772         var coords = [];
31773         
31774         for(var i = 0, finger; finger = touches[i]; i++){
31775             coords.push(finger.pageX, finger.pageY);
31776         }
31777         
31778         var x = Math.pow(coords[0] - coords[2], 2);
31779         var y = Math.pow(coords[1] - coords[3], 2);
31780         
31781         this.endDistance = Math.sqrt(x + y);
31782         
31783         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31784         
31785         if(!this.zoomable()){
31786             this.scale = this.startScale;
31787             return;
31788         }
31789         
31790         this.draw();
31791         
31792     },
31793     
31794     onTouchEnd : function(e)
31795     {
31796         this.pinching = false;
31797         this.dragable = false;
31798         
31799     },
31800     
31801     process : function(file, crop)
31802     {
31803         if(this.loadMask){
31804             this.maskEl.mask(this.loadingText);
31805         }
31806         
31807         this.xhr = new XMLHttpRequest();
31808         
31809         file.xhr = this.xhr;
31810
31811         this.xhr.open(this.method, this.url, true);
31812         
31813         var headers = {
31814             "Accept": "application/json",
31815             "Cache-Control": "no-cache",
31816             "X-Requested-With": "XMLHttpRequest"
31817         };
31818         
31819         for (var headerName in headers) {
31820             var headerValue = headers[headerName];
31821             if (headerValue) {
31822                 this.xhr.setRequestHeader(headerName, headerValue);
31823             }
31824         }
31825         
31826         var _this = this;
31827         
31828         this.xhr.onload = function()
31829         {
31830             _this.xhrOnLoad(_this.xhr);
31831         }
31832         
31833         this.xhr.onerror = function()
31834         {
31835             _this.xhrOnError(_this.xhr);
31836         }
31837         
31838         var formData = new FormData();
31839
31840         formData.append('returnHTML', 'NO');
31841         
31842         if(crop){
31843             formData.append('crop', crop);
31844         }
31845         
31846         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31847             formData.append(this.paramName, file, file.name);
31848         }
31849         
31850         if(typeof(file.filename) != 'undefined'){
31851             formData.append('filename', file.filename);
31852         }
31853         
31854         if(typeof(file.mimetype) != 'undefined'){
31855             formData.append('mimetype', file.mimetype);
31856         }
31857         
31858         if(this.fireEvent('arrange', this, formData) != false){
31859             this.xhr.send(formData);
31860         };
31861     },
31862     
31863     xhrOnLoad : function(xhr)
31864     {
31865         if(this.loadMask){
31866             this.maskEl.unmask();
31867         }
31868         
31869         if (xhr.readyState !== 4) {
31870             this.fireEvent('exception', this, xhr);
31871             return;
31872         }
31873
31874         var response = Roo.decode(xhr.responseText);
31875         
31876         if(!response.success){
31877             this.fireEvent('exception', this, xhr);
31878             return;
31879         }
31880         
31881         var response = Roo.decode(xhr.responseText);
31882         
31883         this.fireEvent('upload', this, response);
31884         
31885     },
31886     
31887     xhrOnError : function()
31888     {
31889         if(this.loadMask){
31890             this.maskEl.unmask();
31891         }
31892         
31893         Roo.log('xhr on error');
31894         
31895         var response = Roo.decode(xhr.responseText);
31896           
31897         Roo.log(response);
31898         
31899     },
31900     
31901     prepare : function(file)
31902     {   
31903         if(this.loadMask){
31904             this.maskEl.mask(this.loadingText);
31905         }
31906         
31907         this.file = false;
31908         this.exif = {};
31909         
31910         if(typeof(file) === 'string'){
31911             this.loadCanvas(file);
31912             return;
31913         }
31914         
31915         if(!file || !this.urlAPI){
31916             return;
31917         }
31918         
31919         this.file = file;
31920         this.cropType = file.type;
31921         
31922         var _this = this;
31923         
31924         if(this.fireEvent('prepare', this, this.file) != false){
31925             
31926             var reader = new FileReader();
31927             
31928             reader.onload = function (e) {
31929                 if (e.target.error) {
31930                     Roo.log(e.target.error);
31931                     return;
31932                 }
31933                 
31934                 var buffer = e.target.result,
31935                     dataView = new DataView(buffer),
31936                     offset = 2,
31937                     maxOffset = dataView.byteLength - 4,
31938                     markerBytes,
31939                     markerLength;
31940                 
31941                 if (dataView.getUint16(0) === 0xffd8) {
31942                     while (offset < maxOffset) {
31943                         markerBytes = dataView.getUint16(offset);
31944                         
31945                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31946                             markerLength = dataView.getUint16(offset + 2) + 2;
31947                             if (offset + markerLength > dataView.byteLength) {
31948                                 Roo.log('Invalid meta data: Invalid segment size.');
31949                                 break;
31950                             }
31951                             
31952                             if(markerBytes == 0xffe1){
31953                                 _this.parseExifData(
31954                                     dataView,
31955                                     offset,
31956                                     markerLength
31957                                 );
31958                             }
31959                             
31960                             offset += markerLength;
31961                             
31962                             continue;
31963                         }
31964                         
31965                         break;
31966                     }
31967                     
31968                 }
31969                 
31970                 var url = _this.urlAPI.createObjectURL(_this.file);
31971                 
31972                 _this.loadCanvas(url);
31973                 
31974                 return;
31975             }
31976             
31977             reader.readAsArrayBuffer(this.file);
31978             
31979         }
31980         
31981     },
31982     
31983     parseExifData : function(dataView, offset, length)
31984     {
31985         var tiffOffset = offset + 10,
31986             littleEndian,
31987             dirOffset;
31988     
31989         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31990             // No Exif data, might be XMP data instead
31991             return;
31992         }
31993         
31994         // Check for the ASCII code for "Exif" (0x45786966):
31995         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31996             // No Exif data, might be XMP data instead
31997             return;
31998         }
31999         if (tiffOffset + 8 > dataView.byteLength) {
32000             Roo.log('Invalid Exif data: Invalid segment size.');
32001             return;
32002         }
32003         // Check for the two null bytes:
32004         if (dataView.getUint16(offset + 8) !== 0x0000) {
32005             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32006             return;
32007         }
32008         // Check the byte alignment:
32009         switch (dataView.getUint16(tiffOffset)) {
32010         case 0x4949:
32011             littleEndian = true;
32012             break;
32013         case 0x4D4D:
32014             littleEndian = false;
32015             break;
32016         default:
32017             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32018             return;
32019         }
32020         // Check for the TIFF tag marker (0x002A):
32021         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32022             Roo.log('Invalid Exif data: Missing TIFF marker.');
32023             return;
32024         }
32025         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32026         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32027         
32028         this.parseExifTags(
32029             dataView,
32030             tiffOffset,
32031             tiffOffset + dirOffset,
32032             littleEndian
32033         );
32034     },
32035     
32036     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32037     {
32038         var tagsNumber,
32039             dirEndOffset,
32040             i;
32041         if (dirOffset + 6 > dataView.byteLength) {
32042             Roo.log('Invalid Exif data: Invalid directory offset.');
32043             return;
32044         }
32045         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32046         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32047         if (dirEndOffset + 4 > dataView.byteLength) {
32048             Roo.log('Invalid Exif data: Invalid directory size.');
32049             return;
32050         }
32051         for (i = 0; i < tagsNumber; i += 1) {
32052             this.parseExifTag(
32053                 dataView,
32054                 tiffOffset,
32055                 dirOffset + 2 + 12 * i, // tag offset
32056                 littleEndian
32057             );
32058         }
32059         // Return the offset to the next directory:
32060         return dataView.getUint32(dirEndOffset, littleEndian);
32061     },
32062     
32063     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32064     {
32065         var tag = dataView.getUint16(offset, littleEndian);
32066         
32067         this.exif[tag] = this.getExifValue(
32068             dataView,
32069             tiffOffset,
32070             offset,
32071             dataView.getUint16(offset + 2, littleEndian), // tag type
32072             dataView.getUint32(offset + 4, littleEndian), // tag length
32073             littleEndian
32074         );
32075     },
32076     
32077     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32078     {
32079         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32080             tagSize,
32081             dataOffset,
32082             values,
32083             i,
32084             str,
32085             c;
32086     
32087         if (!tagType) {
32088             Roo.log('Invalid Exif data: Invalid tag type.');
32089             return;
32090         }
32091         
32092         tagSize = tagType.size * length;
32093         // Determine if the value is contained in the dataOffset bytes,
32094         // or if the value at the dataOffset is a pointer to the actual data:
32095         dataOffset = tagSize > 4 ?
32096                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32097         if (dataOffset + tagSize > dataView.byteLength) {
32098             Roo.log('Invalid Exif data: Invalid data offset.');
32099             return;
32100         }
32101         if (length === 1) {
32102             return tagType.getValue(dataView, dataOffset, littleEndian);
32103         }
32104         values = [];
32105         for (i = 0; i < length; i += 1) {
32106             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32107         }
32108         
32109         if (tagType.ascii) {
32110             str = '';
32111             // Concatenate the chars:
32112             for (i = 0; i < values.length; i += 1) {
32113                 c = values[i];
32114                 // Ignore the terminating NULL byte(s):
32115                 if (c === '\u0000') {
32116                     break;
32117                 }
32118                 str += c;
32119             }
32120             return str;
32121         }
32122         return values;
32123     }
32124     
32125 });
32126
32127 Roo.apply(Roo.bootstrap.UploadCropbox, {
32128     tags : {
32129         'Orientation': 0x0112
32130     },
32131     
32132     Orientation: {
32133             1: 0, //'top-left',
32134 //            2: 'top-right',
32135             3: 180, //'bottom-right',
32136 //            4: 'bottom-left',
32137 //            5: 'left-top',
32138             6: 90, //'right-top',
32139 //            7: 'right-bottom',
32140             8: 270 //'left-bottom'
32141     },
32142     
32143     exifTagTypes : {
32144         // byte, 8-bit unsigned int:
32145         1: {
32146             getValue: function (dataView, dataOffset) {
32147                 return dataView.getUint8(dataOffset);
32148             },
32149             size: 1
32150         },
32151         // ascii, 8-bit byte:
32152         2: {
32153             getValue: function (dataView, dataOffset) {
32154                 return String.fromCharCode(dataView.getUint8(dataOffset));
32155             },
32156             size: 1,
32157             ascii: true
32158         },
32159         // short, 16 bit int:
32160         3: {
32161             getValue: function (dataView, dataOffset, littleEndian) {
32162                 return dataView.getUint16(dataOffset, littleEndian);
32163             },
32164             size: 2
32165         },
32166         // long, 32 bit int:
32167         4: {
32168             getValue: function (dataView, dataOffset, littleEndian) {
32169                 return dataView.getUint32(dataOffset, littleEndian);
32170             },
32171             size: 4
32172         },
32173         // rational = two long values, first is numerator, second is denominator:
32174         5: {
32175             getValue: function (dataView, dataOffset, littleEndian) {
32176                 return dataView.getUint32(dataOffset, littleEndian) /
32177                     dataView.getUint32(dataOffset + 4, littleEndian);
32178             },
32179             size: 8
32180         },
32181         // slong, 32 bit signed int:
32182         9: {
32183             getValue: function (dataView, dataOffset, littleEndian) {
32184                 return dataView.getInt32(dataOffset, littleEndian);
32185             },
32186             size: 4
32187         },
32188         // srational, two slongs, first is numerator, second is denominator:
32189         10: {
32190             getValue: function (dataView, dataOffset, littleEndian) {
32191                 return dataView.getInt32(dataOffset, littleEndian) /
32192                     dataView.getInt32(dataOffset + 4, littleEndian);
32193             },
32194             size: 8
32195         }
32196     },
32197     
32198     footer : {
32199         STANDARD : [
32200             {
32201                 tag : 'div',
32202                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32203                 action : 'rotate-left',
32204                 cn : [
32205                     {
32206                         tag : 'button',
32207                         cls : 'btn btn-default',
32208                         html : '<i class="fa fa-undo"></i>'
32209                     }
32210                 ]
32211             },
32212             {
32213                 tag : 'div',
32214                 cls : 'btn-group roo-upload-cropbox-picture',
32215                 action : 'picture',
32216                 cn : [
32217                     {
32218                         tag : 'button',
32219                         cls : 'btn btn-default',
32220                         html : '<i class="fa fa-picture-o"></i>'
32221                     }
32222                 ]
32223             },
32224             {
32225                 tag : 'div',
32226                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32227                 action : 'rotate-right',
32228                 cn : [
32229                     {
32230                         tag : 'button',
32231                         cls : 'btn btn-default',
32232                         html : '<i class="fa fa-repeat"></i>'
32233                     }
32234                 ]
32235             }
32236         ],
32237         DOCUMENT : [
32238             {
32239                 tag : 'div',
32240                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32241                 action : 'rotate-left',
32242                 cn : [
32243                     {
32244                         tag : 'button',
32245                         cls : 'btn btn-default',
32246                         html : '<i class="fa fa-undo"></i>'
32247                     }
32248                 ]
32249             },
32250             {
32251                 tag : 'div',
32252                 cls : 'btn-group roo-upload-cropbox-download',
32253                 action : 'download',
32254                 cn : [
32255                     {
32256                         tag : 'button',
32257                         cls : 'btn btn-default',
32258                         html : '<i class="fa fa-download"></i>'
32259                     }
32260                 ]
32261             },
32262             {
32263                 tag : 'div',
32264                 cls : 'btn-group roo-upload-cropbox-crop',
32265                 action : 'crop',
32266                 cn : [
32267                     {
32268                         tag : 'button',
32269                         cls : 'btn btn-default',
32270                         html : '<i class="fa fa-crop"></i>'
32271                     }
32272                 ]
32273             },
32274             {
32275                 tag : 'div',
32276                 cls : 'btn-group roo-upload-cropbox-trash',
32277                 action : 'trash',
32278                 cn : [
32279                     {
32280                         tag : 'button',
32281                         cls : 'btn btn-default',
32282                         html : '<i class="fa fa-trash"></i>'
32283                     }
32284                 ]
32285             },
32286             {
32287                 tag : 'div',
32288                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32289                 action : 'rotate-right',
32290                 cn : [
32291                     {
32292                         tag : 'button',
32293                         cls : 'btn btn-default',
32294                         html : '<i class="fa fa-repeat"></i>'
32295                     }
32296                 ]
32297             }
32298         ],
32299         ROTATOR : [
32300             {
32301                 tag : 'div',
32302                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32303                 action : 'rotate-left',
32304                 cn : [
32305                     {
32306                         tag : 'button',
32307                         cls : 'btn btn-default',
32308                         html : '<i class="fa fa-undo"></i>'
32309                     }
32310                 ]
32311             },
32312             {
32313                 tag : 'div',
32314                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32315                 action : 'rotate-right',
32316                 cn : [
32317                     {
32318                         tag : 'button',
32319                         cls : 'btn btn-default',
32320                         html : '<i class="fa fa-repeat"></i>'
32321                     }
32322                 ]
32323             }
32324         ]
32325     }
32326 });
32327
32328 /*
32329 * Licence: LGPL
32330 */
32331
32332 /**
32333  * @class Roo.bootstrap.DocumentManager
32334  * @extends Roo.bootstrap.Component
32335  * Bootstrap DocumentManager class
32336  * @cfg {String} paramName default 'imageUpload'
32337  * @cfg {String} toolTipName default 'filename'
32338  * @cfg {String} method default POST
32339  * @cfg {String} url action url
32340  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32341  * @cfg {Boolean} multiple multiple upload default true
32342  * @cfg {Number} thumbSize default 300
32343  * @cfg {String} fieldLabel
32344  * @cfg {Number} labelWidth default 4
32345  * @cfg {String} labelAlign (left|top) default left
32346  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32347 * @cfg {Number} labellg set the width of label (1-12)
32348  * @cfg {Number} labelmd set the width of label (1-12)
32349  * @cfg {Number} labelsm set the width of label (1-12)
32350  * @cfg {Number} labelxs set the width of label (1-12)
32351  * 
32352  * @constructor
32353  * Create a new DocumentManager
32354  * @param {Object} config The config object
32355  */
32356
32357 Roo.bootstrap.DocumentManager = function(config){
32358     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32359     
32360     this.files = [];
32361     this.delegates = [];
32362     
32363     this.addEvents({
32364         /**
32365          * @event initial
32366          * Fire when initial the DocumentManager
32367          * @param {Roo.bootstrap.DocumentManager} this
32368          */
32369         "initial" : true,
32370         /**
32371          * @event inspect
32372          * inspect selected file
32373          * @param {Roo.bootstrap.DocumentManager} this
32374          * @param {File} file
32375          */
32376         "inspect" : true,
32377         /**
32378          * @event exception
32379          * Fire when xhr load exception
32380          * @param {Roo.bootstrap.DocumentManager} this
32381          * @param {XMLHttpRequest} xhr
32382          */
32383         "exception" : true,
32384         /**
32385          * @event afterupload
32386          * Fire when xhr load exception
32387          * @param {Roo.bootstrap.DocumentManager} this
32388          * @param {XMLHttpRequest} xhr
32389          */
32390         "afterupload" : true,
32391         /**
32392          * @event prepare
32393          * prepare the form data
32394          * @param {Roo.bootstrap.DocumentManager} this
32395          * @param {Object} formData
32396          */
32397         "prepare" : true,
32398         /**
32399          * @event remove
32400          * Fire when remove the file
32401          * @param {Roo.bootstrap.DocumentManager} this
32402          * @param {Object} file
32403          */
32404         "remove" : true,
32405         /**
32406          * @event refresh
32407          * Fire after refresh the file
32408          * @param {Roo.bootstrap.DocumentManager} this
32409          */
32410         "refresh" : true,
32411         /**
32412          * @event click
32413          * Fire after click the image
32414          * @param {Roo.bootstrap.DocumentManager} this
32415          * @param {Object} file
32416          */
32417         "click" : true,
32418         /**
32419          * @event edit
32420          * Fire when upload a image and editable set to true
32421          * @param {Roo.bootstrap.DocumentManager} this
32422          * @param {Object} file
32423          */
32424         "edit" : true,
32425         /**
32426          * @event beforeselectfile
32427          * Fire before select file
32428          * @param {Roo.bootstrap.DocumentManager} this
32429          */
32430         "beforeselectfile" : true,
32431         /**
32432          * @event process
32433          * Fire before process file
32434          * @param {Roo.bootstrap.DocumentManager} this
32435          * @param {Object} file
32436          */
32437         "process" : true,
32438         /**
32439          * @event previewrendered
32440          * Fire when preview rendered
32441          * @param {Roo.bootstrap.DocumentManager} this
32442          * @param {Object} file
32443          */
32444         "previewrendered" : true,
32445         /**
32446          */
32447         "previewResize" : true
32448         
32449     });
32450 };
32451
32452 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32453     
32454     boxes : 0,
32455     inputName : '',
32456     thumbSize : 300,
32457     multiple : true,
32458     files : false,
32459     method : 'POST',
32460     url : '',
32461     paramName : 'imageUpload',
32462     toolTipName : 'filename',
32463     fieldLabel : '',
32464     labelWidth : 4,
32465     labelAlign : 'left',
32466     editable : true,
32467     delegates : false,
32468     xhr : false, 
32469     
32470     labellg : 0,
32471     labelmd : 0,
32472     labelsm : 0,
32473     labelxs : 0,
32474     
32475     getAutoCreate : function()
32476     {   
32477         var managerWidget = {
32478             tag : 'div',
32479             cls : 'roo-document-manager',
32480             cn : [
32481                 {
32482                     tag : 'input',
32483                     cls : 'roo-document-manager-selector',
32484                     type : 'file'
32485                 },
32486                 {
32487                     tag : 'div',
32488                     cls : 'roo-document-manager-uploader',
32489                     cn : [
32490                         {
32491                             tag : 'div',
32492                             cls : 'roo-document-manager-upload-btn',
32493                             html : '<i class="fa fa-plus"></i>'
32494                         }
32495                     ]
32496                     
32497                 }
32498             ]
32499         };
32500         
32501         var content = [
32502             {
32503                 tag : 'div',
32504                 cls : 'column col-md-12',
32505                 cn : managerWidget
32506             }
32507         ];
32508         
32509         if(this.fieldLabel.length){
32510             
32511             content = [
32512                 {
32513                     tag : 'div',
32514                     cls : 'column col-md-12',
32515                     html : this.fieldLabel
32516                 },
32517                 {
32518                     tag : 'div',
32519                     cls : 'column col-md-12',
32520                     cn : managerWidget
32521                 }
32522             ];
32523
32524             if(this.labelAlign == 'left'){
32525                 content = [
32526                     {
32527                         tag : 'div',
32528                         cls : 'column',
32529                         html : this.fieldLabel
32530                     },
32531                     {
32532                         tag : 'div',
32533                         cls : 'column',
32534                         cn : managerWidget
32535                     }
32536                 ];
32537                 
32538                 if(this.labelWidth > 12){
32539                     content[0].style = "width: " + this.labelWidth + 'px';
32540                 }
32541
32542                 if(this.labelWidth < 13 && this.labelmd == 0){
32543                     this.labelmd = this.labelWidth;
32544                 }
32545
32546                 if(this.labellg > 0){
32547                     content[0].cls += ' col-lg-' + this.labellg;
32548                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32549                 }
32550
32551                 if(this.labelmd > 0){
32552                     content[0].cls += ' col-md-' + this.labelmd;
32553                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32554                 }
32555
32556                 if(this.labelsm > 0){
32557                     content[0].cls += ' col-sm-' + this.labelsm;
32558                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32559                 }
32560
32561                 if(this.labelxs > 0){
32562                     content[0].cls += ' col-xs-' + this.labelxs;
32563                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32564                 }
32565                 
32566             }
32567         }
32568         
32569         var cfg = {
32570             tag : 'div',
32571             cls : 'row clearfix',
32572             cn : content
32573         };
32574         
32575         return cfg;
32576         
32577     },
32578     
32579     initEvents : function()
32580     {
32581         this.managerEl = this.el.select('.roo-document-manager', true).first();
32582         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32583         
32584         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32585         this.selectorEl.hide();
32586         
32587         if(this.multiple){
32588             this.selectorEl.attr('multiple', 'multiple');
32589         }
32590         
32591         this.selectorEl.on('change', this.onFileSelected, this);
32592         
32593         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32594         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32595         
32596         this.uploader.on('click', this.onUploaderClick, this);
32597         
32598         this.renderProgressDialog();
32599         
32600         var _this = this;
32601         
32602         window.addEventListener("resize", function() { _this.refresh(); } );
32603         
32604         this.fireEvent('initial', this);
32605     },
32606     
32607     renderProgressDialog : function()
32608     {
32609         var _this = this;
32610         
32611         this.progressDialog = new Roo.bootstrap.Modal({
32612             cls : 'roo-document-manager-progress-dialog',
32613             allow_close : false,
32614             animate : false,
32615             title : '',
32616             buttons : [
32617                 {
32618                     name  :'cancel',
32619                     weight : 'danger',
32620                     html : 'Cancel'
32621                 }
32622             ], 
32623             listeners : { 
32624                 btnclick : function() {
32625                     _this.uploadCancel();
32626                     this.hide();
32627                 }
32628             }
32629         });
32630          
32631         this.progressDialog.render(Roo.get(document.body));
32632          
32633         this.progress = new Roo.bootstrap.Progress({
32634             cls : 'roo-document-manager-progress',
32635             active : true,
32636             striped : true
32637         });
32638         
32639         this.progress.render(this.progressDialog.getChildContainer());
32640         
32641         this.progressBar = new Roo.bootstrap.ProgressBar({
32642             cls : 'roo-document-manager-progress-bar',
32643             aria_valuenow : 0,
32644             aria_valuemin : 0,
32645             aria_valuemax : 12,
32646             panel : 'success'
32647         });
32648         
32649         this.progressBar.render(this.progress.getChildContainer());
32650     },
32651     
32652     onUploaderClick : function(e)
32653     {
32654         e.preventDefault();
32655      
32656         if(this.fireEvent('beforeselectfile', this) != false){
32657             this.selectorEl.dom.click();
32658         }
32659         
32660     },
32661     
32662     onFileSelected : function(e)
32663     {
32664         e.preventDefault();
32665         
32666         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32667             return;
32668         }
32669         
32670         Roo.each(this.selectorEl.dom.files, function(file){
32671             if(this.fireEvent('inspect', this, file) != false){
32672                 this.files.push(file);
32673             }
32674         }, this);
32675         
32676         this.queue();
32677         
32678     },
32679     
32680     queue : function()
32681     {
32682         this.selectorEl.dom.value = '';
32683         
32684         if(!this.files || !this.files.length){
32685             return;
32686         }
32687         
32688         if(this.boxes > 0 && this.files.length > this.boxes){
32689             this.files = this.files.slice(0, this.boxes);
32690         }
32691         
32692         this.uploader.show();
32693         
32694         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32695             this.uploader.hide();
32696         }
32697         
32698         var _this = this;
32699         
32700         var files = [];
32701         
32702         var docs = [];
32703         
32704         Roo.each(this.files, function(file){
32705             
32706             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32707                 var f = this.renderPreview(file);
32708                 files.push(f);
32709                 return;
32710             }
32711             
32712             if(file.type.indexOf('image') != -1){
32713                 this.delegates.push(
32714                     (function(){
32715                         _this.process(file);
32716                     }).createDelegate(this)
32717                 );
32718         
32719                 return;
32720             }
32721             
32722             docs.push(
32723                 (function(){
32724                     _this.process(file);
32725                 }).createDelegate(this)
32726             );
32727             
32728         }, this);
32729         
32730         this.files = files;
32731         
32732         this.delegates = this.delegates.concat(docs);
32733         
32734         if(!this.delegates.length){
32735             this.refresh();
32736             return;
32737         }
32738         
32739         this.progressBar.aria_valuemax = this.delegates.length;
32740         
32741         this.arrange();
32742         
32743         return;
32744     },
32745     
32746     arrange : function()
32747     {
32748         if(!this.delegates.length){
32749             this.progressDialog.hide();
32750             this.refresh();
32751             return;
32752         }
32753         
32754         var delegate = this.delegates.shift();
32755         
32756         this.progressDialog.show();
32757         
32758         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32759         
32760         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32761         
32762         delegate();
32763     },
32764     
32765     refresh : function()
32766     {
32767         this.uploader.show();
32768         
32769         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32770             this.uploader.hide();
32771         }
32772         
32773         Roo.isTouch ? this.closable(false) : this.closable(true);
32774         
32775         this.fireEvent('refresh', this);
32776     },
32777     
32778     onRemove : function(e, el, o)
32779     {
32780         e.preventDefault();
32781         
32782         this.fireEvent('remove', this, o);
32783         
32784     },
32785     
32786     remove : function(o)
32787     {
32788         var files = [];
32789         
32790         Roo.each(this.files, function(file){
32791             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32792                 files.push(file);
32793                 return;
32794             }
32795
32796             o.target.remove();
32797
32798         }, this);
32799         
32800         this.files = files;
32801         
32802         this.refresh();
32803     },
32804     
32805     clear : function()
32806     {
32807         Roo.each(this.files, function(file){
32808             if(!file.target){
32809                 return;
32810             }
32811             
32812             file.target.remove();
32813
32814         }, this);
32815         
32816         this.files = [];
32817         
32818         this.refresh();
32819     },
32820     
32821     onClick : function(e, el, o)
32822     {
32823         e.preventDefault();
32824         
32825         this.fireEvent('click', this, o);
32826         
32827     },
32828     
32829     closable : function(closable)
32830     {
32831         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32832             
32833             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32834             
32835             if(closable){
32836                 el.show();
32837                 return;
32838             }
32839             
32840             el.hide();
32841             
32842         }, this);
32843     },
32844     
32845     xhrOnLoad : function(xhr)
32846     {
32847         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32848             el.remove();
32849         }, this);
32850         
32851         if (xhr.readyState !== 4) {
32852             this.arrange();
32853             this.fireEvent('exception', this, xhr);
32854             return;
32855         }
32856
32857         var response = Roo.decode(xhr.responseText);
32858         
32859         if(!response.success){
32860             this.arrange();
32861             this.fireEvent('exception', this, xhr);
32862             return;
32863         }
32864         
32865         var file = this.renderPreview(response.data);
32866         
32867         this.files.push(file);
32868         
32869         this.arrange();
32870         
32871         this.fireEvent('afterupload', this, xhr);
32872         
32873     },
32874     
32875     xhrOnError : function(xhr)
32876     {
32877         Roo.log('xhr on error');
32878         
32879         var response = Roo.decode(xhr.responseText);
32880           
32881         Roo.log(response);
32882         
32883         this.arrange();
32884     },
32885     
32886     process : function(file)
32887     {
32888         if(this.fireEvent('process', this, file) !== false){
32889             if(this.editable && file.type.indexOf('image') != -1){
32890                 this.fireEvent('edit', this, file);
32891                 return;
32892             }
32893
32894             this.uploadStart(file, false);
32895
32896             return;
32897         }
32898         
32899     },
32900     
32901     uploadStart : function(file, crop)
32902     {
32903         this.xhr = new XMLHttpRequest();
32904         
32905         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32906             this.arrange();
32907             return;
32908         }
32909         
32910         file.xhr = this.xhr;
32911             
32912         this.managerEl.createChild({
32913             tag : 'div',
32914             cls : 'roo-document-manager-loading',
32915             cn : [
32916                 {
32917                     tag : 'div',
32918                     tooltip : file.name,
32919                     cls : 'roo-document-manager-thumb',
32920                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32921                 }
32922             ]
32923
32924         });
32925
32926         this.xhr.open(this.method, this.url, true);
32927         
32928         var headers = {
32929             "Accept": "application/json",
32930             "Cache-Control": "no-cache",
32931             "X-Requested-With": "XMLHttpRequest"
32932         };
32933         
32934         for (var headerName in headers) {
32935             var headerValue = headers[headerName];
32936             if (headerValue) {
32937                 this.xhr.setRequestHeader(headerName, headerValue);
32938             }
32939         }
32940         
32941         var _this = this;
32942         
32943         this.xhr.onload = function()
32944         {
32945             _this.xhrOnLoad(_this.xhr);
32946         }
32947         
32948         this.xhr.onerror = function()
32949         {
32950             _this.xhrOnError(_this.xhr);
32951         }
32952         
32953         var formData = new FormData();
32954
32955         formData.append('returnHTML', 'NO');
32956         
32957         if(crop){
32958             formData.append('crop', crop);
32959         }
32960         
32961         formData.append(this.paramName, file, file.name);
32962         
32963         var options = {
32964             file : file, 
32965             manually : false
32966         };
32967         
32968         if(this.fireEvent('prepare', this, formData, options) != false){
32969             
32970             if(options.manually){
32971                 return;
32972             }
32973             
32974             this.xhr.send(formData);
32975             return;
32976         };
32977         
32978         this.uploadCancel();
32979     },
32980     
32981     uploadCancel : function()
32982     {
32983         if (this.xhr) {
32984             this.xhr.abort();
32985         }
32986         
32987         this.delegates = [];
32988         
32989         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32990             el.remove();
32991         }, this);
32992         
32993         this.arrange();
32994     },
32995     
32996     renderPreview : function(file)
32997     {
32998         if(typeof(file.target) != 'undefined' && file.target){
32999             return file;
33000         }
33001         
33002         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33003         
33004         var previewEl = this.managerEl.createChild({
33005             tag : 'div',
33006             cls : 'roo-document-manager-preview',
33007             cn : [
33008                 {
33009                     tag : 'div',
33010                     tooltip : file[this.toolTipName],
33011                     cls : 'roo-document-manager-thumb',
33012                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33013                 },
33014                 {
33015                     tag : 'button',
33016                     cls : 'close',
33017                     html : '<i class="fa fa-times-circle"></i>'
33018                 }
33019             ]
33020         });
33021
33022         var close = previewEl.select('button.close', true).first();
33023
33024         close.on('click', this.onRemove, this, file);
33025
33026         file.target = previewEl;
33027
33028         var image = previewEl.select('img', true).first();
33029         
33030         var _this = this;
33031         
33032         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33033         
33034         image.on('click', this.onClick, this, file);
33035         
33036         this.fireEvent('previewrendered', this, file);
33037         
33038         return file;
33039         
33040     },
33041     
33042     onPreviewLoad : function(file, image)
33043     {
33044         if(typeof(file.target) == 'undefined' || !file.target){
33045             return;
33046         }
33047         
33048         var width = image.dom.naturalWidth || image.dom.width;
33049         var height = image.dom.naturalHeight || image.dom.height;
33050         
33051         if(!this.previewResize) {
33052             return;
33053         }
33054         
33055         if(width > height){
33056             file.target.addClass('wide');
33057             return;
33058         }
33059         
33060         file.target.addClass('tall');
33061         return;
33062         
33063     },
33064     
33065     uploadFromSource : function(file, crop)
33066     {
33067         this.xhr = new XMLHttpRequest();
33068         
33069         this.managerEl.createChild({
33070             tag : 'div',
33071             cls : 'roo-document-manager-loading',
33072             cn : [
33073                 {
33074                     tag : 'div',
33075                     tooltip : file.name,
33076                     cls : 'roo-document-manager-thumb',
33077                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33078                 }
33079             ]
33080
33081         });
33082
33083         this.xhr.open(this.method, this.url, true);
33084         
33085         var headers = {
33086             "Accept": "application/json",
33087             "Cache-Control": "no-cache",
33088             "X-Requested-With": "XMLHttpRequest"
33089         };
33090         
33091         for (var headerName in headers) {
33092             var headerValue = headers[headerName];
33093             if (headerValue) {
33094                 this.xhr.setRequestHeader(headerName, headerValue);
33095             }
33096         }
33097         
33098         var _this = this;
33099         
33100         this.xhr.onload = function()
33101         {
33102             _this.xhrOnLoad(_this.xhr);
33103         }
33104         
33105         this.xhr.onerror = function()
33106         {
33107             _this.xhrOnError(_this.xhr);
33108         }
33109         
33110         var formData = new FormData();
33111
33112         formData.append('returnHTML', 'NO');
33113         
33114         formData.append('crop', crop);
33115         
33116         if(typeof(file.filename) != 'undefined'){
33117             formData.append('filename', file.filename);
33118         }
33119         
33120         if(typeof(file.mimetype) != 'undefined'){
33121             formData.append('mimetype', file.mimetype);
33122         }
33123         
33124         Roo.log(formData);
33125         
33126         if(this.fireEvent('prepare', this, formData) != false){
33127             this.xhr.send(formData);
33128         };
33129     }
33130 });
33131
33132 /*
33133 * Licence: LGPL
33134 */
33135
33136 /**
33137  * @class Roo.bootstrap.DocumentViewer
33138  * @extends Roo.bootstrap.Component
33139  * Bootstrap DocumentViewer class
33140  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33141  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33142  * 
33143  * @constructor
33144  * Create a new DocumentViewer
33145  * @param {Object} config The config object
33146  */
33147
33148 Roo.bootstrap.DocumentViewer = function(config){
33149     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33150     
33151     this.addEvents({
33152         /**
33153          * @event initial
33154          * Fire after initEvent
33155          * @param {Roo.bootstrap.DocumentViewer} this
33156          */
33157         "initial" : true,
33158         /**
33159          * @event click
33160          * Fire after click
33161          * @param {Roo.bootstrap.DocumentViewer} this
33162          */
33163         "click" : true,
33164         /**
33165          * @event download
33166          * Fire after download button
33167          * @param {Roo.bootstrap.DocumentViewer} this
33168          */
33169         "download" : true,
33170         /**
33171          * @event trash
33172          * Fire after trash button
33173          * @param {Roo.bootstrap.DocumentViewer} this
33174          */
33175         "trash" : true
33176         
33177     });
33178 };
33179
33180 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33181     
33182     showDownload : true,
33183     
33184     showTrash : true,
33185     
33186     getAutoCreate : function()
33187     {
33188         var cfg = {
33189             tag : 'div',
33190             cls : 'roo-document-viewer',
33191             cn : [
33192                 {
33193                     tag : 'div',
33194                     cls : 'roo-document-viewer-body',
33195                     cn : [
33196                         {
33197                             tag : 'div',
33198                             cls : 'roo-document-viewer-thumb',
33199                             cn : [
33200                                 {
33201                                     tag : 'img',
33202                                     cls : 'roo-document-viewer-image'
33203                                 }
33204                             ]
33205                         }
33206                     ]
33207                 },
33208                 {
33209                     tag : 'div',
33210                     cls : 'roo-document-viewer-footer',
33211                     cn : {
33212                         tag : 'div',
33213                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33214                         cn : [
33215                             {
33216                                 tag : 'div',
33217                                 cls : 'btn-group roo-document-viewer-download',
33218                                 cn : [
33219                                     {
33220                                         tag : 'button',
33221                                         cls : 'btn btn-default',
33222                                         html : '<i class="fa fa-download"></i>'
33223                                     }
33224                                 ]
33225                             },
33226                             {
33227                                 tag : 'div',
33228                                 cls : 'btn-group roo-document-viewer-trash',
33229                                 cn : [
33230                                     {
33231                                         tag : 'button',
33232                                         cls : 'btn btn-default',
33233                                         html : '<i class="fa fa-trash"></i>'
33234                                     }
33235                                 ]
33236                             }
33237                         ]
33238                     }
33239                 }
33240             ]
33241         };
33242         
33243         return cfg;
33244     },
33245     
33246     initEvents : function()
33247     {
33248         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33249         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33252         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33253         
33254         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33255         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33256         
33257         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33258         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33259         
33260         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33261         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33262         
33263         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33264         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33265         
33266         this.bodyEl.on('click', this.onClick, this);
33267         this.downloadBtn.on('click', this.onDownload, this);
33268         this.trashBtn.on('click', this.onTrash, this);
33269         
33270         this.downloadBtn.hide();
33271         this.trashBtn.hide();
33272         
33273         if(this.showDownload){
33274             this.downloadBtn.show();
33275         }
33276         
33277         if(this.showTrash){
33278             this.trashBtn.show();
33279         }
33280         
33281         if(!this.showDownload && !this.showTrash) {
33282             this.footerEl.hide();
33283         }
33284         
33285     },
33286     
33287     initial : function()
33288     {
33289         this.fireEvent('initial', this);
33290         
33291     },
33292     
33293     onClick : function(e)
33294     {
33295         e.preventDefault();
33296         
33297         this.fireEvent('click', this);
33298     },
33299     
33300     onDownload : function(e)
33301     {
33302         e.preventDefault();
33303         
33304         this.fireEvent('download', this);
33305     },
33306     
33307     onTrash : function(e)
33308     {
33309         e.preventDefault();
33310         
33311         this.fireEvent('trash', this);
33312     }
33313     
33314 });
33315 /*
33316  * - LGPL
33317  *
33318  * nav progress bar
33319  * 
33320  */
33321
33322 /**
33323  * @class Roo.bootstrap.NavProgressBar
33324  * @extends Roo.bootstrap.Component
33325  * Bootstrap NavProgressBar class
33326  * 
33327  * @constructor
33328  * Create a new nav progress bar
33329  * @param {Object} config The config object
33330  */
33331
33332 Roo.bootstrap.NavProgressBar = function(config){
33333     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33334
33335     this.bullets = this.bullets || [];
33336    
33337 //    Roo.bootstrap.NavProgressBar.register(this);
33338      this.addEvents({
33339         /**
33340              * @event changed
33341              * Fires when the active item changes
33342              * @param {Roo.bootstrap.NavProgressBar} this
33343              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33344              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33345          */
33346         'changed': true
33347      });
33348     
33349 };
33350
33351 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33352     
33353     bullets : [],
33354     barItems : [],
33355     
33356     getAutoCreate : function()
33357     {
33358         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33359         
33360         cfg = {
33361             tag : 'div',
33362             cls : 'roo-navigation-bar-group',
33363             cn : [
33364                 {
33365                     tag : 'div',
33366                     cls : 'roo-navigation-top-bar'
33367                 },
33368                 {
33369                     tag : 'div',
33370                     cls : 'roo-navigation-bullets-bar',
33371                     cn : [
33372                         {
33373                             tag : 'ul',
33374                             cls : 'roo-navigation-bar'
33375                         }
33376                     ]
33377                 },
33378                 
33379                 {
33380                     tag : 'div',
33381                     cls : 'roo-navigation-bottom-bar'
33382                 }
33383             ]
33384             
33385         };
33386         
33387         return cfg;
33388         
33389     },
33390     
33391     initEvents: function() 
33392     {
33393         
33394     },
33395     
33396     onRender : function(ct, position) 
33397     {
33398         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33399         
33400         if(this.bullets.length){
33401             Roo.each(this.bullets, function(b){
33402                this.addItem(b);
33403             }, this);
33404         }
33405         
33406         this.format();
33407         
33408     },
33409     
33410     addItem : function(cfg)
33411     {
33412         var item = new Roo.bootstrap.NavProgressItem(cfg);
33413         
33414         item.parentId = this.id;
33415         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33416         
33417         if(cfg.html){
33418             var top = new Roo.bootstrap.Element({
33419                 tag : 'div',
33420                 cls : 'roo-navigation-bar-text'
33421             });
33422             
33423             var bottom = new Roo.bootstrap.Element({
33424                 tag : 'div',
33425                 cls : 'roo-navigation-bar-text'
33426             });
33427             
33428             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33429             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33430             
33431             var topText = new Roo.bootstrap.Element({
33432                 tag : 'span',
33433                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33434             });
33435             
33436             var bottomText = new Roo.bootstrap.Element({
33437                 tag : 'span',
33438                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33439             });
33440             
33441             topText.onRender(top.el, null);
33442             bottomText.onRender(bottom.el, null);
33443             
33444             item.topEl = top;
33445             item.bottomEl = bottom;
33446         }
33447         
33448         this.barItems.push(item);
33449         
33450         return item;
33451     },
33452     
33453     getActive : function()
33454     {
33455         var active = false;
33456         
33457         Roo.each(this.barItems, function(v){
33458             
33459             if (!v.isActive()) {
33460                 return;
33461             }
33462             
33463             active = v;
33464             return false;
33465             
33466         });
33467         
33468         return active;
33469     },
33470     
33471     setActiveItem : function(item)
33472     {
33473         var prev = false;
33474         
33475         Roo.each(this.barItems, function(v){
33476             if (v.rid == item.rid) {
33477                 return ;
33478             }
33479             
33480             if (v.isActive()) {
33481                 v.setActive(false);
33482                 prev = v;
33483             }
33484         });
33485
33486         item.setActive(true);
33487         
33488         this.fireEvent('changed', this, item, prev);
33489     },
33490     
33491     getBarItem: function(rid)
33492     {
33493         var ret = false;
33494         
33495         Roo.each(this.barItems, function(e) {
33496             if (e.rid != rid) {
33497                 return;
33498             }
33499             
33500             ret =  e;
33501             return false;
33502         });
33503         
33504         return ret;
33505     },
33506     
33507     indexOfItem : function(item)
33508     {
33509         var index = false;
33510         
33511         Roo.each(this.barItems, function(v, i){
33512             
33513             if (v.rid != item.rid) {
33514                 return;
33515             }
33516             
33517             index = i;
33518             return false
33519         });
33520         
33521         return index;
33522     },
33523     
33524     setActiveNext : function()
33525     {
33526         var i = this.indexOfItem(this.getActive());
33527         
33528         if (i > this.barItems.length) {
33529             return;
33530         }
33531         
33532         this.setActiveItem(this.barItems[i+1]);
33533     },
33534     
33535     setActivePrev : function()
33536     {
33537         var i = this.indexOfItem(this.getActive());
33538         
33539         if (i  < 1) {
33540             return;
33541         }
33542         
33543         this.setActiveItem(this.barItems[i-1]);
33544     },
33545     
33546     format : function()
33547     {
33548         if(!this.barItems.length){
33549             return;
33550         }
33551      
33552         var width = 100 / this.barItems.length;
33553         
33554         Roo.each(this.barItems, function(i){
33555             i.el.setStyle('width', width + '%');
33556             i.topEl.el.setStyle('width', width + '%');
33557             i.bottomEl.el.setStyle('width', width + '%');
33558         }, this);
33559         
33560     }
33561     
33562 });
33563 /*
33564  * - LGPL
33565  *
33566  * Nav Progress Item
33567  * 
33568  */
33569
33570 /**
33571  * @class Roo.bootstrap.NavProgressItem
33572  * @extends Roo.bootstrap.Component
33573  * Bootstrap NavProgressItem class
33574  * @cfg {String} rid the reference id
33575  * @cfg {Boolean} active (true|false) Is item active default false
33576  * @cfg {Boolean} disabled (true|false) Is item active default false
33577  * @cfg {String} html
33578  * @cfg {String} position (top|bottom) text position default bottom
33579  * @cfg {String} icon show icon instead of number
33580  * 
33581  * @constructor
33582  * Create a new NavProgressItem
33583  * @param {Object} config The config object
33584  */
33585 Roo.bootstrap.NavProgressItem = function(config){
33586     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33587     this.addEvents({
33588         // raw events
33589         /**
33590          * @event click
33591          * The raw click event for the entire grid.
33592          * @param {Roo.bootstrap.NavProgressItem} this
33593          * @param {Roo.EventObject} e
33594          */
33595         "click" : true
33596     });
33597    
33598 };
33599
33600 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33601     
33602     rid : '',
33603     active : false,
33604     disabled : false,
33605     html : '',
33606     position : 'bottom',
33607     icon : false,
33608     
33609     getAutoCreate : function()
33610     {
33611         var iconCls = 'roo-navigation-bar-item-icon';
33612         
33613         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33614         
33615         var cfg = {
33616             tag: 'li',
33617             cls: 'roo-navigation-bar-item',
33618             cn : [
33619                 {
33620                     tag : 'i',
33621                     cls : iconCls
33622                 }
33623             ]
33624         };
33625         
33626         if(this.active){
33627             cfg.cls += ' active';
33628         }
33629         if(this.disabled){
33630             cfg.cls += ' disabled';
33631         }
33632         
33633         return cfg;
33634     },
33635     
33636     disable : function()
33637     {
33638         this.setDisabled(true);
33639     },
33640     
33641     enable : function()
33642     {
33643         this.setDisabled(false);
33644     },
33645     
33646     initEvents: function() 
33647     {
33648         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33649         
33650         this.iconEl.on('click', this.onClick, this);
33651     },
33652     
33653     onClick : function(e)
33654     {
33655         e.preventDefault();
33656         
33657         if(this.disabled){
33658             return;
33659         }
33660         
33661         if(this.fireEvent('click', this, e) === false){
33662             return;
33663         };
33664         
33665         this.parent().setActiveItem(this);
33666     },
33667     
33668     isActive: function () 
33669     {
33670         return this.active;
33671     },
33672     
33673     setActive : function(state)
33674     {
33675         if(this.active == state){
33676             return;
33677         }
33678         
33679         this.active = state;
33680         
33681         if (state) {
33682             this.el.addClass('active');
33683             return;
33684         }
33685         
33686         this.el.removeClass('active');
33687         
33688         return;
33689     },
33690     
33691     setDisabled : function(state)
33692     {
33693         if(this.disabled == state){
33694             return;
33695         }
33696         
33697         this.disabled = state;
33698         
33699         if (state) {
33700             this.el.addClass('disabled');
33701             return;
33702         }
33703         
33704         this.el.removeClass('disabled');
33705     },
33706     
33707     tooltipEl : function()
33708     {
33709         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33710     }
33711 });
33712  
33713
33714  /*
33715  * - LGPL
33716  *
33717  * FieldLabel
33718  * 
33719  */
33720
33721 /**
33722  * @class Roo.bootstrap.FieldLabel
33723  * @extends Roo.bootstrap.Component
33724  * Bootstrap FieldLabel class
33725  * @cfg {String} html contents of the element
33726  * @cfg {String} tag tag of the element default label
33727  * @cfg {String} cls class of the element
33728  * @cfg {String} target label target 
33729  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33730  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33731  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33732  * @cfg {String} iconTooltip default "This field is required"
33733  * @cfg {String} indicatorpos (left|right) default left
33734  * 
33735  * @constructor
33736  * Create a new FieldLabel
33737  * @param {Object} config The config object
33738  */
33739
33740 Roo.bootstrap.FieldLabel = function(config){
33741     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33742     
33743     this.addEvents({
33744             /**
33745              * @event invalid
33746              * Fires after the field has been marked as invalid.
33747              * @param {Roo.form.FieldLabel} this
33748              * @param {String} msg The validation message
33749              */
33750             invalid : true,
33751             /**
33752              * @event valid
33753              * Fires after the field has been validated with no errors.
33754              * @param {Roo.form.FieldLabel} this
33755              */
33756             valid : true
33757         });
33758 };
33759
33760 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33761     
33762     tag: 'label',
33763     cls: '',
33764     html: '',
33765     target: '',
33766     allowBlank : true,
33767     invalidClass : 'has-warning',
33768     validClass : 'has-success',
33769     iconTooltip : 'This field is required',
33770     indicatorpos : 'left',
33771     
33772     getAutoCreate : function(){
33773         
33774         var cls = "";
33775         if (!this.allowBlank) {
33776             cls  = "visible";
33777         }
33778         
33779         var cfg = {
33780             tag : this.tag,
33781             cls : 'roo-bootstrap-field-label ' + this.cls,
33782             for : this.target,
33783             cn : [
33784                 {
33785                     tag : 'i',
33786                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33787                     tooltip : this.iconTooltip
33788                 },
33789                 {
33790                     tag : 'span',
33791                     html : this.html
33792                 }
33793             ] 
33794         };
33795         
33796         if(this.indicatorpos == 'right'){
33797             var cfg = {
33798                 tag : this.tag,
33799                 cls : 'roo-bootstrap-field-label ' + this.cls,
33800                 for : this.target,
33801                 cn : [
33802                     {
33803                         tag : 'span',
33804                         html : this.html
33805                     },
33806                     {
33807                         tag : 'i',
33808                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33809                         tooltip : this.iconTooltip
33810                     }
33811                 ] 
33812             };
33813         }
33814         
33815         return cfg;
33816     },
33817     
33818     initEvents: function() 
33819     {
33820         Roo.bootstrap.Element.superclass.initEvents.call(this);
33821         
33822         this.indicator = this.indicatorEl();
33823         
33824         if(this.indicator){
33825             this.indicator.removeClass('visible');
33826             this.indicator.addClass('invisible');
33827         }
33828         
33829         Roo.bootstrap.FieldLabel.register(this);
33830     },
33831     
33832     indicatorEl : function()
33833     {
33834         var indicator = this.el.select('i.roo-required-indicator',true).first();
33835         
33836         if(!indicator){
33837             return false;
33838         }
33839         
33840         return indicator;
33841         
33842     },
33843     
33844     /**
33845      * Mark this field as valid
33846      */
33847     markValid : function()
33848     {
33849         if(this.indicator){
33850             this.indicator.removeClass('visible');
33851             this.indicator.addClass('invisible');
33852         }
33853         if (Roo.bootstrap.version == 3) {
33854             this.el.removeClass(this.invalidClass);
33855             this.el.addClass(this.validClass);
33856         } else {
33857             this.el.removeClass('is-invalid');
33858             this.el.addClass('is-valid');
33859         }
33860         
33861         
33862         this.fireEvent('valid', this);
33863     },
33864     
33865     /**
33866      * Mark this field as invalid
33867      * @param {String} msg The validation message
33868      */
33869     markInvalid : function(msg)
33870     {
33871         if(this.indicator){
33872             this.indicator.removeClass('invisible');
33873             this.indicator.addClass('visible');
33874         }
33875           if (Roo.bootstrap.version == 3) {
33876             this.el.removeClass(this.validClass);
33877             this.el.addClass(this.invalidClass);
33878         } else {
33879             this.el.removeClass('is-valid');
33880             this.el.addClass('is-invalid');
33881         }
33882         
33883         
33884         this.fireEvent('invalid', this, msg);
33885     }
33886     
33887    
33888 });
33889
33890 Roo.apply(Roo.bootstrap.FieldLabel, {
33891     
33892     groups: {},
33893     
33894      /**
33895     * register a FieldLabel Group
33896     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33897     */
33898     register : function(label)
33899     {
33900         if(this.groups.hasOwnProperty(label.target)){
33901             return;
33902         }
33903      
33904         this.groups[label.target] = label;
33905         
33906     },
33907     /**
33908     * fetch a FieldLabel Group based on the target
33909     * @param {string} target
33910     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33911     */
33912     get: function(target) {
33913         if (typeof(this.groups[target]) == 'undefined') {
33914             return false;
33915         }
33916         
33917         return this.groups[target] ;
33918     }
33919 });
33920
33921  
33922
33923  /*
33924  * - LGPL
33925  *
33926  * page DateSplitField.
33927  * 
33928  */
33929
33930
33931 /**
33932  * @class Roo.bootstrap.DateSplitField
33933  * @extends Roo.bootstrap.Component
33934  * Bootstrap DateSplitField class
33935  * @cfg {string} fieldLabel - the label associated
33936  * @cfg {Number} labelWidth set the width of label (0-12)
33937  * @cfg {String} labelAlign (top|left)
33938  * @cfg {Boolean} dayAllowBlank (true|false) default false
33939  * @cfg {Boolean} monthAllowBlank (true|false) default false
33940  * @cfg {Boolean} yearAllowBlank (true|false) default false
33941  * @cfg {string} dayPlaceholder 
33942  * @cfg {string} monthPlaceholder
33943  * @cfg {string} yearPlaceholder
33944  * @cfg {string} dayFormat default 'd'
33945  * @cfg {string} monthFormat default 'm'
33946  * @cfg {string} yearFormat default 'Y'
33947  * @cfg {Number} labellg set the width of label (1-12)
33948  * @cfg {Number} labelmd set the width of label (1-12)
33949  * @cfg {Number} labelsm set the width of label (1-12)
33950  * @cfg {Number} labelxs set the width of label (1-12)
33951
33952  *     
33953  * @constructor
33954  * Create a new DateSplitField
33955  * @param {Object} config The config object
33956  */
33957
33958 Roo.bootstrap.DateSplitField = function(config){
33959     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33960     
33961     this.addEvents({
33962         // raw events
33963          /**
33964          * @event years
33965          * getting the data of years
33966          * @param {Roo.bootstrap.DateSplitField} this
33967          * @param {Object} years
33968          */
33969         "years" : true,
33970         /**
33971          * @event days
33972          * getting the data of days
33973          * @param {Roo.bootstrap.DateSplitField} this
33974          * @param {Object} days
33975          */
33976         "days" : true,
33977         /**
33978          * @event invalid
33979          * Fires after the field has been marked as invalid.
33980          * @param {Roo.form.Field} this
33981          * @param {String} msg The validation message
33982          */
33983         invalid : true,
33984        /**
33985          * @event valid
33986          * Fires after the field has been validated with no errors.
33987          * @param {Roo.form.Field} this
33988          */
33989         valid : true
33990     });
33991 };
33992
33993 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33994     
33995     fieldLabel : '',
33996     labelAlign : 'top',
33997     labelWidth : 3,
33998     dayAllowBlank : false,
33999     monthAllowBlank : false,
34000     yearAllowBlank : false,
34001     dayPlaceholder : '',
34002     monthPlaceholder : '',
34003     yearPlaceholder : '',
34004     dayFormat : 'd',
34005     monthFormat : 'm',
34006     yearFormat : 'Y',
34007     isFormField : true,
34008     labellg : 0,
34009     labelmd : 0,
34010     labelsm : 0,
34011     labelxs : 0,
34012     
34013     getAutoCreate : function()
34014     {
34015         var cfg = {
34016             tag : 'div',
34017             cls : 'row roo-date-split-field-group',
34018             cn : [
34019                 {
34020                     tag : 'input',
34021                     type : 'hidden',
34022                     cls : 'form-hidden-field roo-date-split-field-group-value',
34023                     name : this.name
34024                 }
34025             ]
34026         };
34027         
34028         var labelCls = 'col-md-12';
34029         var contentCls = 'col-md-4';
34030         
34031         if(this.fieldLabel){
34032             
34033             var label = {
34034                 tag : 'div',
34035                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34036                 cn : [
34037                     {
34038                         tag : 'label',
34039                         html : this.fieldLabel
34040                     }
34041                 ]
34042             };
34043             
34044             if(this.labelAlign == 'left'){
34045             
34046                 if(this.labelWidth > 12){
34047                     label.style = "width: " + this.labelWidth + 'px';
34048                 }
34049
34050                 if(this.labelWidth < 13 && this.labelmd == 0){
34051                     this.labelmd = this.labelWidth;
34052                 }
34053
34054                 if(this.labellg > 0){
34055                     labelCls = ' col-lg-' + this.labellg;
34056                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34057                 }
34058
34059                 if(this.labelmd > 0){
34060                     labelCls = ' col-md-' + this.labelmd;
34061                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34062                 }
34063
34064                 if(this.labelsm > 0){
34065                     labelCls = ' col-sm-' + this.labelsm;
34066                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34067                 }
34068
34069                 if(this.labelxs > 0){
34070                     labelCls = ' col-xs-' + this.labelxs;
34071                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34072                 }
34073             }
34074             
34075             label.cls += ' ' + labelCls;
34076             
34077             cfg.cn.push(label);
34078         }
34079         
34080         Roo.each(['day', 'month', 'year'], function(t){
34081             cfg.cn.push({
34082                 tag : 'div',
34083                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34084             });
34085         }, this);
34086         
34087         return cfg;
34088     },
34089     
34090     inputEl: function ()
34091     {
34092         return this.el.select('.roo-date-split-field-group-value', true).first();
34093     },
34094     
34095     onRender : function(ct, position) 
34096     {
34097         var _this = this;
34098         
34099         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34100         
34101         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34102         
34103         this.dayField = new Roo.bootstrap.ComboBox({
34104             allowBlank : this.dayAllowBlank,
34105             alwaysQuery : true,
34106             displayField : 'value',
34107             editable : false,
34108             fieldLabel : '',
34109             forceSelection : true,
34110             mode : 'local',
34111             placeholder : this.dayPlaceholder,
34112             selectOnFocus : true,
34113             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34114             triggerAction : 'all',
34115             typeAhead : true,
34116             valueField : 'value',
34117             store : new Roo.data.SimpleStore({
34118                 data : (function() {    
34119                     var days = [];
34120                     _this.fireEvent('days', _this, days);
34121                     return days;
34122                 })(),
34123                 fields : [ 'value' ]
34124             }),
34125             listeners : {
34126                 select : function (_self, record, index)
34127                 {
34128                     _this.setValue(_this.getValue());
34129                 }
34130             }
34131         });
34132
34133         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34134         
34135         this.monthField = new Roo.bootstrap.MonthField({
34136             after : '<i class=\"fa fa-calendar\"></i>',
34137             allowBlank : this.monthAllowBlank,
34138             placeholder : this.monthPlaceholder,
34139             readOnly : true,
34140             listeners : {
34141                 render : function (_self)
34142                 {
34143                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34144                         e.preventDefault();
34145                         _self.focus();
34146                     });
34147                 },
34148                 select : function (_self, oldvalue, newvalue)
34149                 {
34150                     _this.setValue(_this.getValue());
34151                 }
34152             }
34153         });
34154         
34155         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34156         
34157         this.yearField = new Roo.bootstrap.ComboBox({
34158             allowBlank : this.yearAllowBlank,
34159             alwaysQuery : true,
34160             displayField : 'value',
34161             editable : false,
34162             fieldLabel : '',
34163             forceSelection : true,
34164             mode : 'local',
34165             placeholder : this.yearPlaceholder,
34166             selectOnFocus : true,
34167             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34168             triggerAction : 'all',
34169             typeAhead : true,
34170             valueField : 'value',
34171             store : new Roo.data.SimpleStore({
34172                 data : (function() {
34173                     var years = [];
34174                     _this.fireEvent('years', _this, years);
34175                     return years;
34176                 })(),
34177                 fields : [ 'value' ]
34178             }),
34179             listeners : {
34180                 select : function (_self, record, index)
34181                 {
34182                     _this.setValue(_this.getValue());
34183                 }
34184             }
34185         });
34186
34187         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34188     },
34189     
34190     setValue : function(v, format)
34191     {
34192         this.inputEl.dom.value = v;
34193         
34194         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34195         
34196         var d = Date.parseDate(v, f);
34197         
34198         if(!d){
34199             this.validate();
34200             return;
34201         }
34202         
34203         this.setDay(d.format(this.dayFormat));
34204         this.setMonth(d.format(this.monthFormat));
34205         this.setYear(d.format(this.yearFormat));
34206         
34207         this.validate();
34208         
34209         return;
34210     },
34211     
34212     setDay : function(v)
34213     {
34214         this.dayField.setValue(v);
34215         this.inputEl.dom.value = this.getValue();
34216         this.validate();
34217         return;
34218     },
34219     
34220     setMonth : function(v)
34221     {
34222         this.monthField.setValue(v, true);
34223         this.inputEl.dom.value = this.getValue();
34224         this.validate();
34225         return;
34226     },
34227     
34228     setYear : function(v)
34229     {
34230         this.yearField.setValue(v);
34231         this.inputEl.dom.value = this.getValue();
34232         this.validate();
34233         return;
34234     },
34235     
34236     getDay : function()
34237     {
34238         return this.dayField.getValue();
34239     },
34240     
34241     getMonth : function()
34242     {
34243         return this.monthField.getValue();
34244     },
34245     
34246     getYear : function()
34247     {
34248         return this.yearField.getValue();
34249     },
34250     
34251     getValue : function()
34252     {
34253         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34254         
34255         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34256         
34257         return date;
34258     },
34259     
34260     reset : function()
34261     {
34262         this.setDay('');
34263         this.setMonth('');
34264         this.setYear('');
34265         this.inputEl.dom.value = '';
34266         this.validate();
34267         return;
34268     },
34269     
34270     validate : function()
34271     {
34272         var d = this.dayField.validate();
34273         var m = this.monthField.validate();
34274         var y = this.yearField.validate();
34275         
34276         var valid = true;
34277         
34278         if(
34279                 (!this.dayAllowBlank && !d) ||
34280                 (!this.monthAllowBlank && !m) ||
34281                 (!this.yearAllowBlank && !y)
34282         ){
34283             valid = false;
34284         }
34285         
34286         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34287             return valid;
34288         }
34289         
34290         if(valid){
34291             this.markValid();
34292             return valid;
34293         }
34294         
34295         this.markInvalid();
34296         
34297         return valid;
34298     },
34299     
34300     markValid : function()
34301     {
34302         
34303         var label = this.el.select('label', true).first();
34304         var icon = this.el.select('i.fa-star', true).first();
34305
34306         if(label && icon){
34307             icon.remove();
34308         }
34309         
34310         this.fireEvent('valid', this);
34311     },
34312     
34313      /**
34314      * Mark this field as invalid
34315      * @param {String} msg The validation message
34316      */
34317     markInvalid : function(msg)
34318     {
34319         
34320         var label = this.el.select('label', true).first();
34321         var icon = this.el.select('i.fa-star', true).first();
34322
34323         if(label && !icon){
34324             this.el.select('.roo-date-split-field-label', true).createChild({
34325                 tag : 'i',
34326                 cls : 'text-danger fa fa-lg fa-star',
34327                 tooltip : 'This field is required',
34328                 style : 'margin-right:5px;'
34329             }, label, true);
34330         }
34331         
34332         this.fireEvent('invalid', this, msg);
34333     },
34334     
34335     clearInvalid : function()
34336     {
34337         var label = this.el.select('label', true).first();
34338         var icon = this.el.select('i.fa-star', true).first();
34339
34340         if(label && icon){
34341             icon.remove();
34342         }
34343         
34344         this.fireEvent('valid', this);
34345     },
34346     
34347     getName: function()
34348     {
34349         return this.name;
34350     }
34351     
34352 });
34353
34354  /**
34355  *
34356  * This is based on 
34357  * http://masonry.desandro.com
34358  *
34359  * The idea is to render all the bricks based on vertical width...
34360  *
34361  * The original code extends 'outlayer' - we might need to use that....
34362  * 
34363  */
34364
34365
34366 /**
34367  * @class Roo.bootstrap.LayoutMasonry
34368  * @extends Roo.bootstrap.Component
34369  * Bootstrap Layout Masonry class
34370  * 
34371  * @constructor
34372  * Create a new Element
34373  * @param {Object} config The config object
34374  */
34375
34376 Roo.bootstrap.LayoutMasonry = function(config){
34377     
34378     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34379     
34380     this.bricks = [];
34381     
34382     Roo.bootstrap.LayoutMasonry.register(this);
34383     
34384     this.addEvents({
34385         // raw events
34386         /**
34387          * @event layout
34388          * Fire after layout the items
34389          * @param {Roo.bootstrap.LayoutMasonry} this
34390          * @param {Roo.EventObject} e
34391          */
34392         "layout" : true
34393     });
34394     
34395 };
34396
34397 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34398     
34399     /**
34400      * @cfg {Boolean} isLayoutInstant = no animation?
34401      */   
34402     isLayoutInstant : false, // needed?
34403    
34404     /**
34405      * @cfg {Number} boxWidth  width of the columns
34406      */   
34407     boxWidth : 450,
34408     
34409       /**
34410      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34411      */   
34412     boxHeight : 0,
34413     
34414     /**
34415      * @cfg {Number} padWidth padding below box..
34416      */   
34417     padWidth : 10, 
34418     
34419     /**
34420      * @cfg {Number} gutter gutter width..
34421      */   
34422     gutter : 10,
34423     
34424      /**
34425      * @cfg {Number} maxCols maximum number of columns
34426      */   
34427     
34428     maxCols: 0,
34429     
34430     /**
34431      * @cfg {Boolean} isAutoInitial defalut true
34432      */   
34433     isAutoInitial : true, 
34434     
34435     containerWidth: 0,
34436     
34437     /**
34438      * @cfg {Boolean} isHorizontal defalut false
34439      */   
34440     isHorizontal : false, 
34441
34442     currentSize : null,
34443     
34444     tag: 'div',
34445     
34446     cls: '',
34447     
34448     bricks: null, //CompositeElement
34449     
34450     cols : 1,
34451     
34452     _isLayoutInited : false,
34453     
34454 //    isAlternative : false, // only use for vertical layout...
34455     
34456     /**
34457      * @cfg {Number} alternativePadWidth padding below box..
34458      */   
34459     alternativePadWidth : 50,
34460     
34461     selectedBrick : [],
34462     
34463     getAutoCreate : function(){
34464         
34465         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34466         
34467         var cfg = {
34468             tag: this.tag,
34469             cls: 'blog-masonary-wrapper ' + this.cls,
34470             cn : {
34471                 cls : 'mas-boxes masonary'
34472             }
34473         };
34474         
34475         return cfg;
34476     },
34477     
34478     getChildContainer: function( )
34479     {
34480         if (this.boxesEl) {
34481             return this.boxesEl;
34482         }
34483         
34484         this.boxesEl = this.el.select('.mas-boxes').first();
34485         
34486         return this.boxesEl;
34487     },
34488     
34489     
34490     initEvents : function()
34491     {
34492         var _this = this;
34493         
34494         if(this.isAutoInitial){
34495             Roo.log('hook children rendered');
34496             this.on('childrenrendered', function() {
34497                 Roo.log('children rendered');
34498                 _this.initial();
34499             } ,this);
34500         }
34501     },
34502     
34503     initial : function()
34504     {
34505         this.selectedBrick = [];
34506         
34507         this.currentSize = this.el.getBox(true);
34508         
34509         Roo.EventManager.onWindowResize(this.resize, this); 
34510
34511         if(!this.isAutoInitial){
34512             this.layout();
34513             return;
34514         }
34515         
34516         this.layout();
34517         
34518         return;
34519         //this.layout.defer(500,this);
34520         
34521     },
34522     
34523     resize : function()
34524     {
34525         var cs = this.el.getBox(true);
34526         
34527         if (
34528                 this.currentSize.width == cs.width && 
34529                 this.currentSize.x == cs.x && 
34530                 this.currentSize.height == cs.height && 
34531                 this.currentSize.y == cs.y 
34532         ) {
34533             Roo.log("no change in with or X or Y");
34534             return;
34535         }
34536         
34537         this.currentSize = cs;
34538         
34539         this.layout();
34540         
34541     },
34542     
34543     layout : function()
34544     {   
34545         this._resetLayout();
34546         
34547         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34548         
34549         this.layoutItems( isInstant );
34550       
34551         this._isLayoutInited = true;
34552         
34553         this.fireEvent('layout', this);
34554         
34555     },
34556     
34557     _resetLayout : function()
34558     {
34559         if(this.isHorizontal){
34560             this.horizontalMeasureColumns();
34561             return;
34562         }
34563         
34564         this.verticalMeasureColumns();
34565         
34566     },
34567     
34568     verticalMeasureColumns : function()
34569     {
34570         this.getContainerWidth();
34571         
34572 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34573 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34574 //            return;
34575 //        }
34576         
34577         var boxWidth = this.boxWidth + this.padWidth;
34578         
34579         if(this.containerWidth < this.boxWidth){
34580             boxWidth = this.containerWidth
34581         }
34582         
34583         var containerWidth = this.containerWidth;
34584         
34585         var cols = Math.floor(containerWidth / boxWidth);
34586         
34587         this.cols = Math.max( cols, 1 );
34588         
34589         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34590         
34591         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34592         
34593         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34594         
34595         this.colWidth = boxWidth + avail - this.padWidth;
34596         
34597         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34598         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34599     },
34600     
34601     horizontalMeasureColumns : function()
34602     {
34603         this.getContainerWidth();
34604         
34605         var boxWidth = this.boxWidth;
34606         
34607         if(this.containerWidth < boxWidth){
34608             boxWidth = this.containerWidth;
34609         }
34610         
34611         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34612         
34613         this.el.setHeight(boxWidth);
34614         
34615     },
34616     
34617     getContainerWidth : function()
34618     {
34619         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34620     },
34621     
34622     layoutItems : function( isInstant )
34623     {
34624         Roo.log(this.bricks);
34625         
34626         var items = Roo.apply([], this.bricks);
34627         
34628         if(this.isHorizontal){
34629             this._horizontalLayoutItems( items , isInstant );
34630             return;
34631         }
34632         
34633 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34634 //            this._verticalAlternativeLayoutItems( items , isInstant );
34635 //            return;
34636 //        }
34637         
34638         this._verticalLayoutItems( items , isInstant );
34639         
34640     },
34641     
34642     _verticalLayoutItems : function ( items , isInstant)
34643     {
34644         if ( !items || !items.length ) {
34645             return;
34646         }
34647         
34648         var standard = [
34649             ['xs', 'xs', 'xs', 'tall'],
34650             ['xs', 'xs', 'tall'],
34651             ['xs', 'xs', 'sm'],
34652             ['xs', 'xs', 'xs'],
34653             ['xs', 'tall'],
34654             ['xs', 'sm'],
34655             ['xs', 'xs'],
34656             ['xs'],
34657             
34658             ['sm', 'xs', 'xs'],
34659             ['sm', 'xs'],
34660             ['sm'],
34661             
34662             ['tall', 'xs', 'xs', 'xs'],
34663             ['tall', 'xs', 'xs'],
34664             ['tall', 'xs'],
34665             ['tall']
34666             
34667         ];
34668         
34669         var queue = [];
34670         
34671         var boxes = [];
34672         
34673         var box = [];
34674         
34675         Roo.each(items, function(item, k){
34676             
34677             switch (item.size) {
34678                 // these layouts take up a full box,
34679                 case 'md' :
34680                 case 'md-left' :
34681                 case 'md-right' :
34682                 case 'wide' :
34683                     
34684                     if(box.length){
34685                         boxes.push(box);
34686                         box = [];
34687                     }
34688                     
34689                     boxes.push([item]);
34690                     
34691                     break;
34692                     
34693                 case 'xs' :
34694                 case 'sm' :
34695                 case 'tall' :
34696                     
34697                     box.push(item);
34698                     
34699                     break;
34700                 default :
34701                     break;
34702                     
34703             }
34704             
34705         }, this);
34706         
34707         if(box.length){
34708             boxes.push(box);
34709             box = [];
34710         }
34711         
34712         var filterPattern = function(box, length)
34713         {
34714             if(!box.length){
34715                 return;
34716             }
34717             
34718             var match = false;
34719             
34720             var pattern = box.slice(0, length);
34721             
34722             var format = [];
34723             
34724             Roo.each(pattern, function(i){
34725                 format.push(i.size);
34726             }, this);
34727             
34728             Roo.each(standard, function(s){
34729                 
34730                 if(String(s) != String(format)){
34731                     return;
34732                 }
34733                 
34734                 match = true;
34735                 return false;
34736                 
34737             }, this);
34738             
34739             if(!match && length == 1){
34740                 return;
34741             }
34742             
34743             if(!match){
34744                 filterPattern(box, length - 1);
34745                 return;
34746             }
34747                 
34748             queue.push(pattern);
34749
34750             box = box.slice(length, box.length);
34751
34752             filterPattern(box, 4);
34753
34754             return;
34755             
34756         }
34757         
34758         Roo.each(boxes, function(box, k){
34759             
34760             if(!box.length){
34761                 return;
34762             }
34763             
34764             if(box.length == 1){
34765                 queue.push(box);
34766                 return;
34767             }
34768             
34769             filterPattern(box, 4);
34770             
34771         }, this);
34772         
34773         this._processVerticalLayoutQueue( queue, isInstant );
34774         
34775     },
34776     
34777 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34778 //    {
34779 //        if ( !items || !items.length ) {
34780 //            return;
34781 //        }
34782 //
34783 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34784 //        
34785 //    },
34786     
34787     _horizontalLayoutItems : function ( items , isInstant)
34788     {
34789         if ( !items || !items.length || items.length < 3) {
34790             return;
34791         }
34792         
34793         items.reverse();
34794         
34795         var eItems = items.slice(0, 3);
34796         
34797         items = items.slice(3, items.length);
34798         
34799         var standard = [
34800             ['xs', 'xs', 'xs', 'wide'],
34801             ['xs', 'xs', 'wide'],
34802             ['xs', 'xs', 'sm'],
34803             ['xs', 'xs', 'xs'],
34804             ['xs', 'wide'],
34805             ['xs', 'sm'],
34806             ['xs', 'xs'],
34807             ['xs'],
34808             
34809             ['sm', 'xs', 'xs'],
34810             ['sm', 'xs'],
34811             ['sm'],
34812             
34813             ['wide', 'xs', 'xs', 'xs'],
34814             ['wide', 'xs', 'xs'],
34815             ['wide', 'xs'],
34816             ['wide'],
34817             
34818             ['wide-thin']
34819         ];
34820         
34821         var queue = [];
34822         
34823         var boxes = [];
34824         
34825         var box = [];
34826         
34827         Roo.each(items, function(item, k){
34828             
34829             switch (item.size) {
34830                 case 'md' :
34831                 case 'md-left' :
34832                 case 'md-right' :
34833                 case 'tall' :
34834                     
34835                     if(box.length){
34836                         boxes.push(box);
34837                         box = [];
34838                     }
34839                     
34840                     boxes.push([item]);
34841                     
34842                     break;
34843                     
34844                 case 'xs' :
34845                 case 'sm' :
34846                 case 'wide' :
34847                 case 'wide-thin' :
34848                     
34849                     box.push(item);
34850                     
34851                     break;
34852                 default :
34853                     break;
34854                     
34855             }
34856             
34857         }, this);
34858         
34859         if(box.length){
34860             boxes.push(box);
34861             box = [];
34862         }
34863         
34864         var filterPattern = function(box, length)
34865         {
34866             if(!box.length){
34867                 return;
34868             }
34869             
34870             var match = false;
34871             
34872             var pattern = box.slice(0, length);
34873             
34874             var format = [];
34875             
34876             Roo.each(pattern, function(i){
34877                 format.push(i.size);
34878             }, this);
34879             
34880             Roo.each(standard, function(s){
34881                 
34882                 if(String(s) != String(format)){
34883                     return;
34884                 }
34885                 
34886                 match = true;
34887                 return false;
34888                 
34889             }, this);
34890             
34891             if(!match && length == 1){
34892                 return;
34893             }
34894             
34895             if(!match){
34896                 filterPattern(box, length - 1);
34897                 return;
34898             }
34899                 
34900             queue.push(pattern);
34901
34902             box = box.slice(length, box.length);
34903
34904             filterPattern(box, 4);
34905
34906             return;
34907             
34908         }
34909         
34910         Roo.each(boxes, function(box, k){
34911             
34912             if(!box.length){
34913                 return;
34914             }
34915             
34916             if(box.length == 1){
34917                 queue.push(box);
34918                 return;
34919             }
34920             
34921             filterPattern(box, 4);
34922             
34923         }, this);
34924         
34925         
34926         var prune = [];
34927         
34928         var pos = this.el.getBox(true);
34929         
34930         var minX = pos.x;
34931         
34932         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34933         
34934         var hit_end = false;
34935         
34936         Roo.each(queue, function(box){
34937             
34938             if(hit_end){
34939                 
34940                 Roo.each(box, function(b){
34941                 
34942                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34943                     b.el.hide();
34944
34945                 }, this);
34946
34947                 return;
34948             }
34949             
34950             var mx = 0;
34951             
34952             Roo.each(box, function(b){
34953                 
34954                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34955                 b.el.show();
34956
34957                 mx = Math.max(mx, b.x);
34958                 
34959             }, this);
34960             
34961             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34962             
34963             if(maxX < minX){
34964                 
34965                 Roo.each(box, function(b){
34966                 
34967                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34968                     b.el.hide();
34969                     
34970                 }, this);
34971                 
34972                 hit_end = true;
34973                 
34974                 return;
34975             }
34976             
34977             prune.push(box);
34978             
34979         }, this);
34980         
34981         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34982     },
34983     
34984     /** Sets position of item in DOM
34985     * @param {Element} item
34986     * @param {Number} x - horizontal position
34987     * @param {Number} y - vertical position
34988     * @param {Boolean} isInstant - disables transitions
34989     */
34990     _processVerticalLayoutQueue : function( queue, isInstant )
34991     {
34992         var pos = this.el.getBox(true);
34993         var x = pos.x;
34994         var y = pos.y;
34995         var maxY = [];
34996         
34997         for (var i = 0; i < this.cols; i++){
34998             maxY[i] = pos.y;
34999         }
35000         
35001         Roo.each(queue, function(box, k){
35002             
35003             var col = k % this.cols;
35004             
35005             Roo.each(box, function(b,kk){
35006                 
35007                 b.el.position('absolute');
35008                 
35009                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35010                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35011                 
35012                 if(b.size == 'md-left' || b.size == 'md-right'){
35013                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35014                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35015                 }
35016                 
35017                 b.el.setWidth(width);
35018                 b.el.setHeight(height);
35019                 // iframe?
35020                 b.el.select('iframe',true).setSize(width,height);
35021                 
35022             }, this);
35023             
35024             for (var i = 0; i < this.cols; i++){
35025                 
35026                 if(maxY[i] < maxY[col]){
35027                     col = i;
35028                     continue;
35029                 }
35030                 
35031                 col = Math.min(col, i);
35032                 
35033             }
35034             
35035             x = pos.x + col * (this.colWidth + this.padWidth);
35036             
35037             y = maxY[col];
35038             
35039             var positions = [];
35040             
35041             switch (box.length){
35042                 case 1 :
35043                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35044                     break;
35045                 case 2 :
35046                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35047                     break;
35048                 case 3 :
35049                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35050                     break;
35051                 case 4 :
35052                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35053                     break;
35054                 default :
35055                     break;
35056             }
35057             
35058             Roo.each(box, function(b,kk){
35059                 
35060                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35061                 
35062                 var sz = b.el.getSize();
35063                 
35064                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35065                 
35066             }, this);
35067             
35068         }, this);
35069         
35070         var mY = 0;
35071         
35072         for (var i = 0; i < this.cols; i++){
35073             mY = Math.max(mY, maxY[i]);
35074         }
35075         
35076         this.el.setHeight(mY - pos.y);
35077         
35078     },
35079     
35080 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35081 //    {
35082 //        var pos = this.el.getBox(true);
35083 //        var x = pos.x;
35084 //        var y = pos.y;
35085 //        var maxX = pos.right;
35086 //        
35087 //        var maxHeight = 0;
35088 //        
35089 //        Roo.each(items, function(item, k){
35090 //            
35091 //            var c = k % 2;
35092 //            
35093 //            item.el.position('absolute');
35094 //                
35095 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35096 //
35097 //            item.el.setWidth(width);
35098 //
35099 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35100 //
35101 //            item.el.setHeight(height);
35102 //            
35103 //            if(c == 0){
35104 //                item.el.setXY([x, y], isInstant ? false : true);
35105 //            } else {
35106 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35107 //            }
35108 //            
35109 //            y = y + height + this.alternativePadWidth;
35110 //            
35111 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35112 //            
35113 //        }, this);
35114 //        
35115 //        this.el.setHeight(maxHeight);
35116 //        
35117 //    },
35118     
35119     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35120     {
35121         var pos = this.el.getBox(true);
35122         
35123         var minX = pos.x;
35124         var minY = pos.y;
35125         
35126         var maxX = pos.right;
35127         
35128         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35129         
35130         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35131         
35132         Roo.each(queue, function(box, k){
35133             
35134             Roo.each(box, function(b, kk){
35135                 
35136                 b.el.position('absolute');
35137                 
35138                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35139                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35140                 
35141                 if(b.size == 'md-left' || b.size == 'md-right'){
35142                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35143                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35144                 }
35145                 
35146                 b.el.setWidth(width);
35147                 b.el.setHeight(height);
35148                 
35149             }, this);
35150             
35151             if(!box.length){
35152                 return;
35153             }
35154             
35155             var positions = [];
35156             
35157             switch (box.length){
35158                 case 1 :
35159                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35160                     break;
35161                 case 2 :
35162                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35163                     break;
35164                 case 3 :
35165                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35166                     break;
35167                 case 4 :
35168                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35169                     break;
35170                 default :
35171                     break;
35172             }
35173             
35174             Roo.each(box, function(b,kk){
35175                 
35176                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35177                 
35178                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35179                 
35180             }, this);
35181             
35182         }, this);
35183         
35184     },
35185     
35186     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35187     {
35188         Roo.each(eItems, function(b,k){
35189             
35190             b.size = (k == 0) ? 'sm' : 'xs';
35191             b.x = (k == 0) ? 2 : 1;
35192             b.y = (k == 0) ? 2 : 1;
35193             
35194             b.el.position('absolute');
35195             
35196             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35197                 
35198             b.el.setWidth(width);
35199             
35200             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35201             
35202             b.el.setHeight(height);
35203             
35204         }, this);
35205
35206         var positions = [];
35207         
35208         positions.push({
35209             x : maxX - this.unitWidth * 2 - this.gutter,
35210             y : minY
35211         });
35212         
35213         positions.push({
35214             x : maxX - this.unitWidth,
35215             y : minY + (this.unitWidth + this.gutter) * 2
35216         });
35217         
35218         positions.push({
35219             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35220             y : minY
35221         });
35222         
35223         Roo.each(eItems, function(b,k){
35224             
35225             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35226
35227         }, this);
35228         
35229     },
35230     
35231     getVerticalOneBoxColPositions : function(x, y, box)
35232     {
35233         var pos = [];
35234         
35235         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35236         
35237         if(box[0].size == 'md-left'){
35238             rand = 0;
35239         }
35240         
35241         if(box[0].size == 'md-right'){
35242             rand = 1;
35243         }
35244         
35245         pos.push({
35246             x : x + (this.unitWidth + this.gutter) * rand,
35247             y : y
35248         });
35249         
35250         return pos;
35251     },
35252     
35253     getVerticalTwoBoxColPositions : function(x, y, box)
35254     {
35255         var pos = [];
35256         
35257         if(box[0].size == 'xs'){
35258             
35259             pos.push({
35260                 x : x,
35261                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35262             });
35263
35264             pos.push({
35265                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35266                 y : y
35267             });
35268             
35269             return pos;
35270             
35271         }
35272         
35273         pos.push({
35274             x : x,
35275             y : y
35276         });
35277
35278         pos.push({
35279             x : x + (this.unitWidth + this.gutter) * 2,
35280             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35281         });
35282         
35283         return pos;
35284         
35285     },
35286     
35287     getVerticalThreeBoxColPositions : function(x, y, box)
35288     {
35289         var pos = [];
35290         
35291         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35292             
35293             pos.push({
35294                 x : x,
35295                 y : y
35296             });
35297
35298             pos.push({
35299                 x : x + (this.unitWidth + this.gutter) * 1,
35300                 y : y
35301             });
35302             
35303             pos.push({
35304                 x : x + (this.unitWidth + this.gutter) * 2,
35305                 y : y
35306             });
35307             
35308             return pos;
35309             
35310         }
35311         
35312         if(box[0].size == 'xs' && box[1].size == 'xs'){
35313             
35314             pos.push({
35315                 x : x,
35316                 y : y
35317             });
35318
35319             pos.push({
35320                 x : x,
35321                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35322             });
35323             
35324             pos.push({
35325                 x : x + (this.unitWidth + this.gutter) * 1,
35326                 y : y
35327             });
35328             
35329             return pos;
35330             
35331         }
35332         
35333         pos.push({
35334             x : x,
35335             y : y
35336         });
35337
35338         pos.push({
35339             x : x + (this.unitWidth + this.gutter) * 2,
35340             y : y
35341         });
35342
35343         pos.push({
35344             x : x + (this.unitWidth + this.gutter) * 2,
35345             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35346         });
35347             
35348         return pos;
35349         
35350     },
35351     
35352     getVerticalFourBoxColPositions : function(x, y, box)
35353     {
35354         var pos = [];
35355         
35356         if(box[0].size == 'xs'){
35357             
35358             pos.push({
35359                 x : x,
35360                 y : y
35361             });
35362
35363             pos.push({
35364                 x : x,
35365                 y : y + (this.unitHeight + this.gutter) * 1
35366             });
35367             
35368             pos.push({
35369                 x : x,
35370                 y : y + (this.unitHeight + this.gutter) * 2
35371             });
35372             
35373             pos.push({
35374                 x : x + (this.unitWidth + this.gutter) * 1,
35375                 y : y
35376             });
35377             
35378             return pos;
35379             
35380         }
35381         
35382         pos.push({
35383             x : x,
35384             y : y
35385         });
35386
35387         pos.push({
35388             x : x + (this.unitWidth + this.gutter) * 2,
35389             y : y
35390         });
35391
35392         pos.push({
35393             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35394             y : y + (this.unitHeight + this.gutter) * 1
35395         });
35396
35397         pos.push({
35398             x : x + (this.unitWidth + this.gutter) * 2,
35399             y : y + (this.unitWidth + this.gutter) * 2
35400         });
35401
35402         return pos;
35403         
35404     },
35405     
35406     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35407     {
35408         var pos = [];
35409         
35410         if(box[0].size == 'md-left'){
35411             pos.push({
35412                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35413                 y : minY
35414             });
35415             
35416             return pos;
35417         }
35418         
35419         if(box[0].size == 'md-right'){
35420             pos.push({
35421                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35422                 y : minY + (this.unitWidth + this.gutter) * 1
35423             });
35424             
35425             return pos;
35426         }
35427         
35428         var rand = Math.floor(Math.random() * (4 - box[0].y));
35429         
35430         pos.push({
35431             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35432             y : minY + (this.unitWidth + this.gutter) * rand
35433         });
35434         
35435         return pos;
35436         
35437     },
35438     
35439     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35440     {
35441         var pos = [];
35442         
35443         if(box[0].size == 'xs'){
35444             
35445             pos.push({
35446                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35447                 y : minY
35448             });
35449
35450             pos.push({
35451                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35452                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35453             });
35454             
35455             return pos;
35456             
35457         }
35458         
35459         pos.push({
35460             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35461             y : minY
35462         });
35463
35464         pos.push({
35465             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35466             y : minY + (this.unitWidth + this.gutter) * 2
35467         });
35468         
35469         return pos;
35470         
35471     },
35472     
35473     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35474     {
35475         var pos = [];
35476         
35477         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35478             
35479             pos.push({
35480                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35481                 y : minY
35482             });
35483
35484             pos.push({
35485                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35486                 y : minY + (this.unitWidth + this.gutter) * 1
35487             });
35488             
35489             pos.push({
35490                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35491                 y : minY + (this.unitWidth + this.gutter) * 2
35492             });
35493             
35494             return pos;
35495             
35496         }
35497         
35498         if(box[0].size == 'xs' && box[1].size == 'xs'){
35499             
35500             pos.push({
35501                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35502                 y : minY
35503             });
35504
35505             pos.push({
35506                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35507                 y : minY
35508             });
35509             
35510             pos.push({
35511                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35512                 y : minY + (this.unitWidth + this.gutter) * 1
35513             });
35514             
35515             return pos;
35516             
35517         }
35518         
35519         pos.push({
35520             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35521             y : minY
35522         });
35523
35524         pos.push({
35525             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35526             y : minY + (this.unitWidth + this.gutter) * 2
35527         });
35528
35529         pos.push({
35530             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35531             y : minY + (this.unitWidth + this.gutter) * 2
35532         });
35533             
35534         return pos;
35535         
35536     },
35537     
35538     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35539     {
35540         var pos = [];
35541         
35542         if(box[0].size == 'xs'){
35543             
35544             pos.push({
35545                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35546                 y : minY
35547             });
35548
35549             pos.push({
35550                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35551                 y : minY
35552             });
35553             
35554             pos.push({
35555                 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),
35556                 y : minY
35557             });
35558             
35559             pos.push({
35560                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35561                 y : minY + (this.unitWidth + this.gutter) * 1
35562             });
35563             
35564             return pos;
35565             
35566         }
35567         
35568         pos.push({
35569             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35570             y : minY
35571         });
35572         
35573         pos.push({
35574             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35575             y : minY + (this.unitWidth + this.gutter) * 2
35576         });
35577         
35578         pos.push({
35579             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35580             y : minY + (this.unitWidth + this.gutter) * 2
35581         });
35582         
35583         pos.push({
35584             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),
35585             y : minY + (this.unitWidth + this.gutter) * 2
35586         });
35587
35588         return pos;
35589         
35590     },
35591     
35592     /**
35593     * remove a Masonry Brick
35594     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35595     */
35596     removeBrick : function(brick_id)
35597     {
35598         if (!brick_id) {
35599             return;
35600         }
35601         
35602         for (var i = 0; i<this.bricks.length; i++) {
35603             if (this.bricks[i].id == brick_id) {
35604                 this.bricks.splice(i,1);
35605                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35606                 this.initial();
35607             }
35608         }
35609     },
35610     
35611     /**
35612     * adds a Masonry Brick
35613     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35614     */
35615     addBrick : function(cfg)
35616     {
35617         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35618         //this.register(cn);
35619         cn.parentId = this.id;
35620         cn.render(this.el);
35621         return cn;
35622     },
35623     
35624     /**
35625     * register a Masonry Brick
35626     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35627     */
35628     
35629     register : function(brick)
35630     {
35631         this.bricks.push(brick);
35632         brick.masonryId = this.id;
35633     },
35634     
35635     /**
35636     * clear all the Masonry Brick
35637     */
35638     clearAll : function()
35639     {
35640         this.bricks = [];
35641         //this.getChildContainer().dom.innerHTML = "";
35642         this.el.dom.innerHTML = '';
35643     },
35644     
35645     getSelected : function()
35646     {
35647         if (!this.selectedBrick) {
35648             return false;
35649         }
35650         
35651         return this.selectedBrick;
35652     }
35653 });
35654
35655 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35656     
35657     groups: {},
35658      /**
35659     * register a Masonry Layout
35660     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35661     */
35662     
35663     register : function(layout)
35664     {
35665         this.groups[layout.id] = layout;
35666     },
35667     /**
35668     * fetch a  Masonry Layout based on the masonry layout ID
35669     * @param {string} the masonry layout to add
35670     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35671     */
35672     
35673     get: function(layout_id) {
35674         if (typeof(this.groups[layout_id]) == 'undefined') {
35675             return false;
35676         }
35677         return this.groups[layout_id] ;
35678     }
35679     
35680     
35681     
35682 });
35683
35684  
35685
35686  /**
35687  *
35688  * This is based on 
35689  * http://masonry.desandro.com
35690  *
35691  * The idea is to render all the bricks based on vertical width...
35692  *
35693  * The original code extends 'outlayer' - we might need to use that....
35694  * 
35695  */
35696
35697
35698 /**
35699  * @class Roo.bootstrap.LayoutMasonryAuto
35700  * @extends Roo.bootstrap.Component
35701  * Bootstrap Layout Masonry class
35702  * 
35703  * @constructor
35704  * Create a new Element
35705  * @param {Object} config The config object
35706  */
35707
35708 Roo.bootstrap.LayoutMasonryAuto = function(config){
35709     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35710 };
35711
35712 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35713     
35714       /**
35715      * @cfg {Boolean} isFitWidth  - resize the width..
35716      */   
35717     isFitWidth : false,  // options..
35718     /**
35719      * @cfg {Boolean} isOriginLeft = left align?
35720      */   
35721     isOriginLeft : true,
35722     /**
35723      * @cfg {Boolean} isOriginTop = top align?
35724      */   
35725     isOriginTop : false,
35726     /**
35727      * @cfg {Boolean} isLayoutInstant = no animation?
35728      */   
35729     isLayoutInstant : false, // needed?
35730     /**
35731      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35732      */   
35733     isResizingContainer : true,
35734     /**
35735      * @cfg {Number} columnWidth  width of the columns 
35736      */   
35737     
35738     columnWidth : 0,
35739     
35740     /**
35741      * @cfg {Number} maxCols maximum number of columns
35742      */   
35743     
35744     maxCols: 0,
35745     /**
35746      * @cfg {Number} padHeight padding below box..
35747      */   
35748     
35749     padHeight : 10, 
35750     
35751     /**
35752      * @cfg {Boolean} isAutoInitial defalut true
35753      */   
35754     
35755     isAutoInitial : true, 
35756     
35757     // private?
35758     gutter : 0,
35759     
35760     containerWidth: 0,
35761     initialColumnWidth : 0,
35762     currentSize : null,
35763     
35764     colYs : null, // array.
35765     maxY : 0,
35766     padWidth: 10,
35767     
35768     
35769     tag: 'div',
35770     cls: '',
35771     bricks: null, //CompositeElement
35772     cols : 0, // array?
35773     // element : null, // wrapped now this.el
35774     _isLayoutInited : null, 
35775     
35776     
35777     getAutoCreate : function(){
35778         
35779         var cfg = {
35780             tag: this.tag,
35781             cls: 'blog-masonary-wrapper ' + this.cls,
35782             cn : {
35783                 cls : 'mas-boxes masonary'
35784             }
35785         };
35786         
35787         return cfg;
35788     },
35789     
35790     getChildContainer: function( )
35791     {
35792         if (this.boxesEl) {
35793             return this.boxesEl;
35794         }
35795         
35796         this.boxesEl = this.el.select('.mas-boxes').first();
35797         
35798         return this.boxesEl;
35799     },
35800     
35801     
35802     initEvents : function()
35803     {
35804         var _this = this;
35805         
35806         if(this.isAutoInitial){
35807             Roo.log('hook children rendered');
35808             this.on('childrenrendered', function() {
35809                 Roo.log('children rendered');
35810                 _this.initial();
35811             } ,this);
35812         }
35813         
35814     },
35815     
35816     initial : function()
35817     {
35818         this.reloadItems();
35819
35820         this.currentSize = this.el.getBox(true);
35821
35822         /// was window resize... - let's see if this works..
35823         Roo.EventManager.onWindowResize(this.resize, this); 
35824
35825         if(!this.isAutoInitial){
35826             this.layout();
35827             return;
35828         }
35829         
35830         this.layout.defer(500,this);
35831     },
35832     
35833     reloadItems: function()
35834     {
35835         this.bricks = this.el.select('.masonry-brick', true);
35836         
35837         this.bricks.each(function(b) {
35838             //Roo.log(b.getSize());
35839             if (!b.attr('originalwidth')) {
35840                 b.attr('originalwidth',  b.getSize().width);
35841             }
35842             
35843         });
35844         
35845         Roo.log(this.bricks.elements.length);
35846     },
35847     
35848     resize : function()
35849     {
35850         Roo.log('resize');
35851         var cs = this.el.getBox(true);
35852         
35853         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35854             Roo.log("no change in with or X");
35855             return;
35856         }
35857         this.currentSize = cs;
35858         this.layout();
35859     },
35860     
35861     layout : function()
35862     {
35863          Roo.log('layout');
35864         this._resetLayout();
35865         //this._manageStamps();
35866       
35867         // don't animate first layout
35868         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35869         this.layoutItems( isInstant );
35870       
35871         // flag for initalized
35872         this._isLayoutInited = true;
35873     },
35874     
35875     layoutItems : function( isInstant )
35876     {
35877         //var items = this._getItemsForLayout( this.items );
35878         // original code supports filtering layout items.. we just ignore it..
35879         
35880         this._layoutItems( this.bricks , isInstant );
35881       
35882         this._postLayout();
35883     },
35884     _layoutItems : function ( items , isInstant)
35885     {
35886        //this.fireEvent( 'layout', this, items );
35887     
35888
35889         if ( !items || !items.elements.length ) {
35890           // no items, emit event with empty array
35891             return;
35892         }
35893
35894         var queue = [];
35895         items.each(function(item) {
35896             Roo.log("layout item");
35897             Roo.log(item);
35898             // get x/y object from method
35899             var position = this._getItemLayoutPosition( item );
35900             // enqueue
35901             position.item = item;
35902             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35903             queue.push( position );
35904         }, this);
35905       
35906         this._processLayoutQueue( queue );
35907     },
35908     /** Sets position of item in DOM
35909     * @param {Element} item
35910     * @param {Number} x - horizontal position
35911     * @param {Number} y - vertical position
35912     * @param {Boolean} isInstant - disables transitions
35913     */
35914     _processLayoutQueue : function( queue )
35915     {
35916         for ( var i=0, len = queue.length; i < len; i++ ) {
35917             var obj = queue[i];
35918             obj.item.position('absolute');
35919             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35920         }
35921     },
35922       
35923     
35924     /**
35925     * Any logic you want to do after each layout,
35926     * i.e. size the container
35927     */
35928     _postLayout : function()
35929     {
35930         this.resizeContainer();
35931     },
35932     
35933     resizeContainer : function()
35934     {
35935         if ( !this.isResizingContainer ) {
35936             return;
35937         }
35938         var size = this._getContainerSize();
35939         if ( size ) {
35940             this.el.setSize(size.width,size.height);
35941             this.boxesEl.setSize(size.width,size.height);
35942         }
35943     },
35944     
35945     
35946     
35947     _resetLayout : function()
35948     {
35949         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35950         this.colWidth = this.el.getWidth();
35951         //this.gutter = this.el.getWidth(); 
35952         
35953         this.measureColumns();
35954
35955         // reset column Y
35956         var i = this.cols;
35957         this.colYs = [];
35958         while (i--) {
35959             this.colYs.push( 0 );
35960         }
35961     
35962         this.maxY = 0;
35963     },
35964
35965     measureColumns : function()
35966     {
35967         this.getContainerWidth();
35968       // if columnWidth is 0, default to outerWidth of first item
35969         if ( !this.columnWidth ) {
35970             var firstItem = this.bricks.first();
35971             Roo.log(firstItem);
35972             this.columnWidth  = this.containerWidth;
35973             if (firstItem && firstItem.attr('originalwidth') ) {
35974                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35975             }
35976             // columnWidth fall back to item of first element
35977             Roo.log("set column width?");
35978                         this.initialColumnWidth = this.columnWidth  ;
35979
35980             // if first elem has no width, default to size of container
35981             
35982         }
35983         
35984         
35985         if (this.initialColumnWidth) {
35986             this.columnWidth = this.initialColumnWidth;
35987         }
35988         
35989         
35990             
35991         // column width is fixed at the top - however if container width get's smaller we should
35992         // reduce it...
35993         
35994         // this bit calcs how man columns..
35995             
35996         var columnWidth = this.columnWidth += this.gutter;
35997       
35998         // calculate columns
35999         var containerWidth = this.containerWidth + this.gutter;
36000         
36001         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36002         // fix rounding errors, typically with gutters
36003         var excess = columnWidth - containerWidth % columnWidth;
36004         
36005         
36006         // if overshoot is less than a pixel, round up, otherwise floor it
36007         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36008         cols = Math[ mathMethod ]( cols );
36009         this.cols = Math.max( cols, 1 );
36010         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36011         
36012          // padding positioning..
36013         var totalColWidth = this.cols * this.columnWidth;
36014         var padavail = this.containerWidth - totalColWidth;
36015         // so for 2 columns - we need 3 'pads'
36016         
36017         var padNeeded = (1+this.cols) * this.padWidth;
36018         
36019         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36020         
36021         this.columnWidth += padExtra
36022         //this.padWidth = Math.floor(padavail /  ( this.cols));
36023         
36024         // adjust colum width so that padding is fixed??
36025         
36026         // we have 3 columns ... total = width * 3
36027         // we have X left over... that should be used by 
36028         
36029         //if (this.expandC) {
36030             
36031         //}
36032         
36033         
36034         
36035     },
36036     
36037     getContainerWidth : function()
36038     {
36039        /* // container is parent if fit width
36040         var container = this.isFitWidth ? this.element.parentNode : this.element;
36041         // check that this.size and size are there
36042         // IE8 triggers resize on body size change, so they might not be
36043         
36044         var size = getSize( container );  //FIXME
36045         this.containerWidth = size && size.innerWidth; //FIXME
36046         */
36047          
36048         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36049         
36050     },
36051     
36052     _getItemLayoutPosition : function( item )  // what is item?
36053     {
36054         // we resize the item to our columnWidth..
36055       
36056         item.setWidth(this.columnWidth);
36057         item.autoBoxAdjust  = false;
36058         
36059         var sz = item.getSize();
36060  
36061         // how many columns does this brick span
36062         var remainder = this.containerWidth % this.columnWidth;
36063         
36064         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36065         // round if off by 1 pixel, otherwise use ceil
36066         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36067         colSpan = Math.min( colSpan, this.cols );
36068         
36069         // normally this should be '1' as we dont' currently allow multi width columns..
36070         
36071         var colGroup = this._getColGroup( colSpan );
36072         // get the minimum Y value from the columns
36073         var minimumY = Math.min.apply( Math, colGroup );
36074         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36075         
36076         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36077          
36078         // position the brick
36079         var position = {
36080             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36081             y: this.currentSize.y + minimumY + this.padHeight
36082         };
36083         
36084         Roo.log(position);
36085         // apply setHeight to necessary columns
36086         var setHeight = minimumY + sz.height + this.padHeight;
36087         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36088         
36089         var setSpan = this.cols + 1 - colGroup.length;
36090         for ( var i = 0; i < setSpan; i++ ) {
36091           this.colYs[ shortColIndex + i ] = setHeight ;
36092         }
36093       
36094         return position;
36095     },
36096     
36097     /**
36098      * @param {Number} colSpan - number of columns the element spans
36099      * @returns {Array} colGroup
36100      */
36101     _getColGroup : function( colSpan )
36102     {
36103         if ( colSpan < 2 ) {
36104           // if brick spans only one column, use all the column Ys
36105           return this.colYs;
36106         }
36107       
36108         var colGroup = [];
36109         // how many different places could this brick fit horizontally
36110         var groupCount = this.cols + 1 - colSpan;
36111         // for each group potential horizontal position
36112         for ( var i = 0; i < groupCount; i++ ) {
36113           // make an array of colY values for that one group
36114           var groupColYs = this.colYs.slice( i, i + colSpan );
36115           // and get the max value of the array
36116           colGroup[i] = Math.max.apply( Math, groupColYs );
36117         }
36118         return colGroup;
36119     },
36120     /*
36121     _manageStamp : function( stamp )
36122     {
36123         var stampSize =  stamp.getSize();
36124         var offset = stamp.getBox();
36125         // get the columns that this stamp affects
36126         var firstX = this.isOriginLeft ? offset.x : offset.right;
36127         var lastX = firstX + stampSize.width;
36128         var firstCol = Math.floor( firstX / this.columnWidth );
36129         firstCol = Math.max( 0, firstCol );
36130         
36131         var lastCol = Math.floor( lastX / this.columnWidth );
36132         // lastCol should not go over if multiple of columnWidth #425
36133         lastCol -= lastX % this.columnWidth ? 0 : 1;
36134         lastCol = Math.min( this.cols - 1, lastCol );
36135         
36136         // set colYs to bottom of the stamp
36137         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36138             stampSize.height;
36139             
36140         for ( var i = firstCol; i <= lastCol; i++ ) {
36141           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36142         }
36143     },
36144     */
36145     
36146     _getContainerSize : function()
36147     {
36148         this.maxY = Math.max.apply( Math, this.colYs );
36149         var size = {
36150             height: this.maxY
36151         };
36152       
36153         if ( this.isFitWidth ) {
36154             size.width = this._getContainerFitWidth();
36155         }
36156       
36157         return size;
36158     },
36159     
36160     _getContainerFitWidth : function()
36161     {
36162         var unusedCols = 0;
36163         // count unused columns
36164         var i = this.cols;
36165         while ( --i ) {
36166           if ( this.colYs[i] !== 0 ) {
36167             break;
36168           }
36169           unusedCols++;
36170         }
36171         // fit container to columns that have been used
36172         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36173     },
36174     
36175     needsResizeLayout : function()
36176     {
36177         var previousWidth = this.containerWidth;
36178         this.getContainerWidth();
36179         return previousWidth !== this.containerWidth;
36180     }
36181  
36182 });
36183
36184  
36185
36186  /*
36187  * - LGPL
36188  *
36189  * element
36190  * 
36191  */
36192
36193 /**
36194  * @class Roo.bootstrap.MasonryBrick
36195  * @extends Roo.bootstrap.Component
36196  * Bootstrap MasonryBrick class
36197  * 
36198  * @constructor
36199  * Create a new MasonryBrick
36200  * @param {Object} config The config object
36201  */
36202
36203 Roo.bootstrap.MasonryBrick = function(config){
36204     
36205     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36206     
36207     Roo.bootstrap.MasonryBrick.register(this);
36208     
36209     this.addEvents({
36210         // raw events
36211         /**
36212          * @event click
36213          * When a MasonryBrick is clcik
36214          * @param {Roo.bootstrap.MasonryBrick} this
36215          * @param {Roo.EventObject} e
36216          */
36217         "click" : true
36218     });
36219 };
36220
36221 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36222     
36223     /**
36224      * @cfg {String} title
36225      */   
36226     title : '',
36227     /**
36228      * @cfg {String} html
36229      */   
36230     html : '',
36231     /**
36232      * @cfg {String} bgimage
36233      */   
36234     bgimage : '',
36235     /**
36236      * @cfg {String} videourl
36237      */   
36238     videourl : '',
36239     /**
36240      * @cfg {String} cls
36241      */   
36242     cls : '',
36243     /**
36244      * @cfg {String} href
36245      */   
36246     href : '',
36247     /**
36248      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36249      */   
36250     size : 'xs',
36251     
36252     /**
36253      * @cfg {String} placetitle (center|bottom)
36254      */   
36255     placetitle : '',
36256     
36257     /**
36258      * @cfg {Boolean} isFitContainer defalut true
36259      */   
36260     isFitContainer : true, 
36261     
36262     /**
36263      * @cfg {Boolean} preventDefault defalut false
36264      */   
36265     preventDefault : false, 
36266     
36267     /**
36268      * @cfg {Boolean} inverse defalut false
36269      */   
36270     maskInverse : false, 
36271     
36272     getAutoCreate : function()
36273     {
36274         if(!this.isFitContainer){
36275             return this.getSplitAutoCreate();
36276         }
36277         
36278         var cls = 'masonry-brick masonry-brick-full';
36279         
36280         if(this.href.length){
36281             cls += ' masonry-brick-link';
36282         }
36283         
36284         if(this.bgimage.length){
36285             cls += ' masonry-brick-image';
36286         }
36287         
36288         if(this.maskInverse){
36289             cls += ' mask-inverse';
36290         }
36291         
36292         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36293             cls += ' enable-mask';
36294         }
36295         
36296         if(this.size){
36297             cls += ' masonry-' + this.size + '-brick';
36298         }
36299         
36300         if(this.placetitle.length){
36301             
36302             switch (this.placetitle) {
36303                 case 'center' :
36304                     cls += ' masonry-center-title';
36305                     break;
36306                 case 'bottom' :
36307                     cls += ' masonry-bottom-title';
36308                     break;
36309                 default:
36310                     break;
36311             }
36312             
36313         } else {
36314             if(!this.html.length && !this.bgimage.length){
36315                 cls += ' masonry-center-title';
36316             }
36317
36318             if(!this.html.length && this.bgimage.length){
36319                 cls += ' masonry-bottom-title';
36320             }
36321         }
36322         
36323         if(this.cls){
36324             cls += ' ' + this.cls;
36325         }
36326         
36327         var cfg = {
36328             tag: (this.href.length) ? 'a' : 'div',
36329             cls: cls,
36330             cn: [
36331                 {
36332                     tag: 'div',
36333                     cls: 'masonry-brick-mask'
36334                 },
36335                 {
36336                     tag: 'div',
36337                     cls: 'masonry-brick-paragraph',
36338                     cn: []
36339                 }
36340             ]
36341         };
36342         
36343         if(this.href.length){
36344             cfg.href = this.href;
36345         }
36346         
36347         var cn = cfg.cn[1].cn;
36348         
36349         if(this.title.length){
36350             cn.push({
36351                 tag: 'h4',
36352                 cls: 'masonry-brick-title',
36353                 html: this.title
36354             });
36355         }
36356         
36357         if(this.html.length){
36358             cn.push({
36359                 tag: 'p',
36360                 cls: 'masonry-brick-text',
36361                 html: this.html
36362             });
36363         }
36364         
36365         if (!this.title.length && !this.html.length) {
36366             cfg.cn[1].cls += ' hide';
36367         }
36368         
36369         if(this.bgimage.length){
36370             cfg.cn.push({
36371                 tag: 'img',
36372                 cls: 'masonry-brick-image-view',
36373                 src: this.bgimage
36374             });
36375         }
36376         
36377         if(this.videourl.length){
36378             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36379             // youtube support only?
36380             cfg.cn.push({
36381                 tag: 'iframe',
36382                 cls: 'masonry-brick-image-view',
36383                 src: vurl,
36384                 frameborder : 0,
36385                 allowfullscreen : true
36386             });
36387         }
36388         
36389         return cfg;
36390         
36391     },
36392     
36393     getSplitAutoCreate : function()
36394     {
36395         var cls = 'masonry-brick masonry-brick-split';
36396         
36397         if(this.href.length){
36398             cls += ' masonry-brick-link';
36399         }
36400         
36401         if(this.bgimage.length){
36402             cls += ' masonry-brick-image';
36403         }
36404         
36405         if(this.size){
36406             cls += ' masonry-' + this.size + '-brick';
36407         }
36408         
36409         switch (this.placetitle) {
36410             case 'center' :
36411                 cls += ' masonry-center-title';
36412                 break;
36413             case 'bottom' :
36414                 cls += ' masonry-bottom-title';
36415                 break;
36416             default:
36417                 if(!this.bgimage.length){
36418                     cls += ' masonry-center-title';
36419                 }
36420
36421                 if(this.bgimage.length){
36422                     cls += ' masonry-bottom-title';
36423                 }
36424                 break;
36425         }
36426         
36427         if(this.cls){
36428             cls += ' ' + this.cls;
36429         }
36430         
36431         var cfg = {
36432             tag: (this.href.length) ? 'a' : 'div',
36433             cls: cls,
36434             cn: [
36435                 {
36436                     tag: 'div',
36437                     cls: 'masonry-brick-split-head',
36438                     cn: [
36439                         {
36440                             tag: 'div',
36441                             cls: 'masonry-brick-paragraph',
36442                             cn: []
36443                         }
36444                     ]
36445                 },
36446                 {
36447                     tag: 'div',
36448                     cls: 'masonry-brick-split-body',
36449                     cn: []
36450                 }
36451             ]
36452         };
36453         
36454         if(this.href.length){
36455             cfg.href = this.href;
36456         }
36457         
36458         if(this.title.length){
36459             cfg.cn[0].cn[0].cn.push({
36460                 tag: 'h4',
36461                 cls: 'masonry-brick-title',
36462                 html: this.title
36463             });
36464         }
36465         
36466         if(this.html.length){
36467             cfg.cn[1].cn.push({
36468                 tag: 'p',
36469                 cls: 'masonry-brick-text',
36470                 html: this.html
36471             });
36472         }
36473
36474         if(this.bgimage.length){
36475             cfg.cn[0].cn.push({
36476                 tag: 'img',
36477                 cls: 'masonry-brick-image-view',
36478                 src: this.bgimage
36479             });
36480         }
36481         
36482         if(this.videourl.length){
36483             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36484             // youtube support only?
36485             cfg.cn[0].cn.cn.push({
36486                 tag: 'iframe',
36487                 cls: 'masonry-brick-image-view',
36488                 src: vurl,
36489                 frameborder : 0,
36490                 allowfullscreen : true
36491             });
36492         }
36493         
36494         return cfg;
36495     },
36496     
36497     initEvents: function() 
36498     {
36499         switch (this.size) {
36500             case 'xs' :
36501                 this.x = 1;
36502                 this.y = 1;
36503                 break;
36504             case 'sm' :
36505                 this.x = 2;
36506                 this.y = 2;
36507                 break;
36508             case 'md' :
36509             case 'md-left' :
36510             case 'md-right' :
36511                 this.x = 3;
36512                 this.y = 3;
36513                 break;
36514             case 'tall' :
36515                 this.x = 2;
36516                 this.y = 3;
36517                 break;
36518             case 'wide' :
36519                 this.x = 3;
36520                 this.y = 2;
36521                 break;
36522             case 'wide-thin' :
36523                 this.x = 3;
36524                 this.y = 1;
36525                 break;
36526                         
36527             default :
36528                 break;
36529         }
36530         
36531         if(Roo.isTouch){
36532             this.el.on('touchstart', this.onTouchStart, this);
36533             this.el.on('touchmove', this.onTouchMove, this);
36534             this.el.on('touchend', this.onTouchEnd, this);
36535             this.el.on('contextmenu', this.onContextMenu, this);
36536         } else {
36537             this.el.on('mouseenter'  ,this.enter, this);
36538             this.el.on('mouseleave', this.leave, this);
36539             this.el.on('click', this.onClick, this);
36540         }
36541         
36542         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36543             this.parent().bricks.push(this);   
36544         }
36545         
36546     },
36547     
36548     onClick: function(e, el)
36549     {
36550         var time = this.endTimer - this.startTimer;
36551         // Roo.log(e.preventDefault());
36552         if(Roo.isTouch){
36553             if(time > 1000){
36554                 e.preventDefault();
36555                 return;
36556             }
36557         }
36558         
36559         if(!this.preventDefault){
36560             return;
36561         }
36562         
36563         e.preventDefault();
36564         
36565         if (this.activeClass != '') {
36566             this.selectBrick();
36567         }
36568         
36569         this.fireEvent('click', this, e);
36570     },
36571     
36572     enter: function(e, el)
36573     {
36574         e.preventDefault();
36575         
36576         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36577             return;
36578         }
36579         
36580         if(this.bgimage.length && this.html.length){
36581             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36582         }
36583     },
36584     
36585     leave: function(e, el)
36586     {
36587         e.preventDefault();
36588         
36589         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36590             return;
36591         }
36592         
36593         if(this.bgimage.length && this.html.length){
36594             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36595         }
36596     },
36597     
36598     onTouchStart: function(e, el)
36599     {
36600 //        e.preventDefault();
36601         
36602         this.touchmoved = false;
36603         
36604         if(!this.isFitContainer){
36605             return;
36606         }
36607         
36608         if(!this.bgimage.length || !this.html.length){
36609             return;
36610         }
36611         
36612         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36613         
36614         this.timer = new Date().getTime();
36615         
36616     },
36617     
36618     onTouchMove: function(e, el)
36619     {
36620         this.touchmoved = true;
36621     },
36622     
36623     onContextMenu : function(e,el)
36624     {
36625         e.preventDefault();
36626         e.stopPropagation();
36627         return false;
36628     },
36629     
36630     onTouchEnd: function(e, el)
36631     {
36632 //        e.preventDefault();
36633         
36634         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36635         
36636             this.leave(e,el);
36637             
36638             return;
36639         }
36640         
36641         if(!this.bgimage.length || !this.html.length){
36642             
36643             if(this.href.length){
36644                 window.location.href = this.href;
36645             }
36646             
36647             return;
36648         }
36649         
36650         if(!this.isFitContainer){
36651             return;
36652         }
36653         
36654         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36655         
36656         window.location.href = this.href;
36657     },
36658     
36659     //selection on single brick only
36660     selectBrick : function() {
36661         
36662         if (!this.parentId) {
36663             return;
36664         }
36665         
36666         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36667         var index = m.selectedBrick.indexOf(this.id);
36668         
36669         if ( index > -1) {
36670             m.selectedBrick.splice(index,1);
36671             this.el.removeClass(this.activeClass);
36672             return;
36673         }
36674         
36675         for(var i = 0; i < m.selectedBrick.length; i++) {
36676             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36677             b.el.removeClass(b.activeClass);
36678         }
36679         
36680         m.selectedBrick = [];
36681         
36682         m.selectedBrick.push(this.id);
36683         this.el.addClass(this.activeClass);
36684         return;
36685     },
36686     
36687     isSelected : function(){
36688         return this.el.hasClass(this.activeClass);
36689         
36690     }
36691 });
36692
36693 Roo.apply(Roo.bootstrap.MasonryBrick, {
36694     
36695     //groups: {},
36696     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36697      /**
36698     * register a Masonry Brick
36699     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36700     */
36701     
36702     register : function(brick)
36703     {
36704         //this.groups[brick.id] = brick;
36705         this.groups.add(brick.id, brick);
36706     },
36707     /**
36708     * fetch a  masonry brick based on the masonry brick ID
36709     * @param {string} the masonry brick to add
36710     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36711     */
36712     
36713     get: function(brick_id) 
36714     {
36715         // if (typeof(this.groups[brick_id]) == 'undefined') {
36716         //     return false;
36717         // }
36718         // return this.groups[brick_id] ;
36719         
36720         if(this.groups.key(brick_id)) {
36721             return this.groups.key(brick_id);
36722         }
36723         
36724         return false;
36725     }
36726     
36727     
36728     
36729 });
36730
36731  /*
36732  * - LGPL
36733  *
36734  * element
36735  * 
36736  */
36737
36738 /**
36739  * @class Roo.bootstrap.Brick
36740  * @extends Roo.bootstrap.Component
36741  * Bootstrap Brick class
36742  * 
36743  * @constructor
36744  * Create a new Brick
36745  * @param {Object} config The config object
36746  */
36747
36748 Roo.bootstrap.Brick = function(config){
36749     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36750     
36751     this.addEvents({
36752         // raw events
36753         /**
36754          * @event click
36755          * When a Brick is click
36756          * @param {Roo.bootstrap.Brick} this
36757          * @param {Roo.EventObject} e
36758          */
36759         "click" : true
36760     });
36761 };
36762
36763 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36764     
36765     /**
36766      * @cfg {String} title
36767      */   
36768     title : '',
36769     /**
36770      * @cfg {String} html
36771      */   
36772     html : '',
36773     /**
36774      * @cfg {String} bgimage
36775      */   
36776     bgimage : '',
36777     /**
36778      * @cfg {String} cls
36779      */   
36780     cls : '',
36781     /**
36782      * @cfg {String} href
36783      */   
36784     href : '',
36785     /**
36786      * @cfg {String} video
36787      */   
36788     video : '',
36789     /**
36790      * @cfg {Boolean} square
36791      */   
36792     square : true,
36793     
36794     getAutoCreate : function()
36795     {
36796         var cls = 'roo-brick';
36797         
36798         if(this.href.length){
36799             cls += ' roo-brick-link';
36800         }
36801         
36802         if(this.bgimage.length){
36803             cls += ' roo-brick-image';
36804         }
36805         
36806         if(!this.html.length && !this.bgimage.length){
36807             cls += ' roo-brick-center-title';
36808         }
36809         
36810         if(!this.html.length && this.bgimage.length){
36811             cls += ' roo-brick-bottom-title';
36812         }
36813         
36814         if(this.cls){
36815             cls += ' ' + this.cls;
36816         }
36817         
36818         var cfg = {
36819             tag: (this.href.length) ? 'a' : 'div',
36820             cls: cls,
36821             cn: [
36822                 {
36823                     tag: 'div',
36824                     cls: 'roo-brick-paragraph',
36825                     cn: []
36826                 }
36827             ]
36828         };
36829         
36830         if(this.href.length){
36831             cfg.href = this.href;
36832         }
36833         
36834         var cn = cfg.cn[0].cn;
36835         
36836         if(this.title.length){
36837             cn.push({
36838                 tag: 'h4',
36839                 cls: 'roo-brick-title',
36840                 html: this.title
36841             });
36842         }
36843         
36844         if(this.html.length){
36845             cn.push({
36846                 tag: 'p',
36847                 cls: 'roo-brick-text',
36848                 html: this.html
36849             });
36850         } else {
36851             cn.cls += ' hide';
36852         }
36853         
36854         if(this.bgimage.length){
36855             cfg.cn.push({
36856                 tag: 'img',
36857                 cls: 'roo-brick-image-view',
36858                 src: this.bgimage
36859             });
36860         }
36861         
36862         return cfg;
36863     },
36864     
36865     initEvents: function() 
36866     {
36867         if(this.title.length || this.html.length){
36868             this.el.on('mouseenter'  ,this.enter, this);
36869             this.el.on('mouseleave', this.leave, this);
36870         }
36871         
36872         Roo.EventManager.onWindowResize(this.resize, this); 
36873         
36874         if(this.bgimage.length){
36875             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36876             this.imageEl.on('load', this.onImageLoad, this);
36877             return;
36878         }
36879         
36880         this.resize();
36881     },
36882     
36883     onImageLoad : function()
36884     {
36885         this.resize();
36886     },
36887     
36888     resize : function()
36889     {
36890         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36891         
36892         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36893         
36894         if(this.bgimage.length){
36895             var image = this.el.select('.roo-brick-image-view', true).first();
36896             
36897             image.setWidth(paragraph.getWidth());
36898             
36899             if(this.square){
36900                 image.setHeight(paragraph.getWidth());
36901             }
36902             
36903             this.el.setHeight(image.getHeight());
36904             paragraph.setHeight(image.getHeight());
36905             
36906         }
36907         
36908     },
36909     
36910     enter: function(e, el)
36911     {
36912         e.preventDefault();
36913         
36914         if(this.bgimage.length){
36915             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36916             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36917         }
36918     },
36919     
36920     leave: function(e, el)
36921     {
36922         e.preventDefault();
36923         
36924         if(this.bgimage.length){
36925             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36926             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36927         }
36928     }
36929     
36930 });
36931
36932  
36933
36934  /*
36935  * - LGPL
36936  *
36937  * Number field 
36938  */
36939
36940 /**
36941  * @class Roo.bootstrap.NumberField
36942  * @extends Roo.bootstrap.Input
36943  * Bootstrap NumberField class
36944  * 
36945  * 
36946  * 
36947  * 
36948  * @constructor
36949  * Create a new NumberField
36950  * @param {Object} config The config object
36951  */
36952
36953 Roo.bootstrap.NumberField = function(config){
36954     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36955 };
36956
36957 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36958     
36959     /**
36960      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36961      */
36962     allowDecimals : true,
36963     /**
36964      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36965      */
36966     decimalSeparator : ".",
36967     /**
36968      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36969      */
36970     decimalPrecision : 2,
36971     /**
36972      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36973      */
36974     allowNegative : true,
36975     
36976     /**
36977      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36978      */
36979     allowZero: true,
36980     /**
36981      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36982      */
36983     minValue : Number.NEGATIVE_INFINITY,
36984     /**
36985      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36986      */
36987     maxValue : Number.MAX_VALUE,
36988     /**
36989      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36990      */
36991     minText : "The minimum value for this field is {0}",
36992     /**
36993      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36994      */
36995     maxText : "The maximum value for this field is {0}",
36996     /**
36997      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36998      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36999      */
37000     nanText : "{0} is not a valid number",
37001     /**
37002      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37003      */
37004     thousandsDelimiter : false,
37005     /**
37006      * @cfg {String} valueAlign alignment of value
37007      */
37008     valueAlign : "left",
37009
37010     getAutoCreate : function()
37011     {
37012         var hiddenInput = {
37013             tag: 'input',
37014             type: 'hidden',
37015             id: Roo.id(),
37016             cls: 'hidden-number-input'
37017         };
37018         
37019         if (this.name) {
37020             hiddenInput.name = this.name;
37021         }
37022         
37023         this.name = '';
37024         
37025         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37026         
37027         this.name = hiddenInput.name;
37028         
37029         if(cfg.cn.length > 0) {
37030             cfg.cn.push(hiddenInput);
37031         }
37032         
37033         return cfg;
37034     },
37035
37036     // private
37037     initEvents : function()
37038     {   
37039         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37040         
37041         var allowed = "0123456789";
37042         
37043         if(this.allowDecimals){
37044             allowed += this.decimalSeparator;
37045         }
37046         
37047         if(this.allowNegative){
37048             allowed += "-";
37049         }
37050         
37051         if(this.thousandsDelimiter) {
37052             allowed += ",";
37053         }
37054         
37055         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37056         
37057         var keyPress = function(e){
37058             
37059             var k = e.getKey();
37060             
37061             var c = e.getCharCode();
37062             
37063             if(
37064                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37065                     allowed.indexOf(String.fromCharCode(c)) === -1
37066             ){
37067                 e.stopEvent();
37068                 return;
37069             }
37070             
37071             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37072                 return;
37073             }
37074             
37075             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37076                 e.stopEvent();
37077             }
37078         };
37079         
37080         this.el.on("keypress", keyPress, this);
37081     },
37082     
37083     validateValue : function(value)
37084     {
37085         
37086         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37087             return false;
37088         }
37089         
37090         var num = this.parseValue(value);
37091         
37092         if(isNaN(num)){
37093             this.markInvalid(String.format(this.nanText, value));
37094             return false;
37095         }
37096         
37097         if(num < this.minValue){
37098             this.markInvalid(String.format(this.minText, this.minValue));
37099             return false;
37100         }
37101         
37102         if(num > this.maxValue){
37103             this.markInvalid(String.format(this.maxText, this.maxValue));
37104             return false;
37105         }
37106         
37107         return true;
37108     },
37109
37110     getValue : function()
37111     {
37112         var v = this.hiddenEl().getValue();
37113         
37114         return this.fixPrecision(this.parseValue(v));
37115     },
37116
37117     parseValue : function(value)
37118     {
37119         if(this.thousandsDelimiter) {
37120             value += "";
37121             r = new RegExp(",", "g");
37122             value = value.replace(r, "");
37123         }
37124         
37125         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37126         return isNaN(value) ? '' : value;
37127     },
37128
37129     fixPrecision : function(value)
37130     {
37131         if(this.thousandsDelimiter) {
37132             value += "";
37133             r = new RegExp(",", "g");
37134             value = value.replace(r, "");
37135         }
37136         
37137         var nan = isNaN(value);
37138         
37139         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37140             return nan ? '' : value;
37141         }
37142         return parseFloat(value).toFixed(this.decimalPrecision);
37143     },
37144
37145     setValue : function(v)
37146     {
37147         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37148         
37149         this.value = v;
37150         
37151         if(this.rendered){
37152             
37153             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37154             
37155             this.inputEl().dom.value = (v == '') ? '' :
37156                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37157             
37158             if(!this.allowZero && v === '0') {
37159                 this.hiddenEl().dom.value = '';
37160                 this.inputEl().dom.value = '';
37161             }
37162             
37163             this.validate();
37164         }
37165     },
37166
37167     decimalPrecisionFcn : function(v)
37168     {
37169         return Math.floor(v);
37170     },
37171
37172     beforeBlur : function()
37173     {
37174         var v = this.parseValue(this.getRawValue());
37175         
37176         if(v || v === 0 || v === ''){
37177             this.setValue(v);
37178         }
37179     },
37180     
37181     hiddenEl : function()
37182     {
37183         return this.el.select('input.hidden-number-input',true).first();
37184     }
37185     
37186 });
37187
37188  
37189
37190 /*
37191 * Licence: LGPL
37192 */
37193
37194 /**
37195  * @class Roo.bootstrap.DocumentSlider
37196  * @extends Roo.bootstrap.Component
37197  * Bootstrap DocumentSlider class
37198  * 
37199  * @constructor
37200  * Create a new DocumentViewer
37201  * @param {Object} config The config object
37202  */
37203
37204 Roo.bootstrap.DocumentSlider = function(config){
37205     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37206     
37207     this.files = [];
37208     
37209     this.addEvents({
37210         /**
37211          * @event initial
37212          * Fire after initEvent
37213          * @param {Roo.bootstrap.DocumentSlider} this
37214          */
37215         "initial" : true,
37216         /**
37217          * @event update
37218          * Fire after update
37219          * @param {Roo.bootstrap.DocumentSlider} this
37220          */
37221         "update" : true,
37222         /**
37223          * @event click
37224          * Fire after click
37225          * @param {Roo.bootstrap.DocumentSlider} this
37226          */
37227         "click" : true
37228     });
37229 };
37230
37231 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37232     
37233     files : false,
37234     
37235     indicator : 0,
37236     
37237     getAutoCreate : function()
37238     {
37239         var cfg = {
37240             tag : 'div',
37241             cls : 'roo-document-slider',
37242             cn : [
37243                 {
37244                     tag : 'div',
37245                     cls : 'roo-document-slider-header',
37246                     cn : [
37247                         {
37248                             tag : 'div',
37249                             cls : 'roo-document-slider-header-title'
37250                         }
37251                     ]
37252                 },
37253                 {
37254                     tag : 'div',
37255                     cls : 'roo-document-slider-body',
37256                     cn : [
37257                         {
37258                             tag : 'div',
37259                             cls : 'roo-document-slider-prev',
37260                             cn : [
37261                                 {
37262                                     tag : 'i',
37263                                     cls : 'fa fa-chevron-left'
37264                                 }
37265                             ]
37266                         },
37267                         {
37268                             tag : 'div',
37269                             cls : 'roo-document-slider-thumb',
37270                             cn : [
37271                                 {
37272                                     tag : 'img',
37273                                     cls : 'roo-document-slider-image'
37274                                 }
37275                             ]
37276                         },
37277                         {
37278                             tag : 'div',
37279                             cls : 'roo-document-slider-next',
37280                             cn : [
37281                                 {
37282                                     tag : 'i',
37283                                     cls : 'fa fa-chevron-right'
37284                                 }
37285                             ]
37286                         }
37287                     ]
37288                 }
37289             ]
37290         };
37291         
37292         return cfg;
37293     },
37294     
37295     initEvents : function()
37296     {
37297         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37298         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37299         
37300         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37301         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37302         
37303         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37304         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37305         
37306         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37307         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37308         
37309         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37310         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37311         
37312         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37313         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37314         
37315         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37316         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37317         
37318         this.thumbEl.on('click', this.onClick, this);
37319         
37320         this.prevIndicator.on('click', this.prev, this);
37321         
37322         this.nextIndicator.on('click', this.next, this);
37323         
37324     },
37325     
37326     initial : function()
37327     {
37328         if(this.files.length){
37329             this.indicator = 1;
37330             this.update()
37331         }
37332         
37333         this.fireEvent('initial', this);
37334     },
37335     
37336     update : function()
37337     {
37338         this.imageEl.attr('src', this.files[this.indicator - 1]);
37339         
37340         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37341         
37342         this.prevIndicator.show();
37343         
37344         if(this.indicator == 1){
37345             this.prevIndicator.hide();
37346         }
37347         
37348         this.nextIndicator.show();
37349         
37350         if(this.indicator == this.files.length){
37351             this.nextIndicator.hide();
37352         }
37353         
37354         this.thumbEl.scrollTo('top');
37355         
37356         this.fireEvent('update', this);
37357     },
37358     
37359     onClick : function(e)
37360     {
37361         e.preventDefault();
37362         
37363         this.fireEvent('click', this);
37364     },
37365     
37366     prev : function(e)
37367     {
37368         e.preventDefault();
37369         
37370         this.indicator = Math.max(1, this.indicator - 1);
37371         
37372         this.update();
37373     },
37374     
37375     next : function(e)
37376     {
37377         e.preventDefault();
37378         
37379         this.indicator = Math.min(this.files.length, this.indicator + 1);
37380         
37381         this.update();
37382     }
37383 });
37384 /*
37385  * - LGPL
37386  *
37387  * RadioSet
37388  *
37389  *
37390  */
37391
37392 /**
37393  * @class Roo.bootstrap.RadioSet
37394  * @extends Roo.bootstrap.Input
37395  * Bootstrap RadioSet class
37396  * @cfg {String} indicatorpos (left|right) default left
37397  * @cfg {Boolean} inline (true|false) inline the element (default true)
37398  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37399  * @constructor
37400  * Create a new RadioSet
37401  * @param {Object} config The config object
37402  */
37403
37404 Roo.bootstrap.RadioSet = function(config){
37405     
37406     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37407     
37408     this.radioes = [];
37409     
37410     Roo.bootstrap.RadioSet.register(this);
37411     
37412     this.addEvents({
37413         /**
37414         * @event check
37415         * Fires when the element is checked or unchecked.
37416         * @param {Roo.bootstrap.RadioSet} this This radio
37417         * @param {Roo.bootstrap.Radio} item The checked item
37418         */
37419        check : true,
37420        /**
37421         * @event click
37422         * Fires when the element is click.
37423         * @param {Roo.bootstrap.RadioSet} this This radio set
37424         * @param {Roo.bootstrap.Radio} item The checked item
37425         * @param {Roo.EventObject} e The event object
37426         */
37427        click : true
37428     });
37429     
37430 };
37431
37432 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37433
37434     radioes : false,
37435     
37436     inline : true,
37437     
37438     weight : '',
37439     
37440     indicatorpos : 'left',
37441     
37442     getAutoCreate : function()
37443     {
37444         var label = {
37445             tag : 'label',
37446             cls : 'roo-radio-set-label',
37447             cn : [
37448                 {
37449                     tag : 'span',
37450                     html : this.fieldLabel
37451                 }
37452             ]
37453         };
37454         if (Roo.bootstrap.version == 3) {
37455             
37456             
37457             if(this.indicatorpos == 'left'){
37458                 label.cn.unshift({
37459                     tag : 'i',
37460                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37461                     tooltip : 'This field is required'
37462                 });
37463             } else {
37464                 label.cn.push({
37465                     tag : 'i',
37466                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37467                     tooltip : 'This field is required'
37468                 });
37469             }
37470         }
37471         var items = {
37472             tag : 'div',
37473             cls : 'roo-radio-set-items'
37474         };
37475         
37476         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37477         
37478         if (align === 'left' && this.fieldLabel.length) {
37479             
37480             items = {
37481                 cls : "roo-radio-set-right", 
37482                 cn: [
37483                     items
37484                 ]
37485             };
37486             
37487             if(this.labelWidth > 12){
37488                 label.style = "width: " + this.labelWidth + 'px';
37489             }
37490             
37491             if(this.labelWidth < 13 && this.labelmd == 0){
37492                 this.labelmd = this.labelWidth;
37493             }
37494             
37495             if(this.labellg > 0){
37496                 label.cls += ' col-lg-' + this.labellg;
37497                 items.cls += ' col-lg-' + (12 - this.labellg);
37498             }
37499             
37500             if(this.labelmd > 0){
37501                 label.cls += ' col-md-' + this.labelmd;
37502                 items.cls += ' col-md-' + (12 - this.labelmd);
37503             }
37504             
37505             if(this.labelsm > 0){
37506                 label.cls += ' col-sm-' + this.labelsm;
37507                 items.cls += ' col-sm-' + (12 - this.labelsm);
37508             }
37509             
37510             if(this.labelxs > 0){
37511                 label.cls += ' col-xs-' + this.labelxs;
37512                 items.cls += ' col-xs-' + (12 - this.labelxs);
37513             }
37514         }
37515         
37516         var cfg = {
37517             tag : 'div',
37518             cls : 'roo-radio-set',
37519             cn : [
37520                 {
37521                     tag : 'input',
37522                     cls : 'roo-radio-set-input',
37523                     type : 'hidden',
37524                     name : this.name,
37525                     value : this.value ? this.value :  ''
37526                 },
37527                 label,
37528                 items
37529             ]
37530         };
37531         
37532         if(this.weight.length){
37533             cfg.cls += ' roo-radio-' + this.weight;
37534         }
37535         
37536         if(this.inline) {
37537             cfg.cls += ' roo-radio-set-inline';
37538         }
37539         
37540         var settings=this;
37541         ['xs','sm','md','lg'].map(function(size){
37542             if (settings[size]) {
37543                 cfg.cls += ' col-' + size + '-' + settings[size];
37544             }
37545         });
37546         
37547         return cfg;
37548         
37549     },
37550
37551     initEvents : function()
37552     {
37553         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37554         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37555         
37556         if(!this.fieldLabel.length){
37557             this.labelEl.hide();
37558         }
37559         
37560         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37561         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37562         
37563         this.indicator = this.indicatorEl();
37564         
37565         if(this.indicator){
37566             this.indicator.addClass('invisible');
37567         }
37568         
37569         this.originalValue = this.getValue();
37570         
37571     },
37572     
37573     inputEl: function ()
37574     {
37575         return this.el.select('.roo-radio-set-input', true).first();
37576     },
37577     
37578     getChildContainer : function()
37579     {
37580         return this.itemsEl;
37581     },
37582     
37583     register : function(item)
37584     {
37585         this.radioes.push(item);
37586         
37587     },
37588     
37589     validate : function()
37590     {   
37591         if(this.getVisibilityEl().hasClass('hidden')){
37592             return true;
37593         }
37594         
37595         var valid = false;
37596         
37597         Roo.each(this.radioes, function(i){
37598             if(!i.checked){
37599                 return;
37600             }
37601             
37602             valid = true;
37603             return false;
37604         });
37605         
37606         if(this.allowBlank) {
37607             return true;
37608         }
37609         
37610         if(this.disabled || valid){
37611             this.markValid();
37612             return true;
37613         }
37614         
37615         this.markInvalid();
37616         return false;
37617         
37618     },
37619     
37620     markValid : function()
37621     {
37622         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37623             this.indicatorEl().removeClass('visible');
37624             this.indicatorEl().addClass('invisible');
37625         }
37626         
37627         
37628         if (Roo.bootstrap.version == 3) {
37629             this.el.removeClass([this.invalidClass, this.validClass]);
37630             this.el.addClass(this.validClass);
37631         } else {
37632             this.el.removeClass(['is-invalid','is-valid']);
37633             this.el.addClass(['is-valid']);
37634         }
37635         this.fireEvent('valid', this);
37636     },
37637     
37638     markInvalid : function(msg)
37639     {
37640         if(this.allowBlank || this.disabled){
37641             return;
37642         }
37643         
37644         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37645             this.indicatorEl().removeClass('invisible');
37646             this.indicatorEl().addClass('visible');
37647         }
37648         if (Roo.bootstrap.version == 3) {
37649             this.el.removeClass([this.invalidClass, this.validClass]);
37650             this.el.addClass(this.invalidClass);
37651         } else {
37652             this.el.removeClass(['is-invalid','is-valid']);
37653             this.el.addClass(['is-invalid']);
37654         }
37655         
37656         this.fireEvent('invalid', this, msg);
37657         
37658     },
37659     
37660     setValue : function(v, suppressEvent)
37661     {   
37662         if(this.value === v){
37663             return;
37664         }
37665         
37666         this.value = v;
37667         
37668         if(this.rendered){
37669             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37670         }
37671         
37672         Roo.each(this.radioes, function(i){
37673             i.checked = false;
37674             i.el.removeClass('checked');
37675         });
37676         
37677         Roo.each(this.radioes, function(i){
37678             
37679             if(i.value === v || i.value.toString() === v.toString()){
37680                 i.checked = true;
37681                 i.el.addClass('checked');
37682                 
37683                 if(suppressEvent !== true){
37684                     this.fireEvent('check', this, i);
37685                 }
37686                 
37687                 return false;
37688             }
37689             
37690         }, this);
37691         
37692         this.validate();
37693     },
37694     
37695     clearInvalid : function(){
37696         
37697         if(!this.el || this.preventMark){
37698             return;
37699         }
37700         
37701         this.el.removeClass([this.invalidClass]);
37702         
37703         this.fireEvent('valid', this);
37704     }
37705     
37706 });
37707
37708 Roo.apply(Roo.bootstrap.RadioSet, {
37709     
37710     groups: {},
37711     
37712     register : function(set)
37713     {
37714         this.groups[set.name] = set;
37715     },
37716     
37717     get: function(name) 
37718     {
37719         if (typeof(this.groups[name]) == 'undefined') {
37720             return false;
37721         }
37722         
37723         return this.groups[name] ;
37724     }
37725     
37726 });
37727 /*
37728  * Based on:
37729  * Ext JS Library 1.1.1
37730  * Copyright(c) 2006-2007, Ext JS, LLC.
37731  *
37732  * Originally Released Under LGPL - original licence link has changed is not relivant.
37733  *
37734  * Fork - LGPL
37735  * <script type="text/javascript">
37736  */
37737
37738
37739 /**
37740  * @class Roo.bootstrap.SplitBar
37741  * @extends Roo.util.Observable
37742  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37743  * <br><br>
37744  * Usage:
37745  * <pre><code>
37746 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37747                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37748 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37749 split.minSize = 100;
37750 split.maxSize = 600;
37751 split.animate = true;
37752 split.on('moved', splitterMoved);
37753 </code></pre>
37754  * @constructor
37755  * Create a new SplitBar
37756  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37757  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37758  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37759  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37760                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37761                         position of the SplitBar).
37762  */
37763 Roo.bootstrap.SplitBar = function(cfg){
37764     
37765     /** @private */
37766     
37767     //{
37768     //  dragElement : elm
37769     //  resizingElement: el,
37770         // optional..
37771     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37772     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37773         // existingProxy ???
37774     //}
37775     
37776     this.el = Roo.get(cfg.dragElement, true);
37777     this.el.dom.unselectable = "on";
37778     /** @private */
37779     this.resizingEl = Roo.get(cfg.resizingElement, true);
37780
37781     /**
37782      * @private
37783      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37784      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37785      * @type Number
37786      */
37787     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37788     
37789     /**
37790      * The minimum size of the resizing element. (Defaults to 0)
37791      * @type Number
37792      */
37793     this.minSize = 0;
37794     
37795     /**
37796      * The maximum size of the resizing element. (Defaults to 2000)
37797      * @type Number
37798      */
37799     this.maxSize = 2000;
37800     
37801     /**
37802      * Whether to animate the transition to the new size
37803      * @type Boolean
37804      */
37805     this.animate = false;
37806     
37807     /**
37808      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37809      * @type Boolean
37810      */
37811     this.useShim = false;
37812     
37813     /** @private */
37814     this.shim = null;
37815     
37816     if(!cfg.existingProxy){
37817         /** @private */
37818         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37819     }else{
37820         this.proxy = Roo.get(cfg.existingProxy).dom;
37821     }
37822     /** @private */
37823     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37824     
37825     /** @private */
37826     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37827     
37828     /** @private */
37829     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37830     
37831     /** @private */
37832     this.dragSpecs = {};
37833     
37834     /**
37835      * @private The adapter to use to positon and resize elements
37836      */
37837     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37838     this.adapter.init(this);
37839     
37840     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37841         /** @private */
37842         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37843         this.el.addClass("roo-splitbar-h");
37844     }else{
37845         /** @private */
37846         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37847         this.el.addClass("roo-splitbar-v");
37848     }
37849     
37850     this.addEvents({
37851         /**
37852          * @event resize
37853          * Fires when the splitter is moved (alias for {@link #event-moved})
37854          * @param {Roo.bootstrap.SplitBar} this
37855          * @param {Number} newSize the new width or height
37856          */
37857         "resize" : true,
37858         /**
37859          * @event moved
37860          * Fires when the splitter is moved
37861          * @param {Roo.bootstrap.SplitBar} this
37862          * @param {Number} newSize the new width or height
37863          */
37864         "moved" : true,
37865         /**
37866          * @event beforeresize
37867          * Fires before the splitter is dragged
37868          * @param {Roo.bootstrap.SplitBar} this
37869          */
37870         "beforeresize" : true,
37871
37872         "beforeapply" : true
37873     });
37874
37875     Roo.util.Observable.call(this);
37876 };
37877
37878 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37879     onStartProxyDrag : function(x, y){
37880         this.fireEvent("beforeresize", this);
37881         if(!this.overlay){
37882             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37883             o.unselectable();
37884             o.enableDisplayMode("block");
37885             // all splitbars share the same overlay
37886             Roo.bootstrap.SplitBar.prototype.overlay = o;
37887         }
37888         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37889         this.overlay.show();
37890         Roo.get(this.proxy).setDisplayed("block");
37891         var size = this.adapter.getElementSize(this);
37892         this.activeMinSize = this.getMinimumSize();;
37893         this.activeMaxSize = this.getMaximumSize();;
37894         var c1 = size - this.activeMinSize;
37895         var c2 = Math.max(this.activeMaxSize - size, 0);
37896         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37897             this.dd.resetConstraints();
37898             this.dd.setXConstraint(
37899                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37900                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37901             );
37902             this.dd.setYConstraint(0, 0);
37903         }else{
37904             this.dd.resetConstraints();
37905             this.dd.setXConstraint(0, 0);
37906             this.dd.setYConstraint(
37907                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37908                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37909             );
37910          }
37911         this.dragSpecs.startSize = size;
37912         this.dragSpecs.startPoint = [x, y];
37913         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37914     },
37915     
37916     /** 
37917      * @private Called after the drag operation by the DDProxy
37918      */
37919     onEndProxyDrag : function(e){
37920         Roo.get(this.proxy).setDisplayed(false);
37921         var endPoint = Roo.lib.Event.getXY(e);
37922         if(this.overlay){
37923             this.overlay.hide();
37924         }
37925         var newSize;
37926         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37927             newSize = this.dragSpecs.startSize + 
37928                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37929                     endPoint[0] - this.dragSpecs.startPoint[0] :
37930                     this.dragSpecs.startPoint[0] - endPoint[0]
37931                 );
37932         }else{
37933             newSize = this.dragSpecs.startSize + 
37934                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37935                     endPoint[1] - this.dragSpecs.startPoint[1] :
37936                     this.dragSpecs.startPoint[1] - endPoint[1]
37937                 );
37938         }
37939         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37940         if(newSize != this.dragSpecs.startSize){
37941             if(this.fireEvent('beforeapply', this, newSize) !== false){
37942                 this.adapter.setElementSize(this, newSize);
37943                 this.fireEvent("moved", this, newSize);
37944                 this.fireEvent("resize", this, newSize);
37945             }
37946         }
37947     },
37948     
37949     /**
37950      * Get the adapter this SplitBar uses
37951      * @return The adapter object
37952      */
37953     getAdapter : function(){
37954         return this.adapter;
37955     },
37956     
37957     /**
37958      * Set the adapter this SplitBar uses
37959      * @param {Object} adapter A SplitBar adapter object
37960      */
37961     setAdapter : function(adapter){
37962         this.adapter = adapter;
37963         this.adapter.init(this);
37964     },
37965     
37966     /**
37967      * Gets the minimum size for the resizing element
37968      * @return {Number} The minimum size
37969      */
37970     getMinimumSize : function(){
37971         return this.minSize;
37972     },
37973     
37974     /**
37975      * Sets the minimum size for the resizing element
37976      * @param {Number} minSize The minimum size
37977      */
37978     setMinimumSize : function(minSize){
37979         this.minSize = minSize;
37980     },
37981     
37982     /**
37983      * Gets the maximum size for the resizing element
37984      * @return {Number} The maximum size
37985      */
37986     getMaximumSize : function(){
37987         return this.maxSize;
37988     },
37989     
37990     /**
37991      * Sets the maximum size for the resizing element
37992      * @param {Number} maxSize The maximum size
37993      */
37994     setMaximumSize : function(maxSize){
37995         this.maxSize = maxSize;
37996     },
37997     
37998     /**
37999      * Sets the initialize size for the resizing element
38000      * @param {Number} size The initial size
38001      */
38002     setCurrentSize : function(size){
38003         var oldAnimate = this.animate;
38004         this.animate = false;
38005         this.adapter.setElementSize(this, size);
38006         this.animate = oldAnimate;
38007     },
38008     
38009     /**
38010      * Destroy this splitbar. 
38011      * @param {Boolean} removeEl True to remove the element
38012      */
38013     destroy : function(removeEl){
38014         if(this.shim){
38015             this.shim.remove();
38016         }
38017         this.dd.unreg();
38018         this.proxy.parentNode.removeChild(this.proxy);
38019         if(removeEl){
38020             this.el.remove();
38021         }
38022     }
38023 });
38024
38025 /**
38026  * @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.
38027  */
38028 Roo.bootstrap.SplitBar.createProxy = function(dir){
38029     var proxy = new Roo.Element(document.createElement("div"));
38030     proxy.unselectable();
38031     var cls = 'roo-splitbar-proxy';
38032     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38033     document.body.appendChild(proxy.dom);
38034     return proxy.dom;
38035 };
38036
38037 /** 
38038  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38039  * Default Adapter. It assumes the splitter and resizing element are not positioned
38040  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38041  */
38042 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38043 };
38044
38045 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38046     // do nothing for now
38047     init : function(s){
38048     
38049     },
38050     /**
38051      * Called before drag operations to get the current size of the resizing element. 
38052      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38053      */
38054      getElementSize : function(s){
38055         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38056             return s.resizingEl.getWidth();
38057         }else{
38058             return s.resizingEl.getHeight();
38059         }
38060     },
38061     
38062     /**
38063      * Called after drag operations to set the size of the resizing element.
38064      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38065      * @param {Number} newSize The new size to set
38066      * @param {Function} onComplete A function to be invoked when resizing is complete
38067      */
38068     setElementSize : function(s, newSize, onComplete){
38069         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38070             if(!s.animate){
38071                 s.resizingEl.setWidth(newSize);
38072                 if(onComplete){
38073                     onComplete(s, newSize);
38074                 }
38075             }else{
38076                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38077             }
38078         }else{
38079             
38080             if(!s.animate){
38081                 s.resizingEl.setHeight(newSize);
38082                 if(onComplete){
38083                     onComplete(s, newSize);
38084                 }
38085             }else{
38086                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38087             }
38088         }
38089     }
38090 };
38091
38092 /** 
38093  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38094  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38095  * Adapter that  moves the splitter element to align with the resized sizing element. 
38096  * Used with an absolute positioned SplitBar.
38097  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38098  * document.body, make sure you assign an id to the body element.
38099  */
38100 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38101     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38102     this.container = Roo.get(container);
38103 };
38104
38105 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38106     init : function(s){
38107         this.basic.init(s);
38108     },
38109     
38110     getElementSize : function(s){
38111         return this.basic.getElementSize(s);
38112     },
38113     
38114     setElementSize : function(s, newSize, onComplete){
38115         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38116     },
38117     
38118     moveSplitter : function(s){
38119         var yes = Roo.bootstrap.SplitBar;
38120         switch(s.placement){
38121             case yes.LEFT:
38122                 s.el.setX(s.resizingEl.getRight());
38123                 break;
38124             case yes.RIGHT:
38125                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38126                 break;
38127             case yes.TOP:
38128                 s.el.setY(s.resizingEl.getBottom());
38129                 break;
38130             case yes.BOTTOM:
38131                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38132                 break;
38133         }
38134     }
38135 };
38136
38137 /**
38138  * Orientation constant - Create a vertical SplitBar
38139  * @static
38140  * @type Number
38141  */
38142 Roo.bootstrap.SplitBar.VERTICAL = 1;
38143
38144 /**
38145  * Orientation constant - Create a horizontal SplitBar
38146  * @static
38147  * @type Number
38148  */
38149 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38150
38151 /**
38152  * Placement constant - The resizing element is to the left of the splitter element
38153  * @static
38154  * @type Number
38155  */
38156 Roo.bootstrap.SplitBar.LEFT = 1;
38157
38158 /**
38159  * Placement constant - The resizing element is to the right of the splitter element
38160  * @static
38161  * @type Number
38162  */
38163 Roo.bootstrap.SplitBar.RIGHT = 2;
38164
38165 /**
38166  * Placement constant - The resizing element is positioned above the splitter element
38167  * @static
38168  * @type Number
38169  */
38170 Roo.bootstrap.SplitBar.TOP = 3;
38171
38172 /**
38173  * Placement constant - The resizing element is positioned under splitter element
38174  * @static
38175  * @type Number
38176  */
38177 Roo.bootstrap.SplitBar.BOTTOM = 4;
38178 Roo.namespace("Roo.bootstrap.layout");/*
38179  * Based on:
38180  * Ext JS Library 1.1.1
38181  * Copyright(c) 2006-2007, Ext JS, LLC.
38182  *
38183  * Originally Released Under LGPL - original licence link has changed is not relivant.
38184  *
38185  * Fork - LGPL
38186  * <script type="text/javascript">
38187  */
38188
38189 /**
38190  * @class Roo.bootstrap.layout.Manager
38191  * @extends Roo.bootstrap.Component
38192  * Base class for layout managers.
38193  */
38194 Roo.bootstrap.layout.Manager = function(config)
38195 {
38196     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38197
38198
38199
38200
38201
38202     /** false to disable window resize monitoring @type Boolean */
38203     this.monitorWindowResize = true;
38204     this.regions = {};
38205     this.addEvents({
38206         /**
38207          * @event layout
38208          * Fires when a layout is performed.
38209          * @param {Roo.LayoutManager} this
38210          */
38211         "layout" : true,
38212         /**
38213          * @event regionresized
38214          * Fires when the user resizes a region.
38215          * @param {Roo.LayoutRegion} region The resized region
38216          * @param {Number} newSize The new size (width for east/west, height for north/south)
38217          */
38218         "regionresized" : true,
38219         /**
38220          * @event regioncollapsed
38221          * Fires when a region is collapsed.
38222          * @param {Roo.LayoutRegion} region The collapsed region
38223          */
38224         "regioncollapsed" : true,
38225         /**
38226          * @event regionexpanded
38227          * Fires when a region is expanded.
38228          * @param {Roo.LayoutRegion} region The expanded region
38229          */
38230         "regionexpanded" : true
38231     });
38232     this.updating = false;
38233
38234     if (config.el) {
38235         this.el = Roo.get(config.el);
38236         this.initEvents();
38237     }
38238
38239 };
38240
38241 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38242
38243
38244     regions : null,
38245
38246     monitorWindowResize : true,
38247
38248
38249     updating : false,
38250
38251
38252     onRender : function(ct, position)
38253     {
38254         if(!this.el){
38255             this.el = Roo.get(ct);
38256             this.initEvents();
38257         }
38258         //this.fireEvent('render',this);
38259     },
38260
38261
38262     initEvents: function()
38263     {
38264
38265
38266         // ie scrollbar fix
38267         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38268             document.body.scroll = "no";
38269         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38270             this.el.position('relative');
38271         }
38272         this.id = this.el.id;
38273         this.el.addClass("roo-layout-container");
38274         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38275         if(this.el.dom != document.body ) {
38276             this.el.on('resize', this.layout,this);
38277             this.el.on('show', this.layout,this);
38278         }
38279
38280     },
38281
38282     /**
38283      * Returns true if this layout is currently being updated
38284      * @return {Boolean}
38285      */
38286     isUpdating : function(){
38287         return this.updating;
38288     },
38289
38290     /**
38291      * Suspend the LayoutManager from doing auto-layouts while
38292      * making multiple add or remove calls
38293      */
38294     beginUpdate : function(){
38295         this.updating = true;
38296     },
38297
38298     /**
38299      * Restore auto-layouts and optionally disable the manager from performing a layout
38300      * @param {Boolean} noLayout true to disable a layout update
38301      */
38302     endUpdate : function(noLayout){
38303         this.updating = false;
38304         if(!noLayout){
38305             this.layout();
38306         }
38307     },
38308
38309     layout: function(){
38310         // abstract...
38311     },
38312
38313     onRegionResized : function(region, newSize){
38314         this.fireEvent("regionresized", region, newSize);
38315         this.layout();
38316     },
38317
38318     onRegionCollapsed : function(region){
38319         this.fireEvent("regioncollapsed", region);
38320     },
38321
38322     onRegionExpanded : function(region){
38323         this.fireEvent("regionexpanded", region);
38324     },
38325
38326     /**
38327      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38328      * performs box-model adjustments.
38329      * @return {Object} The size as an object {width: (the width), height: (the height)}
38330      */
38331     getViewSize : function()
38332     {
38333         var size;
38334         if(this.el.dom != document.body){
38335             size = this.el.getSize();
38336         }else{
38337             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38338         }
38339         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38340         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38341         return size;
38342     },
38343
38344     /**
38345      * Returns the Element this layout is bound to.
38346      * @return {Roo.Element}
38347      */
38348     getEl : function(){
38349         return this.el;
38350     },
38351
38352     /**
38353      * Returns the specified region.
38354      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38355      * @return {Roo.LayoutRegion}
38356      */
38357     getRegion : function(target){
38358         return this.regions[target.toLowerCase()];
38359     },
38360
38361     onWindowResize : function(){
38362         if(this.monitorWindowResize){
38363             this.layout();
38364         }
38365     }
38366 });
38367 /*
38368  * Based on:
38369  * Ext JS Library 1.1.1
38370  * Copyright(c) 2006-2007, Ext JS, LLC.
38371  *
38372  * Originally Released Under LGPL - original licence link has changed is not relivant.
38373  *
38374  * Fork - LGPL
38375  * <script type="text/javascript">
38376  */
38377 /**
38378  * @class Roo.bootstrap.layout.Border
38379  * @extends Roo.bootstrap.layout.Manager
38380  * @builder-top
38381  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38382  * please see: examples/bootstrap/nested.html<br><br>
38383  
38384 <b>The container the layout is rendered into can be either the body element or any other element.
38385 If it is not the body element, the container needs to either be an absolute positioned element,
38386 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38387 the container size if it is not the body element.</b>
38388
38389 * @constructor
38390 * Create a new Border
38391 * @param {Object} config Configuration options
38392  */
38393 Roo.bootstrap.layout.Border = function(config){
38394     config = config || {};
38395     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38396     
38397     
38398     
38399     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38400         if(config[region]){
38401             config[region].region = region;
38402             this.addRegion(config[region]);
38403         }
38404     },this);
38405     
38406 };
38407
38408 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38409
38410 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38411     
38412     parent : false, // this might point to a 'nest' or a ???
38413     
38414     /**
38415      * Creates and adds a new region if it doesn't already exist.
38416      * @param {String} target The target region key (north, south, east, west or center).
38417      * @param {Object} config The regions config object
38418      * @return {BorderLayoutRegion} The new region
38419      */
38420     addRegion : function(config)
38421     {
38422         if(!this.regions[config.region]){
38423             var r = this.factory(config);
38424             this.bindRegion(r);
38425         }
38426         return this.regions[config.region];
38427     },
38428
38429     // private (kinda)
38430     bindRegion : function(r){
38431         this.regions[r.config.region] = r;
38432         
38433         r.on("visibilitychange",    this.layout, this);
38434         r.on("paneladded",          this.layout, this);
38435         r.on("panelremoved",        this.layout, this);
38436         r.on("invalidated",         this.layout, this);
38437         r.on("resized",             this.onRegionResized, this);
38438         r.on("collapsed",           this.onRegionCollapsed, this);
38439         r.on("expanded",            this.onRegionExpanded, this);
38440     },
38441
38442     /**
38443      * Performs a layout update.
38444      */
38445     layout : function()
38446     {
38447         if(this.updating) {
38448             return;
38449         }
38450         
38451         // render all the rebions if they have not been done alreayd?
38452         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38453             if(this.regions[region] && !this.regions[region].bodyEl){
38454                 this.regions[region].onRender(this.el)
38455             }
38456         },this);
38457         
38458         var size = this.getViewSize();
38459         var w = size.width;
38460         var h = size.height;
38461         var centerW = w;
38462         var centerH = h;
38463         var centerY = 0;
38464         var centerX = 0;
38465         //var x = 0, y = 0;
38466
38467         var rs = this.regions;
38468         var north = rs["north"];
38469         var south = rs["south"]; 
38470         var west = rs["west"];
38471         var east = rs["east"];
38472         var center = rs["center"];
38473         //if(this.hideOnLayout){ // not supported anymore
38474             //c.el.setStyle("display", "none");
38475         //}
38476         if(north && north.isVisible()){
38477             var b = north.getBox();
38478             var m = north.getMargins();
38479             b.width = w - (m.left+m.right);
38480             b.x = m.left;
38481             b.y = m.top;
38482             centerY = b.height + b.y + m.bottom;
38483             centerH -= centerY;
38484             north.updateBox(this.safeBox(b));
38485         }
38486         if(south && south.isVisible()){
38487             var b = south.getBox();
38488             var m = south.getMargins();
38489             b.width = w - (m.left+m.right);
38490             b.x = m.left;
38491             var totalHeight = (b.height + m.top + m.bottom);
38492             b.y = h - totalHeight + m.top;
38493             centerH -= totalHeight;
38494             south.updateBox(this.safeBox(b));
38495         }
38496         if(west && west.isVisible()){
38497             var b = west.getBox();
38498             var m = west.getMargins();
38499             b.height = centerH - (m.top+m.bottom);
38500             b.x = m.left;
38501             b.y = centerY + m.top;
38502             var totalWidth = (b.width + m.left + m.right);
38503             centerX += totalWidth;
38504             centerW -= totalWidth;
38505             west.updateBox(this.safeBox(b));
38506         }
38507         if(east && east.isVisible()){
38508             var b = east.getBox();
38509             var m = east.getMargins();
38510             b.height = centerH - (m.top+m.bottom);
38511             var totalWidth = (b.width + m.left + m.right);
38512             b.x = w - totalWidth + m.left;
38513             b.y = centerY + m.top;
38514             centerW -= totalWidth;
38515             east.updateBox(this.safeBox(b));
38516         }
38517         if(center){
38518             var m = center.getMargins();
38519             var centerBox = {
38520                 x: centerX + m.left,
38521                 y: centerY + m.top,
38522                 width: centerW - (m.left+m.right),
38523                 height: centerH - (m.top+m.bottom)
38524             };
38525             //if(this.hideOnLayout){
38526                 //center.el.setStyle("display", "block");
38527             //}
38528             center.updateBox(this.safeBox(centerBox));
38529         }
38530         this.el.repaint();
38531         this.fireEvent("layout", this);
38532     },
38533
38534     // private
38535     safeBox : function(box){
38536         box.width = Math.max(0, box.width);
38537         box.height = Math.max(0, box.height);
38538         return box;
38539     },
38540
38541     /**
38542      * Adds a ContentPanel (or subclass) to this layout.
38543      * @param {String} target The target region key (north, south, east, west or center).
38544      * @param {Roo.ContentPanel} panel The panel to add
38545      * @return {Roo.ContentPanel} The added panel
38546      */
38547     add : function(target, panel){
38548          
38549         target = target.toLowerCase();
38550         return this.regions[target].add(panel);
38551     },
38552
38553     /**
38554      * Remove a ContentPanel (or subclass) to this layout.
38555      * @param {String} target The target region key (north, south, east, west or center).
38556      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38557      * @return {Roo.ContentPanel} The removed panel
38558      */
38559     remove : function(target, panel){
38560         target = target.toLowerCase();
38561         return this.regions[target].remove(panel);
38562     },
38563
38564     /**
38565      * Searches all regions for a panel with the specified id
38566      * @param {String} panelId
38567      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38568      */
38569     findPanel : function(panelId){
38570         var rs = this.regions;
38571         for(var target in rs){
38572             if(typeof rs[target] != "function"){
38573                 var p = rs[target].getPanel(panelId);
38574                 if(p){
38575                     return p;
38576                 }
38577             }
38578         }
38579         return null;
38580     },
38581
38582     /**
38583      * Searches all regions for a panel with the specified id and activates (shows) it.
38584      * @param {String/ContentPanel} panelId The panels id or the panel itself
38585      * @return {Roo.ContentPanel} The shown panel or null
38586      */
38587     showPanel : function(panelId) {
38588       var rs = this.regions;
38589       for(var target in rs){
38590          var r = rs[target];
38591          if(typeof r != "function"){
38592             if(r.hasPanel(panelId)){
38593                return r.showPanel(panelId);
38594             }
38595          }
38596       }
38597       return null;
38598    },
38599
38600    /**
38601      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38602      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38603      */
38604    /*
38605     restoreState : function(provider){
38606         if(!provider){
38607             provider = Roo.state.Manager;
38608         }
38609         var sm = new Roo.LayoutStateManager();
38610         sm.init(this, provider);
38611     },
38612 */
38613  
38614  
38615     /**
38616      * Adds a xtype elements to the layout.
38617      * <pre><code>
38618
38619 layout.addxtype({
38620        xtype : 'ContentPanel',
38621        region: 'west',
38622        items: [ .... ]
38623    }
38624 );
38625
38626 layout.addxtype({
38627         xtype : 'NestedLayoutPanel',
38628         region: 'west',
38629         layout: {
38630            center: { },
38631            west: { }   
38632         },
38633         items : [ ... list of content panels or nested layout panels.. ]
38634    }
38635 );
38636 </code></pre>
38637      * @param {Object} cfg Xtype definition of item to add.
38638      */
38639     addxtype : function(cfg)
38640     {
38641         // basically accepts a pannel...
38642         // can accept a layout region..!?!?
38643         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38644         
38645         
38646         // theory?  children can only be panels??
38647         
38648         //if (!cfg.xtype.match(/Panel$/)) {
38649         //    return false;
38650         //}
38651         var ret = false;
38652         
38653         if (typeof(cfg.region) == 'undefined') {
38654             Roo.log("Failed to add Panel, region was not set");
38655             Roo.log(cfg);
38656             return false;
38657         }
38658         var region = cfg.region;
38659         delete cfg.region;
38660         
38661           
38662         var xitems = [];
38663         if (cfg.items) {
38664             xitems = cfg.items;
38665             delete cfg.items;
38666         }
38667         var nb = false;
38668         
38669         if ( region == 'center') {
38670             Roo.log("Center: " + cfg.title);
38671         }
38672         
38673         
38674         switch(cfg.xtype) 
38675         {
38676             case 'Content':  // ContentPanel (el, cfg)
38677             case 'Scroll':  // ContentPanel (el, cfg)
38678             case 'View': 
38679                 cfg.autoCreate = cfg.autoCreate || true;
38680                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38681                 //} else {
38682                 //    var el = this.el.createChild();
38683                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38684                 //}
38685                 
38686                 this.add(region, ret);
38687                 break;
38688             
38689             /*
38690             case 'TreePanel': // our new panel!
38691                 cfg.el = this.el.createChild();
38692                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38693                 this.add(region, ret);
38694                 break;
38695             */
38696             
38697             case 'Nest': 
38698                 // create a new Layout (which is  a Border Layout...
38699                 
38700                 var clayout = cfg.layout;
38701                 clayout.el  = this.el.createChild();
38702                 clayout.items   = clayout.items  || [];
38703                 
38704                 delete cfg.layout;
38705                 
38706                 // replace this exitems with the clayout ones..
38707                 xitems = clayout.items;
38708                  
38709                 // force background off if it's in center...
38710                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38711                     cfg.background = false;
38712                 }
38713                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38714                 
38715                 
38716                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38717                 //console.log('adding nested layout panel '  + cfg.toSource());
38718                 this.add(region, ret);
38719                 nb = {}; /// find first...
38720                 break;
38721             
38722             case 'Grid':
38723                 
38724                 // needs grid and region
38725                 
38726                 //var el = this.getRegion(region).el.createChild();
38727                 /*
38728                  *var el = this.el.createChild();
38729                 // create the grid first...
38730                 cfg.grid.container = el;
38731                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38732                 */
38733                 
38734                 if (region == 'center' && this.active ) {
38735                     cfg.background = false;
38736                 }
38737                 
38738                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38739                 
38740                 this.add(region, ret);
38741                 /*
38742                 if (cfg.background) {
38743                     // render grid on panel activation (if panel background)
38744                     ret.on('activate', function(gp) {
38745                         if (!gp.grid.rendered) {
38746                     //        gp.grid.render(el);
38747                         }
38748                     });
38749                 } else {
38750                   //  cfg.grid.render(el);
38751                 }
38752                 */
38753                 break;
38754            
38755            
38756             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38757                 // it was the old xcomponent building that caused this before.
38758                 // espeically if border is the top element in the tree.
38759                 ret = this;
38760                 break; 
38761                 
38762                     
38763                 
38764                 
38765                 
38766             default:
38767                 /*
38768                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38769                     
38770                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38771                     this.add(region, ret);
38772                 } else {
38773                 */
38774                     Roo.log(cfg);
38775                     throw "Can not add '" + cfg.xtype + "' to Border";
38776                     return null;
38777              
38778                                 
38779              
38780         }
38781         this.beginUpdate();
38782         // add children..
38783         var region = '';
38784         var abn = {};
38785         Roo.each(xitems, function(i)  {
38786             region = nb && i.region ? i.region : false;
38787             
38788             var add = ret.addxtype(i);
38789            
38790             if (region) {
38791                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38792                 if (!i.background) {
38793                     abn[region] = nb[region] ;
38794                 }
38795             }
38796             
38797         });
38798         this.endUpdate();
38799
38800         // make the last non-background panel active..
38801         //if (nb) { Roo.log(abn); }
38802         if (nb) {
38803             
38804             for(var r in abn) {
38805                 region = this.getRegion(r);
38806                 if (region) {
38807                     // tried using nb[r], but it does not work..
38808                      
38809                     region.showPanel(abn[r]);
38810                    
38811                 }
38812             }
38813         }
38814         return ret;
38815         
38816     },
38817     
38818     
38819 // private
38820     factory : function(cfg)
38821     {
38822         
38823         var validRegions = Roo.bootstrap.layout.Border.regions;
38824
38825         var target = cfg.region;
38826         cfg.mgr = this;
38827         
38828         var r = Roo.bootstrap.layout;
38829         Roo.log(target);
38830         switch(target){
38831             case "north":
38832                 return new r.North(cfg);
38833             case "south":
38834                 return new r.South(cfg);
38835             case "east":
38836                 return new r.East(cfg);
38837             case "west":
38838                 return new r.West(cfg);
38839             case "center":
38840                 return new r.Center(cfg);
38841         }
38842         throw 'Layout region "'+target+'" not supported.';
38843     }
38844     
38845     
38846 });
38847  /*
38848  * Based on:
38849  * Ext JS Library 1.1.1
38850  * Copyright(c) 2006-2007, Ext JS, LLC.
38851  *
38852  * Originally Released Under LGPL - original licence link has changed is not relivant.
38853  *
38854  * Fork - LGPL
38855  * <script type="text/javascript">
38856  */
38857  
38858 /**
38859  * @class Roo.bootstrap.layout.Basic
38860  * @extends Roo.util.Observable
38861  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38862  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38863  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38864  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38865  * @cfg {string}   region  the region that it inhabits..
38866  * @cfg {bool}   skipConfig skip config?
38867  * 
38868
38869  */
38870 Roo.bootstrap.layout.Basic = function(config){
38871     
38872     this.mgr = config.mgr;
38873     
38874     this.position = config.region;
38875     
38876     var skipConfig = config.skipConfig;
38877     
38878     this.events = {
38879         /**
38880          * @scope Roo.BasicLayoutRegion
38881          */
38882         
38883         /**
38884          * @event beforeremove
38885          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38886          * @param {Roo.LayoutRegion} this
38887          * @param {Roo.ContentPanel} panel The panel
38888          * @param {Object} e The cancel event object
38889          */
38890         "beforeremove" : true,
38891         /**
38892          * @event invalidated
38893          * Fires when the layout for this region is changed.
38894          * @param {Roo.LayoutRegion} this
38895          */
38896         "invalidated" : true,
38897         /**
38898          * @event visibilitychange
38899          * Fires when this region is shown or hidden 
38900          * @param {Roo.LayoutRegion} this
38901          * @param {Boolean} visibility true or false
38902          */
38903         "visibilitychange" : true,
38904         /**
38905          * @event paneladded
38906          * Fires when a panel is added. 
38907          * @param {Roo.LayoutRegion} this
38908          * @param {Roo.ContentPanel} panel The panel
38909          */
38910         "paneladded" : true,
38911         /**
38912          * @event panelremoved
38913          * Fires when a panel is removed. 
38914          * @param {Roo.LayoutRegion} this
38915          * @param {Roo.ContentPanel} panel The panel
38916          */
38917         "panelremoved" : true,
38918         /**
38919          * @event beforecollapse
38920          * Fires when this region before collapse.
38921          * @param {Roo.LayoutRegion} this
38922          */
38923         "beforecollapse" : true,
38924         /**
38925          * @event collapsed
38926          * Fires when this region is collapsed.
38927          * @param {Roo.LayoutRegion} this
38928          */
38929         "collapsed" : true,
38930         /**
38931          * @event expanded
38932          * Fires when this region is expanded.
38933          * @param {Roo.LayoutRegion} this
38934          */
38935         "expanded" : true,
38936         /**
38937          * @event slideshow
38938          * Fires when this region is slid into view.
38939          * @param {Roo.LayoutRegion} this
38940          */
38941         "slideshow" : true,
38942         /**
38943          * @event slidehide
38944          * Fires when this region slides out of view. 
38945          * @param {Roo.LayoutRegion} this
38946          */
38947         "slidehide" : true,
38948         /**
38949          * @event panelactivated
38950          * Fires when a panel is activated. 
38951          * @param {Roo.LayoutRegion} this
38952          * @param {Roo.ContentPanel} panel The activated panel
38953          */
38954         "panelactivated" : true,
38955         /**
38956          * @event resized
38957          * Fires when the user resizes this region. 
38958          * @param {Roo.LayoutRegion} this
38959          * @param {Number} newSize The new size (width for east/west, height for north/south)
38960          */
38961         "resized" : true
38962     };
38963     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38964     this.panels = new Roo.util.MixedCollection();
38965     this.panels.getKey = this.getPanelId.createDelegate(this);
38966     this.box = null;
38967     this.activePanel = null;
38968     // ensure listeners are added...
38969     
38970     if (config.listeners || config.events) {
38971         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38972             listeners : config.listeners || {},
38973             events : config.events || {}
38974         });
38975     }
38976     
38977     if(skipConfig !== true){
38978         this.applyConfig(config);
38979     }
38980 };
38981
38982 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38983 {
38984     getPanelId : function(p){
38985         return p.getId();
38986     },
38987     
38988     applyConfig : function(config){
38989         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38990         this.config = config;
38991         
38992     },
38993     
38994     /**
38995      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38996      * the width, for horizontal (north, south) the height.
38997      * @param {Number} newSize The new width or height
38998      */
38999     resizeTo : function(newSize){
39000         var el = this.el ? this.el :
39001                  (this.activePanel ? this.activePanel.getEl() : null);
39002         if(el){
39003             switch(this.position){
39004                 case "east":
39005                 case "west":
39006                     el.setWidth(newSize);
39007                     this.fireEvent("resized", this, newSize);
39008                 break;
39009                 case "north":
39010                 case "south":
39011                     el.setHeight(newSize);
39012                     this.fireEvent("resized", this, newSize);
39013                 break;                
39014             }
39015         }
39016     },
39017     
39018     getBox : function(){
39019         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39020     },
39021     
39022     getMargins : function(){
39023         return this.margins;
39024     },
39025     
39026     updateBox : function(box){
39027         this.box = box;
39028         var el = this.activePanel.getEl();
39029         el.dom.style.left = box.x + "px";
39030         el.dom.style.top = box.y + "px";
39031         this.activePanel.setSize(box.width, box.height);
39032     },
39033     
39034     /**
39035      * Returns the container element for this region.
39036      * @return {Roo.Element}
39037      */
39038     getEl : function(){
39039         return this.activePanel;
39040     },
39041     
39042     /**
39043      * Returns true if this region is currently visible.
39044      * @return {Boolean}
39045      */
39046     isVisible : function(){
39047         return this.activePanel ? true : false;
39048     },
39049     
39050     setActivePanel : function(panel){
39051         panel = this.getPanel(panel);
39052         if(this.activePanel && this.activePanel != panel){
39053             this.activePanel.setActiveState(false);
39054             this.activePanel.getEl().setLeftTop(-10000,-10000);
39055         }
39056         this.activePanel = panel;
39057         panel.setActiveState(true);
39058         if(this.box){
39059             panel.setSize(this.box.width, this.box.height);
39060         }
39061         this.fireEvent("panelactivated", this, panel);
39062         this.fireEvent("invalidated");
39063     },
39064     
39065     /**
39066      * Show the specified panel.
39067      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39068      * @return {Roo.ContentPanel} The shown panel or null
39069      */
39070     showPanel : function(panel){
39071         panel = this.getPanel(panel);
39072         if(panel){
39073             this.setActivePanel(panel);
39074         }
39075         return panel;
39076     },
39077     
39078     /**
39079      * Get the active panel for this region.
39080      * @return {Roo.ContentPanel} The active panel or null
39081      */
39082     getActivePanel : function(){
39083         return this.activePanel;
39084     },
39085     
39086     /**
39087      * Add the passed ContentPanel(s)
39088      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39089      * @return {Roo.ContentPanel} The panel added (if only one was added)
39090      */
39091     add : function(panel){
39092         if(arguments.length > 1){
39093             for(var i = 0, len = arguments.length; i < len; i++) {
39094                 this.add(arguments[i]);
39095             }
39096             return null;
39097         }
39098         if(this.hasPanel(panel)){
39099             this.showPanel(panel);
39100             return panel;
39101         }
39102         var el = panel.getEl();
39103         if(el.dom.parentNode != this.mgr.el.dom){
39104             this.mgr.el.dom.appendChild(el.dom);
39105         }
39106         if(panel.setRegion){
39107             panel.setRegion(this);
39108         }
39109         this.panels.add(panel);
39110         el.setStyle("position", "absolute");
39111         if(!panel.background){
39112             this.setActivePanel(panel);
39113             if(this.config.initialSize && this.panels.getCount()==1){
39114                 this.resizeTo(this.config.initialSize);
39115             }
39116         }
39117         this.fireEvent("paneladded", this, panel);
39118         return panel;
39119     },
39120     
39121     /**
39122      * Returns true if the panel is in this region.
39123      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39124      * @return {Boolean}
39125      */
39126     hasPanel : function(panel){
39127         if(typeof panel == "object"){ // must be panel obj
39128             panel = panel.getId();
39129         }
39130         return this.getPanel(panel) ? true : false;
39131     },
39132     
39133     /**
39134      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39135      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39136      * @param {Boolean} preservePanel Overrides the config preservePanel option
39137      * @return {Roo.ContentPanel} The panel that was removed
39138      */
39139     remove : function(panel, preservePanel){
39140         panel = this.getPanel(panel);
39141         if(!panel){
39142             return null;
39143         }
39144         var e = {};
39145         this.fireEvent("beforeremove", this, panel, e);
39146         if(e.cancel === true){
39147             return null;
39148         }
39149         var panelId = panel.getId();
39150         this.panels.removeKey(panelId);
39151         return panel;
39152     },
39153     
39154     /**
39155      * Returns the panel specified or null if it's not in this region.
39156      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39157      * @return {Roo.ContentPanel}
39158      */
39159     getPanel : function(id){
39160         if(typeof id == "object"){ // must be panel obj
39161             return id;
39162         }
39163         return this.panels.get(id);
39164     },
39165     
39166     /**
39167      * Returns this regions position (north/south/east/west/center).
39168      * @return {String} 
39169      */
39170     getPosition: function(){
39171         return this.position;    
39172     }
39173 });/*
39174  * Based on:
39175  * Ext JS Library 1.1.1
39176  * Copyright(c) 2006-2007, Ext JS, LLC.
39177  *
39178  * Originally Released Under LGPL - original licence link has changed is not relivant.
39179  *
39180  * Fork - LGPL
39181  * <script type="text/javascript">
39182  */
39183  
39184 /**
39185  * @class Roo.bootstrap.layout.Region
39186  * @extends Roo.bootstrap.layout.Basic
39187  * This class represents a region in a layout manager.
39188  
39189  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39190  * @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})
39191  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39192  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39193  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39194  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39195  * @cfg {String}    title           The title for the region (overrides panel titles)
39196  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39197  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39198  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39199  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39200  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39201  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39202  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39203  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39204  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39205  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39206
39207  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39208  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39209  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39210  * @cfg {Number}    width           For East/West panels
39211  * @cfg {Number}    height          For North/South panels
39212  * @cfg {Boolean}   split           To show the splitter
39213  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39214  * 
39215  * @cfg {string}   cls             Extra CSS classes to add to region
39216  * 
39217  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39218  * @cfg {string}   region  the region that it inhabits..
39219  *
39220
39221  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39222  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39223
39224  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39225  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39226  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39227  */
39228 Roo.bootstrap.layout.Region = function(config)
39229 {
39230     this.applyConfig(config);
39231
39232     var mgr = config.mgr;
39233     var pos = config.region;
39234     config.skipConfig = true;
39235     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39236     
39237     if (mgr.el) {
39238         this.onRender(mgr.el);   
39239     }
39240      
39241     this.visible = true;
39242     this.collapsed = false;
39243     this.unrendered_panels = [];
39244 };
39245
39246 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39247
39248     position: '', // set by wrapper (eg. north/south etc..)
39249     unrendered_panels : null,  // unrendered panels.
39250     
39251     tabPosition : false,
39252     
39253     mgr: false, // points to 'Border'
39254     
39255     
39256     createBody : function(){
39257         /** This region's body element 
39258         * @type Roo.Element */
39259         this.bodyEl = this.el.createChild({
39260                 tag: "div",
39261                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39262         });
39263     },
39264
39265     onRender: function(ctr, pos)
39266     {
39267         var dh = Roo.DomHelper;
39268         /** This region's container element 
39269         * @type Roo.Element */
39270         this.el = dh.append(ctr.dom, {
39271                 tag: "div",
39272                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39273             }, true);
39274         /** This region's title element 
39275         * @type Roo.Element */
39276     
39277         this.titleEl = dh.append(this.el.dom,  {
39278                 tag: "div",
39279                 unselectable: "on",
39280                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39281                 children:[
39282                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39283                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39284                 ]
39285             }, true);
39286         
39287         this.titleEl.enableDisplayMode();
39288         /** This region's title text element 
39289         * @type HTMLElement */
39290         this.titleTextEl = this.titleEl.dom.firstChild;
39291         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39292         /*
39293         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39294         this.closeBtn.enableDisplayMode();
39295         this.closeBtn.on("click", this.closeClicked, this);
39296         this.closeBtn.hide();
39297     */
39298         this.createBody(this.config);
39299         if(this.config.hideWhenEmpty){
39300             this.hide();
39301             this.on("paneladded", this.validateVisibility, this);
39302             this.on("panelremoved", this.validateVisibility, this);
39303         }
39304         if(this.autoScroll){
39305             this.bodyEl.setStyle("overflow", "auto");
39306         }else{
39307             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39308         }
39309         //if(c.titlebar !== false){
39310             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39311                 this.titleEl.hide();
39312             }else{
39313                 this.titleEl.show();
39314                 if(this.config.title){
39315                     this.titleTextEl.innerHTML = this.config.title;
39316                 }
39317             }
39318         //}
39319         if(this.config.collapsed){
39320             this.collapse(true);
39321         }
39322         if(this.config.hidden){
39323             this.hide();
39324         }
39325         
39326         if (this.unrendered_panels && this.unrendered_panels.length) {
39327             for (var i =0;i< this.unrendered_panels.length; i++) {
39328                 this.add(this.unrendered_panels[i]);
39329             }
39330             this.unrendered_panels = null;
39331             
39332         }
39333         
39334     },
39335     
39336     applyConfig : function(c)
39337     {
39338         /*
39339          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39340             var dh = Roo.DomHelper;
39341             if(c.titlebar !== false){
39342                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39343                 this.collapseBtn.on("click", this.collapse, this);
39344                 this.collapseBtn.enableDisplayMode();
39345                 /*
39346                 if(c.showPin === true || this.showPin){
39347                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39348                     this.stickBtn.enableDisplayMode();
39349                     this.stickBtn.on("click", this.expand, this);
39350                     this.stickBtn.hide();
39351                 }
39352                 
39353             }
39354             */
39355             /** This region's collapsed element
39356             * @type Roo.Element */
39357             /*
39358              *
39359             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39360                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39361             ]}, true);
39362             
39363             if(c.floatable !== false){
39364                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39365                this.collapsedEl.on("click", this.collapseClick, this);
39366             }
39367
39368             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39369                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39370                    id: "message", unselectable: "on", style:{"float":"left"}});
39371                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39372              }
39373             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39374             this.expandBtn.on("click", this.expand, this);
39375             
39376         }
39377         
39378         if(this.collapseBtn){
39379             this.collapseBtn.setVisible(c.collapsible == true);
39380         }
39381         
39382         this.cmargins = c.cmargins || this.cmargins ||
39383                          (this.position == "west" || this.position == "east" ?
39384                              {top: 0, left: 2, right:2, bottom: 0} :
39385                              {top: 2, left: 0, right:0, bottom: 2});
39386         */
39387         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39388         
39389         
39390         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39391         
39392         this.autoScroll = c.autoScroll || false;
39393         
39394         
39395        
39396         
39397         this.duration = c.duration || .30;
39398         this.slideDuration = c.slideDuration || .45;
39399         this.config = c;
39400        
39401     },
39402     /**
39403      * Returns true if this region is currently visible.
39404      * @return {Boolean}
39405      */
39406     isVisible : function(){
39407         return this.visible;
39408     },
39409
39410     /**
39411      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39412      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39413      */
39414     //setCollapsedTitle : function(title){
39415     //    title = title || "&#160;";
39416      //   if(this.collapsedTitleTextEl){
39417       //      this.collapsedTitleTextEl.innerHTML = title;
39418        // }
39419     //},
39420
39421     getBox : function(){
39422         var b;
39423       //  if(!this.collapsed){
39424             b = this.el.getBox(false, true);
39425        // }else{
39426           //  b = this.collapsedEl.getBox(false, true);
39427         //}
39428         return b;
39429     },
39430
39431     getMargins : function(){
39432         return this.margins;
39433         //return this.collapsed ? this.cmargins : this.margins;
39434     },
39435 /*
39436     highlight : function(){
39437         this.el.addClass("x-layout-panel-dragover");
39438     },
39439
39440     unhighlight : function(){
39441         this.el.removeClass("x-layout-panel-dragover");
39442     },
39443 */
39444     updateBox : function(box)
39445     {
39446         if (!this.bodyEl) {
39447             return; // not rendered yet..
39448         }
39449         
39450         this.box = box;
39451         if(!this.collapsed){
39452             this.el.dom.style.left = box.x + "px";
39453             this.el.dom.style.top = box.y + "px";
39454             this.updateBody(box.width, box.height);
39455         }else{
39456             this.collapsedEl.dom.style.left = box.x + "px";
39457             this.collapsedEl.dom.style.top = box.y + "px";
39458             this.collapsedEl.setSize(box.width, box.height);
39459         }
39460         if(this.tabs){
39461             this.tabs.autoSizeTabs();
39462         }
39463     },
39464
39465     updateBody : function(w, h)
39466     {
39467         if(w !== null){
39468             this.el.setWidth(w);
39469             w -= this.el.getBorderWidth("rl");
39470             if(this.config.adjustments){
39471                 w += this.config.adjustments[0];
39472             }
39473         }
39474         if(h !== null && h > 0){
39475             this.el.setHeight(h);
39476             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39477             h -= this.el.getBorderWidth("tb");
39478             if(this.config.adjustments){
39479                 h += this.config.adjustments[1];
39480             }
39481             this.bodyEl.setHeight(h);
39482             if(this.tabs){
39483                 h = this.tabs.syncHeight(h);
39484             }
39485         }
39486         if(this.panelSize){
39487             w = w !== null ? w : this.panelSize.width;
39488             h = h !== null ? h : this.panelSize.height;
39489         }
39490         if(this.activePanel){
39491             var el = this.activePanel.getEl();
39492             w = w !== null ? w : el.getWidth();
39493             h = h !== null ? h : el.getHeight();
39494             this.panelSize = {width: w, height: h};
39495             this.activePanel.setSize(w, h);
39496         }
39497         if(Roo.isIE && this.tabs){
39498             this.tabs.el.repaint();
39499         }
39500     },
39501
39502     /**
39503      * Returns the container element for this region.
39504      * @return {Roo.Element}
39505      */
39506     getEl : function(){
39507         return this.el;
39508     },
39509
39510     /**
39511      * Hides this region.
39512      */
39513     hide : function(){
39514         //if(!this.collapsed){
39515             this.el.dom.style.left = "-2000px";
39516             this.el.hide();
39517         //}else{
39518          //   this.collapsedEl.dom.style.left = "-2000px";
39519          //   this.collapsedEl.hide();
39520        // }
39521         this.visible = false;
39522         this.fireEvent("visibilitychange", this, false);
39523     },
39524
39525     /**
39526      * Shows this region if it was previously hidden.
39527      */
39528     show : function(){
39529         //if(!this.collapsed){
39530             this.el.show();
39531         //}else{
39532         //    this.collapsedEl.show();
39533        // }
39534         this.visible = true;
39535         this.fireEvent("visibilitychange", this, true);
39536     },
39537 /*
39538     closeClicked : function(){
39539         if(this.activePanel){
39540             this.remove(this.activePanel);
39541         }
39542     },
39543
39544     collapseClick : function(e){
39545         if(this.isSlid){
39546            e.stopPropagation();
39547            this.slideIn();
39548         }else{
39549            e.stopPropagation();
39550            this.slideOut();
39551         }
39552     },
39553 */
39554     /**
39555      * Collapses this region.
39556      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39557      */
39558     /*
39559     collapse : function(skipAnim, skipCheck = false){
39560         if(this.collapsed) {
39561             return;
39562         }
39563         
39564         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39565             
39566             this.collapsed = true;
39567             if(this.split){
39568                 this.split.el.hide();
39569             }
39570             if(this.config.animate && skipAnim !== true){
39571                 this.fireEvent("invalidated", this);
39572                 this.animateCollapse();
39573             }else{
39574                 this.el.setLocation(-20000,-20000);
39575                 this.el.hide();
39576                 this.collapsedEl.show();
39577                 this.fireEvent("collapsed", this);
39578                 this.fireEvent("invalidated", this);
39579             }
39580         }
39581         
39582     },
39583 */
39584     animateCollapse : function(){
39585         // overridden
39586     },
39587
39588     /**
39589      * Expands this region if it was previously collapsed.
39590      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39591      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39592      */
39593     /*
39594     expand : function(e, skipAnim){
39595         if(e) {
39596             e.stopPropagation();
39597         }
39598         if(!this.collapsed || this.el.hasActiveFx()) {
39599             return;
39600         }
39601         if(this.isSlid){
39602             this.afterSlideIn();
39603             skipAnim = true;
39604         }
39605         this.collapsed = false;
39606         if(this.config.animate && skipAnim !== true){
39607             this.animateExpand();
39608         }else{
39609             this.el.show();
39610             if(this.split){
39611                 this.split.el.show();
39612             }
39613             this.collapsedEl.setLocation(-2000,-2000);
39614             this.collapsedEl.hide();
39615             this.fireEvent("invalidated", this);
39616             this.fireEvent("expanded", this);
39617         }
39618     },
39619 */
39620     animateExpand : function(){
39621         // overridden
39622     },
39623
39624     initTabs : function()
39625     {
39626         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39627         
39628         var ts = new Roo.bootstrap.panel.Tabs({
39629             el: this.bodyEl.dom,
39630             region : this,
39631             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39632             disableTooltips: this.config.disableTabTips,
39633             toolbar : this.config.toolbar
39634         });
39635         
39636         if(this.config.hideTabs){
39637             ts.stripWrap.setDisplayed(false);
39638         }
39639         this.tabs = ts;
39640         ts.resizeTabs = this.config.resizeTabs === true;
39641         ts.minTabWidth = this.config.minTabWidth || 40;
39642         ts.maxTabWidth = this.config.maxTabWidth || 250;
39643         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39644         ts.monitorResize = false;
39645         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39646         ts.bodyEl.addClass('roo-layout-tabs-body');
39647         this.panels.each(this.initPanelAsTab, this);
39648     },
39649
39650     initPanelAsTab : function(panel){
39651         var ti = this.tabs.addTab(
39652             panel.getEl().id,
39653             panel.getTitle(),
39654             null,
39655             this.config.closeOnTab && panel.isClosable(),
39656             panel.tpl
39657         );
39658         if(panel.tabTip !== undefined){
39659             ti.setTooltip(panel.tabTip);
39660         }
39661         ti.on("activate", function(){
39662               this.setActivePanel(panel);
39663         }, this);
39664         
39665         if(this.config.closeOnTab){
39666             ti.on("beforeclose", function(t, e){
39667                 e.cancel = true;
39668                 this.remove(panel);
39669             }, this);
39670         }
39671         
39672         panel.tabItem = ti;
39673         
39674         return ti;
39675     },
39676
39677     updatePanelTitle : function(panel, title)
39678     {
39679         if(this.activePanel == panel){
39680             this.updateTitle(title);
39681         }
39682         if(this.tabs){
39683             var ti = this.tabs.getTab(panel.getEl().id);
39684             ti.setText(title);
39685             if(panel.tabTip !== undefined){
39686                 ti.setTooltip(panel.tabTip);
39687             }
39688         }
39689     },
39690
39691     updateTitle : function(title){
39692         if(this.titleTextEl && !this.config.title){
39693             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39694         }
39695     },
39696
39697     setActivePanel : function(panel)
39698     {
39699         panel = this.getPanel(panel);
39700         if(this.activePanel && this.activePanel != panel){
39701             if(this.activePanel.setActiveState(false) === false){
39702                 return;
39703             }
39704         }
39705         this.activePanel = panel;
39706         panel.setActiveState(true);
39707         if(this.panelSize){
39708             panel.setSize(this.panelSize.width, this.panelSize.height);
39709         }
39710         if(this.closeBtn){
39711             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39712         }
39713         this.updateTitle(panel.getTitle());
39714         if(this.tabs){
39715             this.fireEvent("invalidated", this);
39716         }
39717         this.fireEvent("panelactivated", this, panel);
39718     },
39719
39720     /**
39721      * Shows the specified panel.
39722      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39723      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39724      */
39725     showPanel : function(panel)
39726     {
39727         panel = this.getPanel(panel);
39728         if(panel){
39729             if(this.tabs){
39730                 var tab = this.tabs.getTab(panel.getEl().id);
39731                 if(tab.isHidden()){
39732                     this.tabs.unhideTab(tab.id);
39733                 }
39734                 tab.activate();
39735             }else{
39736                 this.setActivePanel(panel);
39737             }
39738         }
39739         return panel;
39740     },
39741
39742     /**
39743      * Get the active panel for this region.
39744      * @return {Roo.ContentPanel} The active panel or null
39745      */
39746     getActivePanel : function(){
39747         return this.activePanel;
39748     },
39749
39750     validateVisibility : function(){
39751         if(this.panels.getCount() < 1){
39752             this.updateTitle("&#160;");
39753             this.closeBtn.hide();
39754             this.hide();
39755         }else{
39756             if(!this.isVisible()){
39757                 this.show();
39758             }
39759         }
39760     },
39761
39762     /**
39763      * Adds the passed ContentPanel(s) to this region.
39764      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39765      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39766      */
39767     add : function(panel)
39768     {
39769         if(arguments.length > 1){
39770             for(var i = 0, len = arguments.length; i < len; i++) {
39771                 this.add(arguments[i]);
39772             }
39773             return null;
39774         }
39775         
39776         // if we have not been rendered yet, then we can not really do much of this..
39777         if (!this.bodyEl) {
39778             this.unrendered_panels.push(panel);
39779             return panel;
39780         }
39781         
39782         
39783         
39784         
39785         if(this.hasPanel(panel)){
39786             this.showPanel(panel);
39787             return panel;
39788         }
39789         panel.setRegion(this);
39790         this.panels.add(panel);
39791        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39792             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39793             // and hide them... ???
39794             this.bodyEl.dom.appendChild(panel.getEl().dom);
39795             if(panel.background !== true){
39796                 this.setActivePanel(panel);
39797             }
39798             this.fireEvent("paneladded", this, panel);
39799             return panel;
39800         }
39801         */
39802         if(!this.tabs){
39803             this.initTabs();
39804         }else{
39805             this.initPanelAsTab(panel);
39806         }
39807         
39808         
39809         if(panel.background !== true){
39810             this.tabs.activate(panel.getEl().id);
39811         }
39812         this.fireEvent("paneladded", this, panel);
39813         return panel;
39814     },
39815
39816     /**
39817      * Hides the tab for the specified panel.
39818      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39819      */
39820     hidePanel : function(panel){
39821         if(this.tabs && (panel = this.getPanel(panel))){
39822             this.tabs.hideTab(panel.getEl().id);
39823         }
39824     },
39825
39826     /**
39827      * Unhides the tab for a previously hidden panel.
39828      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39829      */
39830     unhidePanel : function(panel){
39831         if(this.tabs && (panel = this.getPanel(panel))){
39832             this.tabs.unhideTab(panel.getEl().id);
39833         }
39834     },
39835
39836     clearPanels : function(){
39837         while(this.panels.getCount() > 0){
39838              this.remove(this.panels.first());
39839         }
39840     },
39841
39842     /**
39843      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39844      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39845      * @param {Boolean} preservePanel Overrides the config preservePanel option
39846      * @return {Roo.ContentPanel} The panel that was removed
39847      */
39848     remove : function(panel, preservePanel)
39849     {
39850         panel = this.getPanel(panel);
39851         if(!panel){
39852             return null;
39853         }
39854         var e = {};
39855         this.fireEvent("beforeremove", this, panel, e);
39856         if(e.cancel === true){
39857             return null;
39858         }
39859         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39860         var panelId = panel.getId();
39861         this.panels.removeKey(panelId);
39862         if(preservePanel){
39863             document.body.appendChild(panel.getEl().dom);
39864         }
39865         if(this.tabs){
39866             this.tabs.removeTab(panel.getEl().id);
39867         }else if (!preservePanel){
39868             this.bodyEl.dom.removeChild(panel.getEl().dom);
39869         }
39870         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39871             var p = this.panels.first();
39872             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39873             tempEl.appendChild(p.getEl().dom);
39874             this.bodyEl.update("");
39875             this.bodyEl.dom.appendChild(p.getEl().dom);
39876             tempEl = null;
39877             this.updateTitle(p.getTitle());
39878             this.tabs = null;
39879             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39880             this.setActivePanel(p);
39881         }
39882         panel.setRegion(null);
39883         if(this.activePanel == panel){
39884             this.activePanel = null;
39885         }
39886         if(this.config.autoDestroy !== false && preservePanel !== true){
39887             try{panel.destroy();}catch(e){}
39888         }
39889         this.fireEvent("panelremoved", this, panel);
39890         return panel;
39891     },
39892
39893     /**
39894      * Returns the TabPanel component used by this region
39895      * @return {Roo.TabPanel}
39896      */
39897     getTabs : function(){
39898         return this.tabs;
39899     },
39900
39901     createTool : function(parentEl, className){
39902         var btn = Roo.DomHelper.append(parentEl, {
39903             tag: "div",
39904             cls: "x-layout-tools-button",
39905             children: [ {
39906                 tag: "div",
39907                 cls: "roo-layout-tools-button-inner " + className,
39908                 html: "&#160;"
39909             }]
39910         }, true);
39911         btn.addClassOnOver("roo-layout-tools-button-over");
39912         return btn;
39913     }
39914 });/*
39915  * Based on:
39916  * Ext JS Library 1.1.1
39917  * Copyright(c) 2006-2007, Ext JS, LLC.
39918  *
39919  * Originally Released Under LGPL - original licence link has changed is not relivant.
39920  *
39921  * Fork - LGPL
39922  * <script type="text/javascript">
39923  */
39924  
39925
39926
39927 /**
39928  * @class Roo.SplitLayoutRegion
39929  * @extends Roo.LayoutRegion
39930  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39931  */
39932 Roo.bootstrap.layout.Split = function(config){
39933     this.cursor = config.cursor;
39934     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39935 };
39936
39937 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39938 {
39939     splitTip : "Drag to resize.",
39940     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39941     useSplitTips : false,
39942
39943     applyConfig : function(config){
39944         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39945     },
39946     
39947     onRender : function(ctr,pos) {
39948         
39949         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39950         if(!this.config.split){
39951             return;
39952         }
39953         if(!this.split){
39954             
39955             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39956                             tag: "div",
39957                             id: this.el.id + "-split",
39958                             cls: "roo-layout-split roo-layout-split-"+this.position,
39959                             html: "&#160;"
39960             });
39961             /** The SplitBar for this region 
39962             * @type Roo.SplitBar */
39963             // does not exist yet...
39964             Roo.log([this.position, this.orientation]);
39965             
39966             this.split = new Roo.bootstrap.SplitBar({
39967                 dragElement : splitEl,
39968                 resizingElement: this.el,
39969                 orientation : this.orientation
39970             });
39971             
39972             this.split.on("moved", this.onSplitMove, this);
39973             this.split.useShim = this.config.useShim === true;
39974             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39975             if(this.useSplitTips){
39976                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39977             }
39978             //if(config.collapsible){
39979             //    this.split.el.on("dblclick", this.collapse,  this);
39980             //}
39981         }
39982         if(typeof this.config.minSize != "undefined"){
39983             this.split.minSize = this.config.minSize;
39984         }
39985         if(typeof this.config.maxSize != "undefined"){
39986             this.split.maxSize = this.config.maxSize;
39987         }
39988         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39989             this.hideSplitter();
39990         }
39991         
39992     },
39993
39994     getHMaxSize : function(){
39995          var cmax = this.config.maxSize || 10000;
39996          var center = this.mgr.getRegion("center");
39997          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39998     },
39999
40000     getVMaxSize : function(){
40001          var cmax = this.config.maxSize || 10000;
40002          var center = this.mgr.getRegion("center");
40003          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40004     },
40005
40006     onSplitMove : function(split, newSize){
40007         this.fireEvent("resized", this, newSize);
40008     },
40009     
40010     /** 
40011      * Returns the {@link Roo.SplitBar} for this region.
40012      * @return {Roo.SplitBar}
40013      */
40014     getSplitBar : function(){
40015         return this.split;
40016     },
40017     
40018     hide : function(){
40019         this.hideSplitter();
40020         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40021     },
40022
40023     hideSplitter : function(){
40024         if(this.split){
40025             this.split.el.setLocation(-2000,-2000);
40026             this.split.el.hide();
40027         }
40028     },
40029
40030     show : function(){
40031         if(this.split){
40032             this.split.el.show();
40033         }
40034         Roo.bootstrap.layout.Split.superclass.show.call(this);
40035     },
40036     
40037     beforeSlide: function(){
40038         if(Roo.isGecko){// firefox overflow auto bug workaround
40039             this.bodyEl.clip();
40040             if(this.tabs) {
40041                 this.tabs.bodyEl.clip();
40042             }
40043             if(this.activePanel){
40044                 this.activePanel.getEl().clip();
40045                 
40046                 if(this.activePanel.beforeSlide){
40047                     this.activePanel.beforeSlide();
40048                 }
40049             }
40050         }
40051     },
40052     
40053     afterSlide : function(){
40054         if(Roo.isGecko){// firefox overflow auto bug workaround
40055             this.bodyEl.unclip();
40056             if(this.tabs) {
40057                 this.tabs.bodyEl.unclip();
40058             }
40059             if(this.activePanel){
40060                 this.activePanel.getEl().unclip();
40061                 if(this.activePanel.afterSlide){
40062                     this.activePanel.afterSlide();
40063                 }
40064             }
40065         }
40066     },
40067
40068     initAutoHide : function(){
40069         if(this.autoHide !== false){
40070             if(!this.autoHideHd){
40071                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40072                 this.autoHideHd = {
40073                     "mouseout": function(e){
40074                         if(!e.within(this.el, true)){
40075                             st.delay(500);
40076                         }
40077                     },
40078                     "mouseover" : function(e){
40079                         st.cancel();
40080                     },
40081                     scope : this
40082                 };
40083             }
40084             this.el.on(this.autoHideHd);
40085         }
40086     },
40087
40088     clearAutoHide : function(){
40089         if(this.autoHide !== false){
40090             this.el.un("mouseout", this.autoHideHd.mouseout);
40091             this.el.un("mouseover", this.autoHideHd.mouseover);
40092         }
40093     },
40094
40095     clearMonitor : function(){
40096         Roo.get(document).un("click", this.slideInIf, this);
40097     },
40098
40099     // these names are backwards but not changed for compat
40100     slideOut : function(){
40101         if(this.isSlid || this.el.hasActiveFx()){
40102             return;
40103         }
40104         this.isSlid = true;
40105         if(this.collapseBtn){
40106             this.collapseBtn.hide();
40107         }
40108         this.closeBtnState = this.closeBtn.getStyle('display');
40109         this.closeBtn.hide();
40110         if(this.stickBtn){
40111             this.stickBtn.show();
40112         }
40113         this.el.show();
40114         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40115         this.beforeSlide();
40116         this.el.setStyle("z-index", 10001);
40117         this.el.slideIn(this.getSlideAnchor(), {
40118             callback: function(){
40119                 this.afterSlide();
40120                 this.initAutoHide();
40121                 Roo.get(document).on("click", this.slideInIf, this);
40122                 this.fireEvent("slideshow", this);
40123             },
40124             scope: this,
40125             block: true
40126         });
40127     },
40128
40129     afterSlideIn : function(){
40130         this.clearAutoHide();
40131         this.isSlid = false;
40132         this.clearMonitor();
40133         this.el.setStyle("z-index", "");
40134         if(this.collapseBtn){
40135             this.collapseBtn.show();
40136         }
40137         this.closeBtn.setStyle('display', this.closeBtnState);
40138         if(this.stickBtn){
40139             this.stickBtn.hide();
40140         }
40141         this.fireEvent("slidehide", this);
40142     },
40143
40144     slideIn : function(cb){
40145         if(!this.isSlid || this.el.hasActiveFx()){
40146             Roo.callback(cb);
40147             return;
40148         }
40149         this.isSlid = false;
40150         this.beforeSlide();
40151         this.el.slideOut(this.getSlideAnchor(), {
40152             callback: function(){
40153                 this.el.setLeftTop(-10000, -10000);
40154                 this.afterSlide();
40155                 this.afterSlideIn();
40156                 Roo.callback(cb);
40157             },
40158             scope: this,
40159             block: true
40160         });
40161     },
40162     
40163     slideInIf : function(e){
40164         if(!e.within(this.el)){
40165             this.slideIn();
40166         }
40167     },
40168
40169     animateCollapse : function(){
40170         this.beforeSlide();
40171         this.el.setStyle("z-index", 20000);
40172         var anchor = this.getSlideAnchor();
40173         this.el.slideOut(anchor, {
40174             callback : function(){
40175                 this.el.setStyle("z-index", "");
40176                 this.collapsedEl.slideIn(anchor, {duration:.3});
40177                 this.afterSlide();
40178                 this.el.setLocation(-10000,-10000);
40179                 this.el.hide();
40180                 this.fireEvent("collapsed", this);
40181             },
40182             scope: this,
40183             block: true
40184         });
40185     },
40186
40187     animateExpand : function(){
40188         this.beforeSlide();
40189         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40190         this.el.setStyle("z-index", 20000);
40191         this.collapsedEl.hide({
40192             duration:.1
40193         });
40194         this.el.slideIn(this.getSlideAnchor(), {
40195             callback : function(){
40196                 this.el.setStyle("z-index", "");
40197                 this.afterSlide();
40198                 if(this.split){
40199                     this.split.el.show();
40200                 }
40201                 this.fireEvent("invalidated", this);
40202                 this.fireEvent("expanded", this);
40203             },
40204             scope: this,
40205             block: true
40206         });
40207     },
40208
40209     anchors : {
40210         "west" : "left",
40211         "east" : "right",
40212         "north" : "top",
40213         "south" : "bottom"
40214     },
40215
40216     sanchors : {
40217         "west" : "l",
40218         "east" : "r",
40219         "north" : "t",
40220         "south" : "b"
40221     },
40222
40223     canchors : {
40224         "west" : "tl-tr",
40225         "east" : "tr-tl",
40226         "north" : "tl-bl",
40227         "south" : "bl-tl"
40228     },
40229
40230     getAnchor : function(){
40231         return this.anchors[this.position];
40232     },
40233
40234     getCollapseAnchor : function(){
40235         return this.canchors[this.position];
40236     },
40237
40238     getSlideAnchor : function(){
40239         return this.sanchors[this.position];
40240     },
40241
40242     getAlignAdj : function(){
40243         var cm = this.cmargins;
40244         switch(this.position){
40245             case "west":
40246                 return [0, 0];
40247             break;
40248             case "east":
40249                 return [0, 0];
40250             break;
40251             case "north":
40252                 return [0, 0];
40253             break;
40254             case "south":
40255                 return [0, 0];
40256             break;
40257         }
40258     },
40259
40260     getExpandAdj : function(){
40261         var c = this.collapsedEl, cm = this.cmargins;
40262         switch(this.position){
40263             case "west":
40264                 return [-(cm.right+c.getWidth()+cm.left), 0];
40265             break;
40266             case "east":
40267                 return [cm.right+c.getWidth()+cm.left, 0];
40268             break;
40269             case "north":
40270                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40271             break;
40272             case "south":
40273                 return [0, cm.top+cm.bottom+c.getHeight()];
40274             break;
40275         }
40276     }
40277 });/*
40278  * Based on:
40279  * Ext JS Library 1.1.1
40280  * Copyright(c) 2006-2007, Ext JS, LLC.
40281  *
40282  * Originally Released Under LGPL - original licence link has changed is not relivant.
40283  *
40284  * Fork - LGPL
40285  * <script type="text/javascript">
40286  */
40287 /*
40288  * These classes are private internal classes
40289  */
40290 Roo.bootstrap.layout.Center = function(config){
40291     config.region = "center";
40292     Roo.bootstrap.layout.Region.call(this, config);
40293     this.visible = true;
40294     this.minWidth = config.minWidth || 20;
40295     this.minHeight = config.minHeight || 20;
40296 };
40297
40298 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40299     hide : function(){
40300         // center panel can't be hidden
40301     },
40302     
40303     show : function(){
40304         // center panel can't be hidden
40305     },
40306     
40307     getMinWidth: function(){
40308         return this.minWidth;
40309     },
40310     
40311     getMinHeight: function(){
40312         return this.minHeight;
40313     }
40314 });
40315
40316
40317
40318
40319  
40320
40321
40322
40323
40324
40325
40326 Roo.bootstrap.layout.North = function(config)
40327 {
40328     config.region = 'north';
40329     config.cursor = 'n-resize';
40330     
40331     Roo.bootstrap.layout.Split.call(this, config);
40332     
40333     
40334     if(this.split){
40335         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40336         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40337         this.split.el.addClass("roo-layout-split-v");
40338     }
40339     //var size = config.initialSize || config.height;
40340     //if(this.el && typeof size != "undefined"){
40341     //    this.el.setHeight(size);
40342     //}
40343 };
40344 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40345 {
40346     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40347      
40348      
40349     onRender : function(ctr, pos)
40350     {
40351         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40352         var size = this.config.initialSize || this.config.height;
40353         if(this.el && typeof size != "undefined"){
40354             this.el.setHeight(size);
40355         }
40356     
40357     },
40358     
40359     getBox : function(){
40360         if(this.collapsed){
40361             return this.collapsedEl.getBox();
40362         }
40363         var box = this.el.getBox();
40364         if(this.split){
40365             box.height += this.split.el.getHeight();
40366         }
40367         return box;
40368     },
40369     
40370     updateBox : function(box){
40371         if(this.split && !this.collapsed){
40372             box.height -= this.split.el.getHeight();
40373             this.split.el.setLeft(box.x);
40374             this.split.el.setTop(box.y+box.height);
40375             this.split.el.setWidth(box.width);
40376         }
40377         if(this.collapsed){
40378             this.updateBody(box.width, null);
40379         }
40380         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40381     }
40382 });
40383
40384
40385
40386
40387
40388 Roo.bootstrap.layout.South = function(config){
40389     config.region = 'south';
40390     config.cursor = 's-resize';
40391     Roo.bootstrap.layout.Split.call(this, config);
40392     if(this.split){
40393         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40394         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40395         this.split.el.addClass("roo-layout-split-v");
40396     }
40397     
40398 };
40399
40400 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40401     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40402     
40403     onRender : function(ctr, pos)
40404     {
40405         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40406         var size = this.config.initialSize || this.config.height;
40407         if(this.el && typeof size != "undefined"){
40408             this.el.setHeight(size);
40409         }
40410     
40411     },
40412     
40413     getBox : function(){
40414         if(this.collapsed){
40415             return this.collapsedEl.getBox();
40416         }
40417         var box = this.el.getBox();
40418         if(this.split){
40419             var sh = this.split.el.getHeight();
40420             box.height += sh;
40421             box.y -= sh;
40422         }
40423         return box;
40424     },
40425     
40426     updateBox : function(box){
40427         if(this.split && !this.collapsed){
40428             var sh = this.split.el.getHeight();
40429             box.height -= sh;
40430             box.y += sh;
40431             this.split.el.setLeft(box.x);
40432             this.split.el.setTop(box.y-sh);
40433             this.split.el.setWidth(box.width);
40434         }
40435         if(this.collapsed){
40436             this.updateBody(box.width, null);
40437         }
40438         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40439     }
40440 });
40441
40442 Roo.bootstrap.layout.East = function(config){
40443     config.region = "east";
40444     config.cursor = "e-resize";
40445     Roo.bootstrap.layout.Split.call(this, config);
40446     if(this.split){
40447         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40448         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40449         this.split.el.addClass("roo-layout-split-h");
40450     }
40451     
40452 };
40453 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40454     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40455     
40456     onRender : function(ctr, pos)
40457     {
40458         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40459         var size = this.config.initialSize || this.config.width;
40460         if(this.el && typeof size != "undefined"){
40461             this.el.setWidth(size);
40462         }
40463     
40464     },
40465     
40466     getBox : function(){
40467         if(this.collapsed){
40468             return this.collapsedEl.getBox();
40469         }
40470         var box = this.el.getBox();
40471         if(this.split){
40472             var sw = this.split.el.getWidth();
40473             box.width += sw;
40474             box.x -= sw;
40475         }
40476         return box;
40477     },
40478
40479     updateBox : function(box){
40480         if(this.split && !this.collapsed){
40481             var sw = this.split.el.getWidth();
40482             box.width -= sw;
40483             this.split.el.setLeft(box.x);
40484             this.split.el.setTop(box.y);
40485             this.split.el.setHeight(box.height);
40486             box.x += sw;
40487         }
40488         if(this.collapsed){
40489             this.updateBody(null, box.height);
40490         }
40491         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40492     }
40493 });
40494
40495 Roo.bootstrap.layout.West = function(config){
40496     config.region = "west";
40497     config.cursor = "w-resize";
40498     
40499     Roo.bootstrap.layout.Split.call(this, config);
40500     if(this.split){
40501         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40502         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40503         this.split.el.addClass("roo-layout-split-h");
40504     }
40505     
40506 };
40507 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40508     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40509     
40510     onRender: function(ctr, pos)
40511     {
40512         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40513         var size = this.config.initialSize || this.config.width;
40514         if(typeof size != "undefined"){
40515             this.el.setWidth(size);
40516         }
40517     },
40518     
40519     getBox : function(){
40520         if(this.collapsed){
40521             return this.collapsedEl.getBox();
40522         }
40523         var box = this.el.getBox();
40524         if (box.width == 0) {
40525             box.width = this.config.width; // kludge?
40526         }
40527         if(this.split){
40528             box.width += this.split.el.getWidth();
40529         }
40530         return box;
40531     },
40532     
40533     updateBox : function(box){
40534         if(this.split && !this.collapsed){
40535             var sw = this.split.el.getWidth();
40536             box.width -= sw;
40537             this.split.el.setLeft(box.x+box.width);
40538             this.split.el.setTop(box.y);
40539             this.split.el.setHeight(box.height);
40540         }
40541         if(this.collapsed){
40542             this.updateBody(null, box.height);
40543         }
40544         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40545     }
40546 });Roo.namespace("Roo.bootstrap.panel");/*
40547  * Based on:
40548  * Ext JS Library 1.1.1
40549  * Copyright(c) 2006-2007, Ext JS, LLC.
40550  *
40551  * Originally Released Under LGPL - original licence link has changed is not relivant.
40552  *
40553  * Fork - LGPL
40554  * <script type="text/javascript">
40555  */
40556 /**
40557  * @class Roo.ContentPanel
40558  * @extends Roo.util.Observable
40559  * @builder-top
40560  * A basic ContentPanel element.
40561  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40562  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40563  * @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
40564  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40565  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40566  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40567  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40568  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40569  * @cfg {String} title          The title for this panel
40570  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40571  * @cfg {String} url            Calls {@link #setUrl} with this value
40572  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40573  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40574  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40575  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40576  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40577  * @cfg {Boolean} badges render the badges
40578  * @cfg {String} cls  extra classes to use  
40579  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40580
40581  * @constructor
40582  * Create a new ContentPanel.
40583  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40584  * @param {String/Object} config A string to set only the title or a config object
40585  * @param {String} content (optional) Set the HTML content for this panel
40586  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40587  */
40588 Roo.bootstrap.panel.Content = function( config){
40589     
40590     this.tpl = config.tpl || false;
40591     
40592     var el = config.el;
40593     var content = config.content;
40594
40595     if(config.autoCreate){ // xtype is available if this is called from factory
40596         el = Roo.id();
40597     }
40598     this.el = Roo.get(el);
40599     if(!this.el && config && config.autoCreate){
40600         if(typeof config.autoCreate == "object"){
40601             if(!config.autoCreate.id){
40602                 config.autoCreate.id = config.id||el;
40603             }
40604             this.el = Roo.DomHelper.append(document.body,
40605                         config.autoCreate, true);
40606         }else{
40607             var elcfg =  {
40608                 tag: "div",
40609                 cls: (config.cls || '') +
40610                     (config.background ? ' bg-' + config.background : '') +
40611                     " roo-layout-inactive-content",
40612                 id: config.id||el
40613             };
40614             if (config.iframe) {
40615                 elcfg.cn = [
40616                     {
40617                         tag : 'iframe',
40618                         style : 'border: 0px',
40619                         src : 'about:blank'
40620                     }
40621                 ];
40622             }
40623               
40624             if (config.html) {
40625                 elcfg.html = config.html;
40626                 
40627             }
40628                         
40629             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40630             if (config.iframe) {
40631                 this.iframeEl = this.el.select('iframe',true).first();
40632             }
40633             
40634         }
40635     } 
40636     this.closable = false;
40637     this.loaded = false;
40638     this.active = false;
40639    
40640       
40641     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40642         
40643         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40644         
40645         this.wrapEl = this.el; //this.el.wrap();
40646         var ti = [];
40647         if (config.toolbar.items) {
40648             ti = config.toolbar.items ;
40649             delete config.toolbar.items ;
40650         }
40651         
40652         var nitems = [];
40653         this.toolbar.render(this.wrapEl, 'before');
40654         for(var i =0;i < ti.length;i++) {
40655           //  Roo.log(['add child', items[i]]);
40656             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40657         }
40658         this.toolbar.items = nitems;
40659         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40660         delete config.toolbar;
40661         
40662     }
40663     /*
40664     // xtype created footer. - not sure if will work as we normally have to render first..
40665     if (this.footer && !this.footer.el && this.footer.xtype) {
40666         if (!this.wrapEl) {
40667             this.wrapEl = this.el.wrap();
40668         }
40669     
40670         this.footer.container = this.wrapEl.createChild();
40671          
40672         this.footer = Roo.factory(this.footer, Roo);
40673         
40674     }
40675     */
40676     
40677      if(typeof config == "string"){
40678         this.title = config;
40679     }else{
40680         Roo.apply(this, config);
40681     }
40682     
40683     if(this.resizeEl){
40684         this.resizeEl = Roo.get(this.resizeEl, true);
40685     }else{
40686         this.resizeEl = this.el;
40687     }
40688     // handle view.xtype
40689     
40690  
40691     
40692     
40693     this.addEvents({
40694         /**
40695          * @event activate
40696          * Fires when this panel is activated. 
40697          * @param {Roo.ContentPanel} this
40698          */
40699         "activate" : true,
40700         /**
40701          * @event deactivate
40702          * Fires when this panel is activated. 
40703          * @param {Roo.ContentPanel} this
40704          */
40705         "deactivate" : true,
40706
40707         /**
40708          * @event resize
40709          * Fires when this panel is resized if fitToFrame is true.
40710          * @param {Roo.ContentPanel} this
40711          * @param {Number} width The width after any component adjustments
40712          * @param {Number} height The height after any component adjustments
40713          */
40714         "resize" : true,
40715         
40716          /**
40717          * @event render
40718          * Fires when this tab is created
40719          * @param {Roo.ContentPanel} this
40720          */
40721         "render" : true,
40722         
40723           /**
40724          * @event scroll
40725          * Fires when this content is scrolled
40726          * @param {Roo.ContentPanel} this
40727          * @param {Event} scrollEvent
40728          */
40729         "scroll" : true
40730         
40731         
40732         
40733     });
40734     
40735
40736     
40737     
40738     if(this.autoScroll && !this.iframe){
40739         this.resizeEl.setStyle("overflow", "auto");
40740         this.resizeEl.on('scroll', this.onScroll, this);
40741     } else {
40742         // fix randome scrolling
40743         //this.el.on('scroll', function() {
40744         //    Roo.log('fix random scolling');
40745         //    this.scrollTo('top',0); 
40746         //});
40747     }
40748     content = content || this.content;
40749     if(content){
40750         this.setContent(content);
40751     }
40752     if(config && config.url){
40753         this.setUrl(this.url, this.params, this.loadOnce);
40754     }
40755     
40756     
40757     
40758     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40759     
40760     if (this.view && typeof(this.view.xtype) != 'undefined') {
40761         this.view.el = this.el.appendChild(document.createElement("div"));
40762         this.view = Roo.factory(this.view); 
40763         this.view.render  &&  this.view.render(false, '');  
40764     }
40765     
40766     
40767     this.fireEvent('render', this);
40768 };
40769
40770 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40771     
40772     cls : '',
40773     background : '',
40774     
40775     tabTip : '',
40776     
40777     iframe : false,
40778     iframeEl : false,
40779     
40780     /* Resize Element - use this to work out scroll etc. */
40781     resizeEl : false,
40782     
40783     setRegion : function(region){
40784         this.region = region;
40785         this.setActiveClass(region && !this.background);
40786     },
40787     
40788     
40789     setActiveClass: function(state)
40790     {
40791         if(state){
40792            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40793            this.el.setStyle('position','relative');
40794         }else{
40795            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40796            this.el.setStyle('position', 'absolute');
40797         } 
40798     },
40799     
40800     /**
40801      * Returns the toolbar for this Panel if one was configured. 
40802      * @return {Roo.Toolbar} 
40803      */
40804     getToolbar : function(){
40805         return this.toolbar;
40806     },
40807     
40808     setActiveState : function(active)
40809     {
40810         this.active = active;
40811         this.setActiveClass(active);
40812         if(!active){
40813             if(this.fireEvent("deactivate", this) === false){
40814                 return false;
40815             }
40816             return true;
40817         }
40818         this.fireEvent("activate", this);
40819         return true;
40820     },
40821     /**
40822      * Updates this panel's element (not for iframe)
40823      * @param {String} content The new content
40824      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40825     */
40826     setContent : function(content, loadScripts){
40827         if (this.iframe) {
40828             return;
40829         }
40830         
40831         this.el.update(content, loadScripts);
40832     },
40833
40834     ignoreResize : function(w, h){
40835         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40836             return true;
40837         }else{
40838             this.lastSize = {width: w, height: h};
40839             return false;
40840         }
40841     },
40842     /**
40843      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40844      * @return {Roo.UpdateManager} The UpdateManager
40845      */
40846     getUpdateManager : function(){
40847         if (this.iframe) {
40848             return false;
40849         }
40850         return this.el.getUpdateManager();
40851     },
40852      /**
40853      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40854      * Does not work with IFRAME contents
40855      * @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:
40856 <pre><code>
40857 panel.load({
40858     url: "your-url.php",
40859     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40860     callback: yourFunction,
40861     scope: yourObject, //(optional scope)
40862     discardUrl: false,
40863     nocache: false,
40864     text: "Loading...",
40865     timeout: 30,
40866     scripts: false
40867 });
40868 </code></pre>
40869      
40870      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40871      * 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.
40872      * @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}
40873      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40874      * @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.
40875      * @return {Roo.ContentPanel} this
40876      */
40877     load : function(){
40878         
40879         if (this.iframe) {
40880             return this;
40881         }
40882         
40883         var um = this.el.getUpdateManager();
40884         um.update.apply(um, arguments);
40885         return this;
40886     },
40887
40888
40889     /**
40890      * 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.
40891      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40892      * @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)
40893      * @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)
40894      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40895      */
40896     setUrl : function(url, params, loadOnce){
40897         if (this.iframe) {
40898             this.iframeEl.dom.src = url;
40899             return false;
40900         }
40901         
40902         if(this.refreshDelegate){
40903             this.removeListener("activate", this.refreshDelegate);
40904         }
40905         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40906         this.on("activate", this.refreshDelegate);
40907         return this.el.getUpdateManager();
40908     },
40909     
40910     _handleRefresh : function(url, params, loadOnce){
40911         if(!loadOnce || !this.loaded){
40912             var updater = this.el.getUpdateManager();
40913             updater.update(url, params, this._setLoaded.createDelegate(this));
40914         }
40915     },
40916     
40917     _setLoaded : function(){
40918         this.loaded = true;
40919     }, 
40920     
40921     /**
40922      * Returns this panel's id
40923      * @return {String} 
40924      */
40925     getId : function(){
40926         return this.el.id;
40927     },
40928     
40929     /** 
40930      * Returns this panel's element - used by regiosn to add.
40931      * @return {Roo.Element} 
40932      */
40933     getEl : function(){
40934         return this.wrapEl || this.el;
40935     },
40936     
40937    
40938     
40939     adjustForComponents : function(width, height)
40940     {
40941         //Roo.log('adjustForComponents ');
40942         if(this.resizeEl != this.el){
40943             width -= this.el.getFrameWidth('lr');
40944             height -= this.el.getFrameWidth('tb');
40945         }
40946         if(this.toolbar){
40947             var te = this.toolbar.getEl();
40948             te.setWidth(width);
40949             height -= te.getHeight();
40950         }
40951         if(this.footer){
40952             var te = this.footer.getEl();
40953             te.setWidth(width);
40954             height -= te.getHeight();
40955         }
40956         
40957         
40958         if(this.adjustments){
40959             width += this.adjustments[0];
40960             height += this.adjustments[1];
40961         }
40962         return {"width": width, "height": height};
40963     },
40964     
40965     setSize : function(width, height){
40966         if(this.fitToFrame && !this.ignoreResize(width, height)){
40967             if(this.fitContainer && this.resizeEl != this.el){
40968                 this.el.setSize(width, height);
40969             }
40970             var size = this.adjustForComponents(width, height);
40971             if (this.iframe) {
40972                 this.iframeEl.setSize(width,height);
40973             }
40974             
40975             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40976             this.fireEvent('resize', this, size.width, size.height);
40977             
40978             
40979         }
40980     },
40981     
40982     /**
40983      * Returns this panel's title
40984      * @return {String} 
40985      */
40986     getTitle : function(){
40987         
40988         if (typeof(this.title) != 'object') {
40989             return this.title;
40990         }
40991         
40992         var t = '';
40993         for (var k in this.title) {
40994             if (!this.title.hasOwnProperty(k)) {
40995                 continue;
40996             }
40997             
40998             if (k.indexOf('-') >= 0) {
40999                 var s = k.split('-');
41000                 for (var i = 0; i<s.length; i++) {
41001                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41002                 }
41003             } else {
41004                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41005             }
41006         }
41007         return t;
41008     },
41009     
41010     /**
41011      * Set this panel's title
41012      * @param {String} title
41013      */
41014     setTitle : function(title){
41015         this.title = title;
41016         if(this.region){
41017             this.region.updatePanelTitle(this, title);
41018         }
41019     },
41020     
41021     /**
41022      * Returns true is this panel was configured to be closable
41023      * @return {Boolean} 
41024      */
41025     isClosable : function(){
41026         return this.closable;
41027     },
41028     
41029     beforeSlide : function(){
41030         this.el.clip();
41031         this.resizeEl.clip();
41032     },
41033     
41034     afterSlide : function(){
41035         this.el.unclip();
41036         this.resizeEl.unclip();
41037     },
41038     
41039     /**
41040      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41041      *   Will fail silently if the {@link #setUrl} method has not been called.
41042      *   This does not activate the panel, just updates its content.
41043      */
41044     refresh : function(){
41045         if(this.refreshDelegate){
41046            this.loaded = false;
41047            this.refreshDelegate();
41048         }
41049     },
41050     
41051     /**
41052      * Destroys this panel
41053      */
41054     destroy : function(){
41055         this.el.removeAllListeners();
41056         var tempEl = document.createElement("span");
41057         tempEl.appendChild(this.el.dom);
41058         tempEl.innerHTML = "";
41059         this.el.remove();
41060         this.el = null;
41061     },
41062     
41063     /**
41064      * form - if the content panel contains a form - this is a reference to it.
41065      * @type {Roo.form.Form}
41066      */
41067     form : false,
41068     /**
41069      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41070      *    This contains a reference to it.
41071      * @type {Roo.View}
41072      */
41073     view : false,
41074     
41075       /**
41076      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41077      * <pre><code>
41078
41079 layout.addxtype({
41080        xtype : 'Form',
41081        items: [ .... ]
41082    }
41083 );
41084
41085 </code></pre>
41086      * @param {Object} cfg Xtype definition of item to add.
41087      */
41088     
41089     
41090     getChildContainer: function () {
41091         return this.getEl();
41092     },
41093     
41094     
41095     onScroll : function(e)
41096     {
41097         this.fireEvent('scroll', this, e);
41098     }
41099     
41100     
41101     /*
41102         var  ret = new Roo.factory(cfg);
41103         return ret;
41104         
41105         
41106         // add form..
41107         if (cfg.xtype.match(/^Form$/)) {
41108             
41109             var el;
41110             //if (this.footer) {
41111             //    el = this.footer.container.insertSibling(false, 'before');
41112             //} else {
41113                 el = this.el.createChild();
41114             //}
41115
41116             this.form = new  Roo.form.Form(cfg);
41117             
41118             
41119             if ( this.form.allItems.length) {
41120                 this.form.render(el.dom);
41121             }
41122             return this.form;
41123         }
41124         // should only have one of theses..
41125         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41126             // views.. should not be just added - used named prop 'view''
41127             
41128             cfg.el = this.el.appendChild(document.createElement("div"));
41129             // factory?
41130             
41131             var ret = new Roo.factory(cfg);
41132              
41133              ret.render && ret.render(false, ''); // render blank..
41134             this.view = ret;
41135             return ret;
41136         }
41137         return false;
41138     }
41139     \*/
41140 });
41141  
41142 /**
41143  * @class Roo.bootstrap.panel.Grid
41144  * @extends Roo.bootstrap.panel.Content
41145  * @constructor
41146  * Create a new GridPanel.
41147  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41148  * @param {Object} config A the config object
41149   
41150  */
41151
41152
41153
41154 Roo.bootstrap.panel.Grid = function(config)
41155 {
41156     
41157       
41158     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41159         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41160
41161     config.el = this.wrapper;
41162     //this.el = this.wrapper;
41163     
41164       if (config.container) {
41165         // ctor'ed from a Border/panel.grid
41166         
41167         
41168         this.wrapper.setStyle("overflow", "hidden");
41169         this.wrapper.addClass('roo-grid-container');
41170
41171     }
41172     
41173     
41174     if(config.toolbar){
41175         var tool_el = this.wrapper.createChild();    
41176         this.toolbar = Roo.factory(config.toolbar);
41177         var ti = [];
41178         if (config.toolbar.items) {
41179             ti = config.toolbar.items ;
41180             delete config.toolbar.items ;
41181         }
41182         
41183         var nitems = [];
41184         this.toolbar.render(tool_el);
41185         for(var i =0;i < ti.length;i++) {
41186           //  Roo.log(['add child', items[i]]);
41187             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41188         }
41189         this.toolbar.items = nitems;
41190         
41191         delete config.toolbar;
41192     }
41193     
41194     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41195     config.grid.scrollBody = true;;
41196     config.grid.monitorWindowResize = false; // turn off autosizing
41197     config.grid.autoHeight = false;
41198     config.grid.autoWidth = false;
41199     
41200     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41201     
41202     if (config.background) {
41203         // render grid on panel activation (if panel background)
41204         this.on('activate', function(gp) {
41205             if (!gp.grid.rendered) {
41206                 gp.grid.render(this.wrapper);
41207                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41208             }
41209         });
41210             
41211     } else {
41212         this.grid.render(this.wrapper);
41213         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41214
41215     }
41216     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41217     // ??? needed ??? config.el = this.wrapper;
41218     
41219     
41220     
41221   
41222     // xtype created footer. - not sure if will work as we normally have to render first..
41223     if (this.footer && !this.footer.el && this.footer.xtype) {
41224         
41225         var ctr = this.grid.getView().getFooterPanel(true);
41226         this.footer.dataSource = this.grid.dataSource;
41227         this.footer = Roo.factory(this.footer, Roo);
41228         this.footer.render(ctr);
41229         
41230     }
41231     
41232     
41233     
41234     
41235      
41236 };
41237
41238 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41239     getId : function(){
41240         return this.grid.id;
41241     },
41242     
41243     /**
41244      * Returns the grid for this panel
41245      * @return {Roo.bootstrap.Table} 
41246      */
41247     getGrid : function(){
41248         return this.grid;    
41249     },
41250     
41251     setSize : function(width, height){
41252         if(!this.ignoreResize(width, height)){
41253             var grid = this.grid;
41254             var size = this.adjustForComponents(width, height);
41255             // tfoot is not a footer?
41256           
41257             
41258             var gridel = grid.getGridEl();
41259             gridel.setSize(size.width, size.height);
41260             
41261             var tbd = grid.getGridEl().select('tbody', true).first();
41262             var thd = grid.getGridEl().select('thead',true).first();
41263             var tbf= grid.getGridEl().select('tfoot', true).first();
41264
41265             if (tbf) {
41266                 size.height -= tbf.getHeight();
41267             }
41268             if (thd) {
41269                 size.height -= thd.getHeight();
41270             }
41271             
41272             tbd.setSize(size.width, size.height );
41273             // this is for the account management tab -seems to work there.
41274             var thd = grid.getGridEl().select('thead',true).first();
41275             //if (tbd) {
41276             //    tbd.setSize(size.width, size.height - thd.getHeight());
41277             //}
41278              
41279             grid.autoSize();
41280         }
41281     },
41282      
41283     
41284     
41285     beforeSlide : function(){
41286         this.grid.getView().scroller.clip();
41287     },
41288     
41289     afterSlide : function(){
41290         this.grid.getView().scroller.unclip();
41291     },
41292     
41293     destroy : function(){
41294         this.grid.destroy();
41295         delete this.grid;
41296         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41297     }
41298 });
41299
41300 /**
41301  * @class Roo.bootstrap.panel.Nest
41302  * @extends Roo.bootstrap.panel.Content
41303  * @constructor
41304  * Create a new Panel, that can contain a layout.Border.
41305  * 
41306  * 
41307  * @param {Roo.BorderLayout} layout The layout for this panel
41308  * @param {String/Object} config A string to set only the title or a config object
41309  */
41310 Roo.bootstrap.panel.Nest = function(config)
41311 {
41312     // construct with only one argument..
41313     /* FIXME - implement nicer consturctors
41314     if (layout.layout) {
41315         config = layout;
41316         layout = config.layout;
41317         delete config.layout;
41318     }
41319     if (layout.xtype && !layout.getEl) {
41320         // then layout needs constructing..
41321         layout = Roo.factory(layout, Roo);
41322     }
41323     */
41324     
41325     config.el =  config.layout.getEl();
41326     
41327     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41328     
41329     config.layout.monitorWindowResize = false; // turn off autosizing
41330     this.layout = config.layout;
41331     this.layout.getEl().addClass("roo-layout-nested-layout");
41332     this.layout.parent = this;
41333     
41334     
41335     
41336     
41337 };
41338
41339 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41340
41341     setSize : function(width, height){
41342         if(!this.ignoreResize(width, height)){
41343             var size = this.adjustForComponents(width, height);
41344             var el = this.layout.getEl();
41345             if (size.height < 1) {
41346                 el.setWidth(size.width);   
41347             } else {
41348                 el.setSize(size.width, size.height);
41349             }
41350             var touch = el.dom.offsetWidth;
41351             this.layout.layout();
41352             // ie requires a double layout on the first pass
41353             if(Roo.isIE && !this.initialized){
41354                 this.initialized = true;
41355                 this.layout.layout();
41356             }
41357         }
41358     },
41359     
41360     // activate all subpanels if not currently active..
41361     
41362     setActiveState : function(active){
41363         this.active = active;
41364         this.setActiveClass(active);
41365         
41366         if(!active){
41367             this.fireEvent("deactivate", this);
41368             return;
41369         }
41370         
41371         this.fireEvent("activate", this);
41372         // not sure if this should happen before or after..
41373         if (!this.layout) {
41374             return; // should not happen..
41375         }
41376         var reg = false;
41377         for (var r in this.layout.regions) {
41378             reg = this.layout.getRegion(r);
41379             if (reg.getActivePanel()) {
41380                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41381                 reg.setActivePanel(reg.getActivePanel());
41382                 continue;
41383             }
41384             if (!reg.panels.length) {
41385                 continue;
41386             }
41387             reg.showPanel(reg.getPanel(0));
41388         }
41389         
41390         
41391         
41392         
41393     },
41394     
41395     /**
41396      * Returns the nested BorderLayout for this panel
41397      * @return {Roo.BorderLayout} 
41398      */
41399     getLayout : function(){
41400         return this.layout;
41401     },
41402     
41403      /**
41404      * Adds a xtype elements to the layout of the nested panel
41405      * <pre><code>
41406
41407 panel.addxtype({
41408        xtype : 'ContentPanel',
41409        region: 'west',
41410        items: [ .... ]
41411    }
41412 );
41413
41414 panel.addxtype({
41415         xtype : 'NestedLayoutPanel',
41416         region: 'west',
41417         layout: {
41418            center: { },
41419            west: { }   
41420         },
41421         items : [ ... list of content panels or nested layout panels.. ]
41422    }
41423 );
41424 </code></pre>
41425      * @param {Object} cfg Xtype definition of item to add.
41426      */
41427     addxtype : function(cfg) {
41428         return this.layout.addxtype(cfg);
41429     
41430     }
41431 });/*
41432  * Based on:
41433  * Ext JS Library 1.1.1
41434  * Copyright(c) 2006-2007, Ext JS, LLC.
41435  *
41436  * Originally Released Under LGPL - original licence link has changed is not relivant.
41437  *
41438  * Fork - LGPL
41439  * <script type="text/javascript">
41440  */
41441 /**
41442  * @class Roo.TabPanel
41443  * @extends Roo.util.Observable
41444  * A lightweight tab container.
41445  * <br><br>
41446  * Usage:
41447  * <pre><code>
41448 // basic tabs 1, built from existing content
41449 var tabs = new Roo.TabPanel("tabs1");
41450 tabs.addTab("script", "View Script");
41451 tabs.addTab("markup", "View Markup");
41452 tabs.activate("script");
41453
41454 // more advanced tabs, built from javascript
41455 var jtabs = new Roo.TabPanel("jtabs");
41456 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41457
41458 // set up the UpdateManager
41459 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41460 var updater = tab2.getUpdateManager();
41461 updater.setDefaultUrl("ajax1.htm");
41462 tab2.on('activate', updater.refresh, updater, true);
41463
41464 // Use setUrl for Ajax loading
41465 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41466 tab3.setUrl("ajax2.htm", null, true);
41467
41468 // Disabled tab
41469 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41470 tab4.disable();
41471
41472 jtabs.activate("jtabs-1");
41473  * </code></pre>
41474  * @constructor
41475  * Create a new TabPanel.
41476  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41477  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41478  */
41479 Roo.bootstrap.panel.Tabs = function(config){
41480     /**
41481     * The container element for this TabPanel.
41482     * @type Roo.Element
41483     */
41484     this.el = Roo.get(config.el);
41485     delete config.el;
41486     if(config){
41487         if(typeof config == "boolean"){
41488             this.tabPosition = config ? "bottom" : "top";
41489         }else{
41490             Roo.apply(this, config);
41491         }
41492     }
41493     
41494     if(this.tabPosition == "bottom"){
41495         // if tabs are at the bottom = create the body first.
41496         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41497         this.el.addClass("roo-tabs-bottom");
41498     }
41499     // next create the tabs holders
41500     
41501     if (this.tabPosition == "west"){
41502         
41503         var reg = this.region; // fake it..
41504         while (reg) {
41505             if (!reg.mgr.parent) {
41506                 break;
41507             }
41508             reg = reg.mgr.parent.region;
41509         }
41510         Roo.log("got nest?");
41511         Roo.log(reg);
41512         if (reg.mgr.getRegion('west')) {
41513             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41514             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41515             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41516             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41517             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41518         
41519             
41520         }
41521         
41522         
41523     } else {
41524      
41525         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41526         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41527         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41528         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41529     }
41530     
41531     
41532     if(Roo.isIE){
41533         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41534     }
41535     
41536     // finally - if tabs are at the top, then create the body last..
41537     if(this.tabPosition != "bottom"){
41538         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41539          * @type Roo.Element
41540          */
41541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41542         this.el.addClass("roo-tabs-top");
41543     }
41544     this.items = [];
41545
41546     this.bodyEl.setStyle("position", "relative");
41547
41548     this.active = null;
41549     this.activateDelegate = this.activate.createDelegate(this);
41550
41551     this.addEvents({
41552         /**
41553          * @event tabchange
41554          * Fires when the active tab changes
41555          * @param {Roo.TabPanel} this
41556          * @param {Roo.TabPanelItem} activePanel The new active tab
41557          */
41558         "tabchange": true,
41559         /**
41560          * @event beforetabchange
41561          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41562          * @param {Roo.TabPanel} this
41563          * @param {Object} e Set cancel to true on this object to cancel the tab change
41564          * @param {Roo.TabPanelItem} tab The tab being changed to
41565          */
41566         "beforetabchange" : true
41567     });
41568
41569     Roo.EventManager.onWindowResize(this.onResize, this);
41570     this.cpad = this.el.getPadding("lr");
41571     this.hiddenCount = 0;
41572
41573
41574     // toolbar on the tabbar support...
41575     if (this.toolbar) {
41576         alert("no toolbar support yet");
41577         this.toolbar  = false;
41578         /*
41579         var tcfg = this.toolbar;
41580         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41581         this.toolbar = new Roo.Toolbar(tcfg);
41582         if (Roo.isSafari) {
41583             var tbl = tcfg.container.child('table', true);
41584             tbl.setAttribute('width', '100%');
41585         }
41586         */
41587         
41588     }
41589    
41590
41591
41592     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41593 };
41594
41595 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41596     /*
41597      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41598      */
41599     tabPosition : "top",
41600     /*
41601      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41602      */
41603     currentTabWidth : 0,
41604     /*
41605      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41606      */
41607     minTabWidth : 40,
41608     /*
41609      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41610      */
41611     maxTabWidth : 250,
41612     /*
41613      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41614      */
41615     preferredTabWidth : 175,
41616     /*
41617      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41618      */
41619     resizeTabs : false,
41620     /*
41621      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41622      */
41623     monitorResize : true,
41624     /*
41625      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41626      */
41627     toolbar : false,  // set by caller..
41628     
41629     region : false, /// set by caller
41630     
41631     disableTooltips : true, // not used yet...
41632
41633     /**
41634      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41635      * @param {String} id The id of the div to use <b>or create</b>
41636      * @param {String} text The text for the tab
41637      * @param {String} content (optional) Content to put in the TabPanelItem body
41638      * @param {Boolean} closable (optional) True to create a close icon on the tab
41639      * @return {Roo.TabPanelItem} The created TabPanelItem
41640      */
41641     addTab : function(id, text, content, closable, tpl)
41642     {
41643         var item = new Roo.bootstrap.panel.TabItem({
41644             panel: this,
41645             id : id,
41646             text : text,
41647             closable : closable,
41648             tpl : tpl
41649         });
41650         this.addTabItem(item);
41651         if(content){
41652             item.setContent(content);
41653         }
41654         return item;
41655     },
41656
41657     /**
41658      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41659      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41660      * @return {Roo.TabPanelItem}
41661      */
41662     getTab : function(id){
41663         return this.items[id];
41664     },
41665
41666     /**
41667      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41668      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41669      */
41670     hideTab : function(id){
41671         var t = this.items[id];
41672         if(!t.isHidden()){
41673            t.setHidden(true);
41674            this.hiddenCount++;
41675            this.autoSizeTabs();
41676         }
41677     },
41678
41679     /**
41680      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41681      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41682      */
41683     unhideTab : function(id){
41684         var t = this.items[id];
41685         if(t.isHidden()){
41686            t.setHidden(false);
41687            this.hiddenCount--;
41688            this.autoSizeTabs();
41689         }
41690     },
41691
41692     /**
41693      * Adds an existing {@link Roo.TabPanelItem}.
41694      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41695      */
41696     addTabItem : function(item)
41697     {
41698         this.items[item.id] = item;
41699         this.items.push(item);
41700         this.autoSizeTabs();
41701       //  if(this.resizeTabs){
41702     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41703   //         this.autoSizeTabs();
41704 //        }else{
41705 //            item.autoSize();
41706        // }
41707     },
41708
41709     /**
41710      * Removes a {@link Roo.TabPanelItem}.
41711      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41712      */
41713     removeTab : function(id){
41714         var items = this.items;
41715         var tab = items[id];
41716         if(!tab) { return; }
41717         var index = items.indexOf(tab);
41718         if(this.active == tab && items.length > 1){
41719             var newTab = this.getNextAvailable(index);
41720             if(newTab) {
41721                 newTab.activate();
41722             }
41723         }
41724         this.stripEl.dom.removeChild(tab.pnode.dom);
41725         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41726             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41727         }
41728         items.splice(index, 1);
41729         delete this.items[tab.id];
41730         tab.fireEvent("close", tab);
41731         tab.purgeListeners();
41732         this.autoSizeTabs();
41733     },
41734
41735     getNextAvailable : function(start){
41736         var items = this.items;
41737         var index = start;
41738         // look for a next tab that will slide over to
41739         // replace the one being removed
41740         while(index < items.length){
41741             var item = items[++index];
41742             if(item && !item.isHidden()){
41743                 return item;
41744             }
41745         }
41746         // if one isn't found select the previous tab (on the left)
41747         index = start;
41748         while(index >= 0){
41749             var item = items[--index];
41750             if(item && !item.isHidden()){
41751                 return item;
41752             }
41753         }
41754         return null;
41755     },
41756
41757     /**
41758      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41759      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41760      */
41761     disableTab : function(id){
41762         var tab = this.items[id];
41763         if(tab && this.active != tab){
41764             tab.disable();
41765         }
41766     },
41767
41768     /**
41769      * Enables a {@link Roo.TabPanelItem} that is disabled.
41770      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41771      */
41772     enableTab : function(id){
41773         var tab = this.items[id];
41774         tab.enable();
41775     },
41776
41777     /**
41778      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41779      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41780      * @return {Roo.TabPanelItem} The TabPanelItem.
41781      */
41782     activate : function(id)
41783     {
41784         //Roo.log('activite:'  + id);
41785         
41786         var tab = this.items[id];
41787         if(!tab){
41788             return null;
41789         }
41790         if(tab == this.active || tab.disabled){
41791             return tab;
41792         }
41793         var e = {};
41794         this.fireEvent("beforetabchange", this, e, tab);
41795         if(e.cancel !== true && !tab.disabled){
41796             if(this.active){
41797                 this.active.hide();
41798             }
41799             this.active = this.items[id];
41800             this.active.show();
41801             this.fireEvent("tabchange", this, this.active);
41802         }
41803         return tab;
41804     },
41805
41806     /**
41807      * Gets the active {@link Roo.TabPanelItem}.
41808      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41809      */
41810     getActiveTab : function(){
41811         return this.active;
41812     },
41813
41814     /**
41815      * Updates the tab body element to fit the height of the container element
41816      * for overflow scrolling
41817      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41818      */
41819     syncHeight : function(targetHeight){
41820         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41821         var bm = this.bodyEl.getMargins();
41822         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41823         this.bodyEl.setHeight(newHeight);
41824         return newHeight;
41825     },
41826
41827     onResize : function(){
41828         if(this.monitorResize){
41829             this.autoSizeTabs();
41830         }
41831     },
41832
41833     /**
41834      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41835      */
41836     beginUpdate : function(){
41837         this.updating = true;
41838     },
41839
41840     /**
41841      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41842      */
41843     endUpdate : function(){
41844         this.updating = false;
41845         this.autoSizeTabs();
41846     },
41847
41848     /**
41849      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41850      */
41851     autoSizeTabs : function()
41852     {
41853         var count = this.items.length;
41854         var vcount = count - this.hiddenCount;
41855         
41856         if (vcount < 2) {
41857             this.stripEl.hide();
41858         } else {
41859             this.stripEl.show();
41860         }
41861         
41862         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41863             return;
41864         }
41865         
41866         
41867         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41868         var availWidth = Math.floor(w / vcount);
41869         var b = this.stripBody;
41870         if(b.getWidth() > w){
41871             var tabs = this.items;
41872             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41873             if(availWidth < this.minTabWidth){
41874                 /*if(!this.sleft){    // incomplete scrolling code
41875                     this.createScrollButtons();
41876                 }
41877                 this.showScroll();
41878                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41879             }
41880         }else{
41881             if(this.currentTabWidth < this.preferredTabWidth){
41882                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41883             }
41884         }
41885     },
41886
41887     /**
41888      * Returns the number of tabs in this TabPanel.
41889      * @return {Number}
41890      */
41891      getCount : function(){
41892          return this.items.length;
41893      },
41894
41895     /**
41896      * Resizes all the tabs to the passed width
41897      * @param {Number} The new width
41898      */
41899     setTabWidth : function(width){
41900         this.currentTabWidth = width;
41901         for(var i = 0, len = this.items.length; i < len; i++) {
41902                 if(!this.items[i].isHidden()) {
41903                 this.items[i].setWidth(width);
41904             }
41905         }
41906     },
41907
41908     /**
41909      * Destroys this TabPanel
41910      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41911      */
41912     destroy : function(removeEl){
41913         Roo.EventManager.removeResizeListener(this.onResize, this);
41914         for(var i = 0, len = this.items.length; i < len; i++){
41915             this.items[i].purgeListeners();
41916         }
41917         if(removeEl === true){
41918             this.el.update("");
41919             this.el.remove();
41920         }
41921     },
41922     
41923     createStrip : function(container)
41924     {
41925         var strip = document.createElement("nav");
41926         strip.className = Roo.bootstrap.version == 4 ?
41927             "navbar-light bg-light" : 
41928             "navbar navbar-default"; //"x-tabs-wrap";
41929         container.appendChild(strip);
41930         return strip;
41931     },
41932     
41933     createStripList : function(strip)
41934     {
41935         // div wrapper for retard IE
41936         // returns the "tr" element.
41937         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41938         //'<div class="x-tabs-strip-wrap">'+
41939           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41940           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41941         return strip.firstChild; //.firstChild.firstChild.firstChild;
41942     },
41943     createBody : function(container)
41944     {
41945         var body = document.createElement("div");
41946         Roo.id(body, "tab-body");
41947         //Roo.fly(body).addClass("x-tabs-body");
41948         Roo.fly(body).addClass("tab-content");
41949         container.appendChild(body);
41950         return body;
41951     },
41952     createItemBody :function(bodyEl, id){
41953         var body = Roo.getDom(id);
41954         if(!body){
41955             body = document.createElement("div");
41956             body.id = id;
41957         }
41958         //Roo.fly(body).addClass("x-tabs-item-body");
41959         Roo.fly(body).addClass("tab-pane");
41960          bodyEl.insertBefore(body, bodyEl.firstChild);
41961         return body;
41962     },
41963     /** @private */
41964     createStripElements :  function(stripEl, text, closable, tpl)
41965     {
41966         var td = document.createElement("li"); // was td..
41967         td.className = 'nav-item';
41968         
41969         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41970         
41971         
41972         stripEl.appendChild(td);
41973         /*if(closable){
41974             td.className = "x-tabs-closable";
41975             if(!this.closeTpl){
41976                 this.closeTpl = new Roo.Template(
41977                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41978                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41979                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41980                 );
41981             }
41982             var el = this.closeTpl.overwrite(td, {"text": text});
41983             var close = el.getElementsByTagName("div")[0];
41984             var inner = el.getElementsByTagName("em")[0];
41985             return {"el": el, "close": close, "inner": inner};
41986         } else {
41987         */
41988         // not sure what this is..
41989 //            if(!this.tabTpl){
41990                 //this.tabTpl = new Roo.Template(
41991                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41992                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41993                 //);
41994 //                this.tabTpl = new Roo.Template(
41995 //                   '<a href="#">' +
41996 //                   '<span unselectable="on"' +
41997 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41998 //                            ' >{text}</span></a>'
41999 //                );
42000 //                
42001 //            }
42002
42003
42004             var template = tpl || this.tabTpl || false;
42005             
42006             if(!template){
42007                 template =  new Roo.Template(
42008                         Roo.bootstrap.version == 4 ? 
42009                             (
42010                                 '<a class="nav-link" href="#" unselectable="on"' +
42011                                      (this.disableTooltips ? '' : ' title="{text}"') +
42012                                      ' >{text}</a>'
42013                             ) : (
42014                                 '<a class="nav-link" href="#">' +
42015                                 '<span unselectable="on"' +
42016                                          (this.disableTooltips ? '' : ' title="{text}"') +
42017                                     ' >{text}</span></a>'
42018                             )
42019                 );
42020             }
42021             
42022             switch (typeof(template)) {
42023                 case 'object' :
42024                     break;
42025                 case 'string' :
42026                     template = new Roo.Template(template);
42027                     break;
42028                 default :
42029                     break;
42030             }
42031             
42032             var el = template.overwrite(td, {"text": text});
42033             
42034             var inner = el.getElementsByTagName("span")[0];
42035             
42036             return {"el": el, "inner": inner};
42037             
42038     }
42039         
42040     
42041 });
42042
42043 /**
42044  * @class Roo.TabPanelItem
42045  * @extends Roo.util.Observable
42046  * Represents an individual item (tab plus body) in a TabPanel.
42047  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42048  * @param {String} id The id of this TabPanelItem
42049  * @param {String} text The text for the tab of this TabPanelItem
42050  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42051  */
42052 Roo.bootstrap.panel.TabItem = function(config){
42053     /**
42054      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42055      * @type Roo.TabPanel
42056      */
42057     this.tabPanel = config.panel;
42058     /**
42059      * The id for this TabPanelItem
42060      * @type String
42061      */
42062     this.id = config.id;
42063     /** @private */
42064     this.disabled = false;
42065     /** @private */
42066     this.text = config.text;
42067     /** @private */
42068     this.loaded = false;
42069     this.closable = config.closable;
42070
42071     /**
42072      * The body element for this TabPanelItem.
42073      * @type Roo.Element
42074      */
42075     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42076     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42077     this.bodyEl.setStyle("display", "block");
42078     this.bodyEl.setStyle("zoom", "1");
42079     //this.hideAction();
42080
42081     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42082     /** @private */
42083     this.el = Roo.get(els.el);
42084     this.inner = Roo.get(els.inner, true);
42085      this.textEl = Roo.bootstrap.version == 4 ?
42086         this.el : Roo.get(this.el.dom.firstChild, true);
42087
42088     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42089     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42090
42091     
42092 //    this.el.on("mousedown", this.onTabMouseDown, this);
42093     this.el.on("click", this.onTabClick, this);
42094     /** @private */
42095     if(config.closable){
42096         var c = Roo.get(els.close, true);
42097         c.dom.title = this.closeText;
42098         c.addClassOnOver("close-over");
42099         c.on("click", this.closeClick, this);
42100      }
42101
42102     this.addEvents({
42103          /**
42104          * @event activate
42105          * Fires when this tab becomes the active tab.
42106          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42107          * @param {Roo.TabPanelItem} this
42108          */
42109         "activate": true,
42110         /**
42111          * @event beforeclose
42112          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42113          * @param {Roo.TabPanelItem} this
42114          * @param {Object} e Set cancel to true on this object to cancel the close.
42115          */
42116         "beforeclose": true,
42117         /**
42118          * @event close
42119          * Fires when this tab is closed.
42120          * @param {Roo.TabPanelItem} this
42121          */
42122          "close": true,
42123         /**
42124          * @event deactivate
42125          * Fires when this tab is no longer the active tab.
42126          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42127          * @param {Roo.TabPanelItem} this
42128          */
42129          "deactivate" : true
42130     });
42131     this.hidden = false;
42132
42133     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42134 };
42135
42136 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42137            {
42138     purgeListeners : function(){
42139        Roo.util.Observable.prototype.purgeListeners.call(this);
42140        this.el.removeAllListeners();
42141     },
42142     /**
42143      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42144      */
42145     show : function(){
42146         this.status_node.addClass("active");
42147         this.showAction();
42148         if(Roo.isOpera){
42149             this.tabPanel.stripWrap.repaint();
42150         }
42151         this.fireEvent("activate", this.tabPanel, this);
42152     },
42153
42154     /**
42155      * Returns true if this tab is the active tab.
42156      * @return {Boolean}
42157      */
42158     isActive : function(){
42159         return this.tabPanel.getActiveTab() == this;
42160     },
42161
42162     /**
42163      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42164      */
42165     hide : function(){
42166         this.status_node.removeClass("active");
42167         this.hideAction();
42168         this.fireEvent("deactivate", this.tabPanel, this);
42169     },
42170
42171     hideAction : function(){
42172         this.bodyEl.hide();
42173         this.bodyEl.setStyle("position", "absolute");
42174         this.bodyEl.setLeft("-20000px");
42175         this.bodyEl.setTop("-20000px");
42176     },
42177
42178     showAction : function(){
42179         this.bodyEl.setStyle("position", "relative");
42180         this.bodyEl.setTop("");
42181         this.bodyEl.setLeft("");
42182         this.bodyEl.show();
42183     },
42184
42185     /**
42186      * Set the tooltip for the tab.
42187      * @param {String} tooltip The tab's tooltip
42188      */
42189     setTooltip : function(text){
42190         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42191             this.textEl.dom.qtip = text;
42192             this.textEl.dom.removeAttribute('title');
42193         }else{
42194             this.textEl.dom.title = text;
42195         }
42196     },
42197
42198     onTabClick : function(e){
42199         e.preventDefault();
42200         this.tabPanel.activate(this.id);
42201     },
42202
42203     onTabMouseDown : function(e){
42204         e.preventDefault();
42205         this.tabPanel.activate(this.id);
42206     },
42207 /*
42208     getWidth : function(){
42209         return this.inner.getWidth();
42210     },
42211
42212     setWidth : function(width){
42213         var iwidth = width - this.linode.getPadding("lr");
42214         this.inner.setWidth(iwidth);
42215         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42216         this.linode.setWidth(width);
42217     },
42218 */
42219     /**
42220      * Show or hide the tab
42221      * @param {Boolean} hidden True to hide or false to show.
42222      */
42223     setHidden : function(hidden){
42224         this.hidden = hidden;
42225         this.linode.setStyle("display", hidden ? "none" : "");
42226     },
42227
42228     /**
42229      * Returns true if this tab is "hidden"
42230      * @return {Boolean}
42231      */
42232     isHidden : function(){
42233         return this.hidden;
42234     },
42235
42236     /**
42237      * Returns the text for this tab
42238      * @return {String}
42239      */
42240     getText : function(){
42241         return this.text;
42242     },
42243     /*
42244     autoSize : function(){
42245         //this.el.beginMeasure();
42246         this.textEl.setWidth(1);
42247         /*
42248          *  #2804 [new] Tabs in Roojs
42249          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42250          */
42251         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42252         //this.el.endMeasure();
42253     //},
42254
42255     /**
42256      * Sets the text for the tab (Note: this also sets the tooltip text)
42257      * @param {String} text The tab's text and tooltip
42258      */
42259     setText : function(text){
42260         this.text = text;
42261         this.textEl.update(text);
42262         this.setTooltip(text);
42263         //if(!this.tabPanel.resizeTabs){
42264         //    this.autoSize();
42265         //}
42266     },
42267     /**
42268      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42269      */
42270     activate : function(){
42271         this.tabPanel.activate(this.id);
42272     },
42273
42274     /**
42275      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42276      */
42277     disable : function(){
42278         if(this.tabPanel.active != this){
42279             this.disabled = true;
42280             this.status_node.addClass("disabled");
42281         }
42282     },
42283
42284     /**
42285      * Enables this TabPanelItem if it was previously disabled.
42286      */
42287     enable : function(){
42288         this.disabled = false;
42289         this.status_node.removeClass("disabled");
42290     },
42291
42292     /**
42293      * Sets the content for this TabPanelItem.
42294      * @param {String} content The content
42295      * @param {Boolean} loadScripts true to look for and load scripts
42296      */
42297     setContent : function(content, loadScripts){
42298         this.bodyEl.update(content, loadScripts);
42299     },
42300
42301     /**
42302      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42303      * @return {Roo.UpdateManager} The UpdateManager
42304      */
42305     getUpdateManager : function(){
42306         return this.bodyEl.getUpdateManager();
42307     },
42308
42309     /**
42310      * Set a URL to be used to load the content for this TabPanelItem.
42311      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42312      * @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)
42313      * @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)
42314      * @return {Roo.UpdateManager} The UpdateManager
42315      */
42316     setUrl : function(url, params, loadOnce){
42317         if(this.refreshDelegate){
42318             this.un('activate', this.refreshDelegate);
42319         }
42320         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42321         this.on("activate", this.refreshDelegate);
42322         return this.bodyEl.getUpdateManager();
42323     },
42324
42325     /** @private */
42326     _handleRefresh : function(url, params, loadOnce){
42327         if(!loadOnce || !this.loaded){
42328             var updater = this.bodyEl.getUpdateManager();
42329             updater.update(url, params, this._setLoaded.createDelegate(this));
42330         }
42331     },
42332
42333     /**
42334      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42335      *   Will fail silently if the setUrl method has not been called.
42336      *   This does not activate the panel, just updates its content.
42337      */
42338     refresh : function(){
42339         if(this.refreshDelegate){
42340            this.loaded = false;
42341            this.refreshDelegate();
42342         }
42343     },
42344
42345     /** @private */
42346     _setLoaded : function(){
42347         this.loaded = true;
42348     },
42349
42350     /** @private */
42351     closeClick : function(e){
42352         var o = {};
42353         e.stopEvent();
42354         this.fireEvent("beforeclose", this, o);
42355         if(o.cancel !== true){
42356             this.tabPanel.removeTab(this.id);
42357         }
42358     },
42359     /**
42360      * The text displayed in the tooltip for the close icon.
42361      * @type String
42362      */
42363     closeText : "Close this tab"
42364 });
42365 /**
42366 *    This script refer to:
42367 *    Title: International Telephone Input
42368 *    Author: Jack O'Connor
42369 *    Code version:  v12.1.12
42370 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42371 **/
42372
42373 Roo.bootstrap.PhoneInputData = function() {
42374     var d = [
42375       [
42376         "Afghanistan (‫افغانستان‬‎)",
42377         "af",
42378         "93"
42379       ],
42380       [
42381         "Albania (Shqipëri)",
42382         "al",
42383         "355"
42384       ],
42385       [
42386         "Algeria (‫الجزائر‬‎)",
42387         "dz",
42388         "213"
42389       ],
42390       [
42391         "American Samoa",
42392         "as",
42393         "1684"
42394       ],
42395       [
42396         "Andorra",
42397         "ad",
42398         "376"
42399       ],
42400       [
42401         "Angola",
42402         "ao",
42403         "244"
42404       ],
42405       [
42406         "Anguilla",
42407         "ai",
42408         "1264"
42409       ],
42410       [
42411         "Antigua and Barbuda",
42412         "ag",
42413         "1268"
42414       ],
42415       [
42416         "Argentina",
42417         "ar",
42418         "54"
42419       ],
42420       [
42421         "Armenia (Հայաստան)",
42422         "am",
42423         "374"
42424       ],
42425       [
42426         "Aruba",
42427         "aw",
42428         "297"
42429       ],
42430       [
42431         "Australia",
42432         "au",
42433         "61",
42434         0
42435       ],
42436       [
42437         "Austria (Österreich)",
42438         "at",
42439         "43"
42440       ],
42441       [
42442         "Azerbaijan (Azərbaycan)",
42443         "az",
42444         "994"
42445       ],
42446       [
42447         "Bahamas",
42448         "bs",
42449         "1242"
42450       ],
42451       [
42452         "Bahrain (‫البحرين‬‎)",
42453         "bh",
42454         "973"
42455       ],
42456       [
42457         "Bangladesh (বাংলাদেশ)",
42458         "bd",
42459         "880"
42460       ],
42461       [
42462         "Barbados",
42463         "bb",
42464         "1246"
42465       ],
42466       [
42467         "Belarus (Беларусь)",
42468         "by",
42469         "375"
42470       ],
42471       [
42472         "Belgium (België)",
42473         "be",
42474         "32"
42475       ],
42476       [
42477         "Belize",
42478         "bz",
42479         "501"
42480       ],
42481       [
42482         "Benin (Bénin)",
42483         "bj",
42484         "229"
42485       ],
42486       [
42487         "Bermuda",
42488         "bm",
42489         "1441"
42490       ],
42491       [
42492         "Bhutan (འབྲུག)",
42493         "bt",
42494         "975"
42495       ],
42496       [
42497         "Bolivia",
42498         "bo",
42499         "591"
42500       ],
42501       [
42502         "Bosnia and Herzegovina (Босна и Херцеговина)",
42503         "ba",
42504         "387"
42505       ],
42506       [
42507         "Botswana",
42508         "bw",
42509         "267"
42510       ],
42511       [
42512         "Brazil (Brasil)",
42513         "br",
42514         "55"
42515       ],
42516       [
42517         "British Indian Ocean Territory",
42518         "io",
42519         "246"
42520       ],
42521       [
42522         "British Virgin Islands",
42523         "vg",
42524         "1284"
42525       ],
42526       [
42527         "Brunei",
42528         "bn",
42529         "673"
42530       ],
42531       [
42532         "Bulgaria (България)",
42533         "bg",
42534         "359"
42535       ],
42536       [
42537         "Burkina Faso",
42538         "bf",
42539         "226"
42540       ],
42541       [
42542         "Burundi (Uburundi)",
42543         "bi",
42544         "257"
42545       ],
42546       [
42547         "Cambodia (កម្ពុជា)",
42548         "kh",
42549         "855"
42550       ],
42551       [
42552         "Cameroon (Cameroun)",
42553         "cm",
42554         "237"
42555       ],
42556       [
42557         "Canada",
42558         "ca",
42559         "1",
42560         1,
42561         ["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"]
42562       ],
42563       [
42564         "Cape Verde (Kabu Verdi)",
42565         "cv",
42566         "238"
42567       ],
42568       [
42569         "Caribbean Netherlands",
42570         "bq",
42571         "599",
42572         1
42573       ],
42574       [
42575         "Cayman Islands",
42576         "ky",
42577         "1345"
42578       ],
42579       [
42580         "Central African Republic (République centrafricaine)",
42581         "cf",
42582         "236"
42583       ],
42584       [
42585         "Chad (Tchad)",
42586         "td",
42587         "235"
42588       ],
42589       [
42590         "Chile",
42591         "cl",
42592         "56"
42593       ],
42594       [
42595         "China (中国)",
42596         "cn",
42597         "86"
42598       ],
42599       [
42600         "Christmas Island",
42601         "cx",
42602         "61",
42603         2
42604       ],
42605       [
42606         "Cocos (Keeling) Islands",
42607         "cc",
42608         "61",
42609         1
42610       ],
42611       [
42612         "Colombia",
42613         "co",
42614         "57"
42615       ],
42616       [
42617         "Comoros (‫جزر القمر‬‎)",
42618         "km",
42619         "269"
42620       ],
42621       [
42622         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42623         "cd",
42624         "243"
42625       ],
42626       [
42627         "Congo (Republic) (Congo-Brazzaville)",
42628         "cg",
42629         "242"
42630       ],
42631       [
42632         "Cook Islands",
42633         "ck",
42634         "682"
42635       ],
42636       [
42637         "Costa Rica",
42638         "cr",
42639         "506"
42640       ],
42641       [
42642         "Côte d’Ivoire",
42643         "ci",
42644         "225"
42645       ],
42646       [
42647         "Croatia (Hrvatska)",
42648         "hr",
42649         "385"
42650       ],
42651       [
42652         "Cuba",
42653         "cu",
42654         "53"
42655       ],
42656       [
42657         "Curaçao",
42658         "cw",
42659         "599",
42660         0
42661       ],
42662       [
42663         "Cyprus (Κύπρος)",
42664         "cy",
42665         "357"
42666       ],
42667       [
42668         "Czech Republic (Česká republika)",
42669         "cz",
42670         "420"
42671       ],
42672       [
42673         "Denmark (Danmark)",
42674         "dk",
42675         "45"
42676       ],
42677       [
42678         "Djibouti",
42679         "dj",
42680         "253"
42681       ],
42682       [
42683         "Dominica",
42684         "dm",
42685         "1767"
42686       ],
42687       [
42688         "Dominican Republic (República Dominicana)",
42689         "do",
42690         "1",
42691         2,
42692         ["809", "829", "849"]
42693       ],
42694       [
42695         "Ecuador",
42696         "ec",
42697         "593"
42698       ],
42699       [
42700         "Egypt (‫مصر‬‎)",
42701         "eg",
42702         "20"
42703       ],
42704       [
42705         "El Salvador",
42706         "sv",
42707         "503"
42708       ],
42709       [
42710         "Equatorial Guinea (Guinea Ecuatorial)",
42711         "gq",
42712         "240"
42713       ],
42714       [
42715         "Eritrea",
42716         "er",
42717         "291"
42718       ],
42719       [
42720         "Estonia (Eesti)",
42721         "ee",
42722         "372"
42723       ],
42724       [
42725         "Ethiopia",
42726         "et",
42727         "251"
42728       ],
42729       [
42730         "Falkland Islands (Islas Malvinas)",
42731         "fk",
42732         "500"
42733       ],
42734       [
42735         "Faroe Islands (Føroyar)",
42736         "fo",
42737         "298"
42738       ],
42739       [
42740         "Fiji",
42741         "fj",
42742         "679"
42743       ],
42744       [
42745         "Finland (Suomi)",
42746         "fi",
42747         "358",
42748         0
42749       ],
42750       [
42751         "France",
42752         "fr",
42753         "33"
42754       ],
42755       [
42756         "French Guiana (Guyane française)",
42757         "gf",
42758         "594"
42759       ],
42760       [
42761         "French Polynesia (Polynésie française)",
42762         "pf",
42763         "689"
42764       ],
42765       [
42766         "Gabon",
42767         "ga",
42768         "241"
42769       ],
42770       [
42771         "Gambia",
42772         "gm",
42773         "220"
42774       ],
42775       [
42776         "Georgia (საქართველო)",
42777         "ge",
42778         "995"
42779       ],
42780       [
42781         "Germany (Deutschland)",
42782         "de",
42783         "49"
42784       ],
42785       [
42786         "Ghana (Gaana)",
42787         "gh",
42788         "233"
42789       ],
42790       [
42791         "Gibraltar",
42792         "gi",
42793         "350"
42794       ],
42795       [
42796         "Greece (Ελλάδα)",
42797         "gr",
42798         "30"
42799       ],
42800       [
42801         "Greenland (Kalaallit Nunaat)",
42802         "gl",
42803         "299"
42804       ],
42805       [
42806         "Grenada",
42807         "gd",
42808         "1473"
42809       ],
42810       [
42811         "Guadeloupe",
42812         "gp",
42813         "590",
42814         0
42815       ],
42816       [
42817         "Guam",
42818         "gu",
42819         "1671"
42820       ],
42821       [
42822         "Guatemala",
42823         "gt",
42824         "502"
42825       ],
42826       [
42827         "Guernsey",
42828         "gg",
42829         "44",
42830         1
42831       ],
42832       [
42833         "Guinea (Guinée)",
42834         "gn",
42835         "224"
42836       ],
42837       [
42838         "Guinea-Bissau (Guiné Bissau)",
42839         "gw",
42840         "245"
42841       ],
42842       [
42843         "Guyana",
42844         "gy",
42845         "592"
42846       ],
42847       [
42848         "Haiti",
42849         "ht",
42850         "509"
42851       ],
42852       [
42853         "Honduras",
42854         "hn",
42855         "504"
42856       ],
42857       [
42858         "Hong Kong (香港)",
42859         "hk",
42860         "852"
42861       ],
42862       [
42863         "Hungary (Magyarország)",
42864         "hu",
42865         "36"
42866       ],
42867       [
42868         "Iceland (Ísland)",
42869         "is",
42870         "354"
42871       ],
42872       [
42873         "India (भारत)",
42874         "in",
42875         "91"
42876       ],
42877       [
42878         "Indonesia",
42879         "id",
42880         "62"
42881       ],
42882       [
42883         "Iran (‫ایران‬‎)",
42884         "ir",
42885         "98"
42886       ],
42887       [
42888         "Iraq (‫العراق‬‎)",
42889         "iq",
42890         "964"
42891       ],
42892       [
42893         "Ireland",
42894         "ie",
42895         "353"
42896       ],
42897       [
42898         "Isle of Man",
42899         "im",
42900         "44",
42901         2
42902       ],
42903       [
42904         "Israel (‫ישראל‬‎)",
42905         "il",
42906         "972"
42907       ],
42908       [
42909         "Italy (Italia)",
42910         "it",
42911         "39",
42912         0
42913       ],
42914       [
42915         "Jamaica",
42916         "jm",
42917         "1876"
42918       ],
42919       [
42920         "Japan (日本)",
42921         "jp",
42922         "81"
42923       ],
42924       [
42925         "Jersey",
42926         "je",
42927         "44",
42928         3
42929       ],
42930       [
42931         "Jordan (‫الأردن‬‎)",
42932         "jo",
42933         "962"
42934       ],
42935       [
42936         "Kazakhstan (Казахстан)",
42937         "kz",
42938         "7",
42939         1
42940       ],
42941       [
42942         "Kenya",
42943         "ke",
42944         "254"
42945       ],
42946       [
42947         "Kiribati",
42948         "ki",
42949         "686"
42950       ],
42951       [
42952         "Kosovo",
42953         "xk",
42954         "383"
42955       ],
42956       [
42957         "Kuwait (‫الكويت‬‎)",
42958         "kw",
42959         "965"
42960       ],
42961       [
42962         "Kyrgyzstan (Кыргызстан)",
42963         "kg",
42964         "996"
42965       ],
42966       [
42967         "Laos (ລາວ)",
42968         "la",
42969         "856"
42970       ],
42971       [
42972         "Latvia (Latvija)",
42973         "lv",
42974         "371"
42975       ],
42976       [
42977         "Lebanon (‫لبنان‬‎)",
42978         "lb",
42979         "961"
42980       ],
42981       [
42982         "Lesotho",
42983         "ls",
42984         "266"
42985       ],
42986       [
42987         "Liberia",
42988         "lr",
42989         "231"
42990       ],
42991       [
42992         "Libya (‫ليبيا‬‎)",
42993         "ly",
42994         "218"
42995       ],
42996       [
42997         "Liechtenstein",
42998         "li",
42999         "423"
43000       ],
43001       [
43002         "Lithuania (Lietuva)",
43003         "lt",
43004         "370"
43005       ],
43006       [
43007         "Luxembourg",
43008         "lu",
43009         "352"
43010       ],
43011       [
43012         "Macau (澳門)",
43013         "mo",
43014         "853"
43015       ],
43016       [
43017         "Macedonia (FYROM) (Македонија)",
43018         "mk",
43019         "389"
43020       ],
43021       [
43022         "Madagascar (Madagasikara)",
43023         "mg",
43024         "261"
43025       ],
43026       [
43027         "Malawi",
43028         "mw",
43029         "265"
43030       ],
43031       [
43032         "Malaysia",
43033         "my",
43034         "60"
43035       ],
43036       [
43037         "Maldives",
43038         "mv",
43039         "960"
43040       ],
43041       [
43042         "Mali",
43043         "ml",
43044         "223"
43045       ],
43046       [
43047         "Malta",
43048         "mt",
43049         "356"
43050       ],
43051       [
43052         "Marshall Islands",
43053         "mh",
43054         "692"
43055       ],
43056       [
43057         "Martinique",
43058         "mq",
43059         "596"
43060       ],
43061       [
43062         "Mauritania (‫موريتانيا‬‎)",
43063         "mr",
43064         "222"
43065       ],
43066       [
43067         "Mauritius (Moris)",
43068         "mu",
43069         "230"
43070       ],
43071       [
43072         "Mayotte",
43073         "yt",
43074         "262",
43075         1
43076       ],
43077       [
43078         "Mexico (México)",
43079         "mx",
43080         "52"
43081       ],
43082       [
43083         "Micronesia",
43084         "fm",
43085         "691"
43086       ],
43087       [
43088         "Moldova (Republica Moldova)",
43089         "md",
43090         "373"
43091       ],
43092       [
43093         "Monaco",
43094         "mc",
43095         "377"
43096       ],
43097       [
43098         "Mongolia (Монгол)",
43099         "mn",
43100         "976"
43101       ],
43102       [
43103         "Montenegro (Crna Gora)",
43104         "me",
43105         "382"
43106       ],
43107       [
43108         "Montserrat",
43109         "ms",
43110         "1664"
43111       ],
43112       [
43113         "Morocco (‫المغرب‬‎)",
43114         "ma",
43115         "212",
43116         0
43117       ],
43118       [
43119         "Mozambique (Moçambique)",
43120         "mz",
43121         "258"
43122       ],
43123       [
43124         "Myanmar (Burma) (မြန်မာ)",
43125         "mm",
43126         "95"
43127       ],
43128       [
43129         "Namibia (Namibië)",
43130         "na",
43131         "264"
43132       ],
43133       [
43134         "Nauru",
43135         "nr",
43136         "674"
43137       ],
43138       [
43139         "Nepal (नेपाल)",
43140         "np",
43141         "977"
43142       ],
43143       [
43144         "Netherlands (Nederland)",
43145         "nl",
43146         "31"
43147       ],
43148       [
43149         "New Caledonia (Nouvelle-Calédonie)",
43150         "nc",
43151         "687"
43152       ],
43153       [
43154         "New Zealand",
43155         "nz",
43156         "64"
43157       ],
43158       [
43159         "Nicaragua",
43160         "ni",
43161         "505"
43162       ],
43163       [
43164         "Niger (Nijar)",
43165         "ne",
43166         "227"
43167       ],
43168       [
43169         "Nigeria",
43170         "ng",
43171         "234"
43172       ],
43173       [
43174         "Niue",
43175         "nu",
43176         "683"
43177       ],
43178       [
43179         "Norfolk Island",
43180         "nf",
43181         "672"
43182       ],
43183       [
43184         "North Korea (조선 민주주의 인민 공화국)",
43185         "kp",
43186         "850"
43187       ],
43188       [
43189         "Northern Mariana Islands",
43190         "mp",
43191         "1670"
43192       ],
43193       [
43194         "Norway (Norge)",
43195         "no",
43196         "47",
43197         0
43198       ],
43199       [
43200         "Oman (‫عُمان‬‎)",
43201         "om",
43202         "968"
43203       ],
43204       [
43205         "Pakistan (‫پاکستان‬‎)",
43206         "pk",
43207         "92"
43208       ],
43209       [
43210         "Palau",
43211         "pw",
43212         "680"
43213       ],
43214       [
43215         "Palestine (‫فلسطين‬‎)",
43216         "ps",
43217         "970"
43218       ],
43219       [
43220         "Panama (Panamá)",
43221         "pa",
43222         "507"
43223       ],
43224       [
43225         "Papua New Guinea",
43226         "pg",
43227         "675"
43228       ],
43229       [
43230         "Paraguay",
43231         "py",
43232         "595"
43233       ],
43234       [
43235         "Peru (Perú)",
43236         "pe",
43237         "51"
43238       ],
43239       [
43240         "Philippines",
43241         "ph",
43242         "63"
43243       ],
43244       [
43245         "Poland (Polska)",
43246         "pl",
43247         "48"
43248       ],
43249       [
43250         "Portugal",
43251         "pt",
43252         "351"
43253       ],
43254       [
43255         "Puerto Rico",
43256         "pr",
43257         "1",
43258         3,
43259         ["787", "939"]
43260       ],
43261       [
43262         "Qatar (‫قطر‬‎)",
43263         "qa",
43264         "974"
43265       ],
43266       [
43267         "Réunion (La Réunion)",
43268         "re",
43269         "262",
43270         0
43271       ],
43272       [
43273         "Romania (România)",
43274         "ro",
43275         "40"
43276       ],
43277       [
43278         "Russia (Россия)",
43279         "ru",
43280         "7",
43281         0
43282       ],
43283       [
43284         "Rwanda",
43285         "rw",
43286         "250"
43287       ],
43288       [
43289         "Saint Barthélemy",
43290         "bl",
43291         "590",
43292         1
43293       ],
43294       [
43295         "Saint Helena",
43296         "sh",
43297         "290"
43298       ],
43299       [
43300         "Saint Kitts and Nevis",
43301         "kn",
43302         "1869"
43303       ],
43304       [
43305         "Saint Lucia",
43306         "lc",
43307         "1758"
43308       ],
43309       [
43310         "Saint Martin (Saint-Martin (partie française))",
43311         "mf",
43312         "590",
43313         2
43314       ],
43315       [
43316         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43317         "pm",
43318         "508"
43319       ],
43320       [
43321         "Saint Vincent and the Grenadines",
43322         "vc",
43323         "1784"
43324       ],
43325       [
43326         "Samoa",
43327         "ws",
43328         "685"
43329       ],
43330       [
43331         "San Marino",
43332         "sm",
43333         "378"
43334       ],
43335       [
43336         "São Tomé and Príncipe (São Tomé e Príncipe)",
43337         "st",
43338         "239"
43339       ],
43340       [
43341         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43342         "sa",
43343         "966"
43344       ],
43345       [
43346         "Senegal (Sénégal)",
43347         "sn",
43348         "221"
43349       ],
43350       [
43351         "Serbia (Србија)",
43352         "rs",
43353         "381"
43354       ],
43355       [
43356         "Seychelles",
43357         "sc",
43358         "248"
43359       ],
43360       [
43361         "Sierra Leone",
43362         "sl",
43363         "232"
43364       ],
43365       [
43366         "Singapore",
43367         "sg",
43368         "65"
43369       ],
43370       [
43371         "Sint Maarten",
43372         "sx",
43373         "1721"
43374       ],
43375       [
43376         "Slovakia (Slovensko)",
43377         "sk",
43378         "421"
43379       ],
43380       [
43381         "Slovenia (Slovenija)",
43382         "si",
43383         "386"
43384       ],
43385       [
43386         "Solomon Islands",
43387         "sb",
43388         "677"
43389       ],
43390       [
43391         "Somalia (Soomaaliya)",
43392         "so",
43393         "252"
43394       ],
43395       [
43396         "South Africa",
43397         "za",
43398         "27"
43399       ],
43400       [
43401         "South Korea (대한민국)",
43402         "kr",
43403         "82"
43404       ],
43405       [
43406         "South Sudan (‫جنوب السودان‬‎)",
43407         "ss",
43408         "211"
43409       ],
43410       [
43411         "Spain (España)",
43412         "es",
43413         "34"
43414       ],
43415       [
43416         "Sri Lanka (ශ්‍රී ලංකාව)",
43417         "lk",
43418         "94"
43419       ],
43420       [
43421         "Sudan (‫السودان‬‎)",
43422         "sd",
43423         "249"
43424       ],
43425       [
43426         "Suriname",
43427         "sr",
43428         "597"
43429       ],
43430       [
43431         "Svalbard and Jan Mayen",
43432         "sj",
43433         "47",
43434         1
43435       ],
43436       [
43437         "Swaziland",
43438         "sz",
43439         "268"
43440       ],
43441       [
43442         "Sweden (Sverige)",
43443         "se",
43444         "46"
43445       ],
43446       [
43447         "Switzerland (Schweiz)",
43448         "ch",
43449         "41"
43450       ],
43451       [
43452         "Syria (‫سوريا‬‎)",
43453         "sy",
43454         "963"
43455       ],
43456       [
43457         "Taiwan (台灣)",
43458         "tw",
43459         "886"
43460       ],
43461       [
43462         "Tajikistan",
43463         "tj",
43464         "992"
43465       ],
43466       [
43467         "Tanzania",
43468         "tz",
43469         "255"
43470       ],
43471       [
43472         "Thailand (ไทย)",
43473         "th",
43474         "66"
43475       ],
43476       [
43477         "Timor-Leste",
43478         "tl",
43479         "670"
43480       ],
43481       [
43482         "Togo",
43483         "tg",
43484         "228"
43485       ],
43486       [
43487         "Tokelau",
43488         "tk",
43489         "690"
43490       ],
43491       [
43492         "Tonga",
43493         "to",
43494         "676"
43495       ],
43496       [
43497         "Trinidad and Tobago",
43498         "tt",
43499         "1868"
43500       ],
43501       [
43502         "Tunisia (‫تونس‬‎)",
43503         "tn",
43504         "216"
43505       ],
43506       [
43507         "Turkey (Türkiye)",
43508         "tr",
43509         "90"
43510       ],
43511       [
43512         "Turkmenistan",
43513         "tm",
43514         "993"
43515       ],
43516       [
43517         "Turks and Caicos Islands",
43518         "tc",
43519         "1649"
43520       ],
43521       [
43522         "Tuvalu",
43523         "tv",
43524         "688"
43525       ],
43526       [
43527         "U.S. Virgin Islands",
43528         "vi",
43529         "1340"
43530       ],
43531       [
43532         "Uganda",
43533         "ug",
43534         "256"
43535       ],
43536       [
43537         "Ukraine (Україна)",
43538         "ua",
43539         "380"
43540       ],
43541       [
43542         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43543         "ae",
43544         "971"
43545       ],
43546       [
43547         "United Kingdom",
43548         "gb",
43549         "44",
43550         0
43551       ],
43552       [
43553         "United States",
43554         "us",
43555         "1",
43556         0
43557       ],
43558       [
43559         "Uruguay",
43560         "uy",
43561         "598"
43562       ],
43563       [
43564         "Uzbekistan (Oʻzbekiston)",
43565         "uz",
43566         "998"
43567       ],
43568       [
43569         "Vanuatu",
43570         "vu",
43571         "678"
43572       ],
43573       [
43574         "Vatican City (Città del Vaticano)",
43575         "va",
43576         "39",
43577         1
43578       ],
43579       [
43580         "Venezuela",
43581         "ve",
43582         "58"
43583       ],
43584       [
43585         "Vietnam (Việt Nam)",
43586         "vn",
43587         "84"
43588       ],
43589       [
43590         "Wallis and Futuna (Wallis-et-Futuna)",
43591         "wf",
43592         "681"
43593       ],
43594       [
43595         "Western Sahara (‫الصحراء الغربية‬‎)",
43596         "eh",
43597         "212",
43598         1
43599       ],
43600       [
43601         "Yemen (‫اليمن‬‎)",
43602         "ye",
43603         "967"
43604       ],
43605       [
43606         "Zambia",
43607         "zm",
43608         "260"
43609       ],
43610       [
43611         "Zimbabwe",
43612         "zw",
43613         "263"
43614       ],
43615       [
43616         "Åland Islands",
43617         "ax",
43618         "358",
43619         1
43620       ]
43621   ];
43622   
43623   return d;
43624 }/**
43625 *    This script refer to:
43626 *    Title: International Telephone Input
43627 *    Author: Jack O'Connor
43628 *    Code version:  v12.1.12
43629 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43630 **/
43631
43632 /**
43633  * @class Roo.bootstrap.PhoneInput
43634  * @extends Roo.bootstrap.TriggerField
43635  * An input with International dial-code selection
43636  
43637  * @cfg {String} defaultDialCode default '+852'
43638  * @cfg {Array} preferedCountries default []
43639   
43640  * @constructor
43641  * Create a new PhoneInput.
43642  * @param {Object} config Configuration options
43643  */
43644
43645 Roo.bootstrap.PhoneInput = function(config) {
43646     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43647 };
43648
43649 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43650         /**
43651         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43652         */
43653         listWidth: undefined,
43654         
43655         selectedClass: 'active',
43656         
43657         invalidClass : "has-warning",
43658         
43659         validClass: 'has-success',
43660         
43661         allowed: '0123456789',
43662         
43663         max_length: 15,
43664         
43665         /**
43666          * @cfg {String} defaultDialCode The default dial code when initializing the input
43667          */
43668         defaultDialCode: '+852',
43669         
43670         /**
43671          * @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
43672          */
43673         preferedCountries: false,
43674         
43675         getAutoCreate : function()
43676         {
43677             var data = Roo.bootstrap.PhoneInputData();
43678             var align = this.labelAlign || this.parentLabelAlign();
43679             var id = Roo.id();
43680             
43681             this.allCountries = [];
43682             this.dialCodeMapping = [];
43683             
43684             for (var i = 0; i < data.length; i++) {
43685               var c = data[i];
43686               this.allCountries[i] = {
43687                 name: c[0],
43688                 iso2: c[1],
43689                 dialCode: c[2],
43690                 priority: c[3] || 0,
43691                 areaCodes: c[4] || null
43692               };
43693               this.dialCodeMapping[c[2]] = {
43694                   name: c[0],
43695                   iso2: c[1],
43696                   priority: c[3] || 0,
43697                   areaCodes: c[4] || null
43698               };
43699             }
43700             
43701             var cfg = {
43702                 cls: 'form-group',
43703                 cn: []
43704             };
43705             
43706             var input =  {
43707                 tag: 'input',
43708                 id : id,
43709                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43710                 maxlength: this.max_length,
43711                 cls : 'form-control tel-input',
43712                 autocomplete: 'new-password'
43713             };
43714             
43715             var hiddenInput = {
43716                 tag: 'input',
43717                 type: 'hidden',
43718                 cls: 'hidden-tel-input'
43719             };
43720             
43721             if (this.name) {
43722                 hiddenInput.name = this.name;
43723             }
43724             
43725             if (this.disabled) {
43726                 input.disabled = true;
43727             }
43728             
43729             var flag_container = {
43730                 tag: 'div',
43731                 cls: 'flag-box',
43732                 cn: [
43733                     {
43734                         tag: 'div',
43735                         cls: 'flag'
43736                     },
43737                     {
43738                         tag: 'div',
43739                         cls: 'caret'
43740                     }
43741                 ]
43742             };
43743             
43744             var box = {
43745                 tag: 'div',
43746                 cls: this.hasFeedback ? 'has-feedback' : '',
43747                 cn: [
43748                     hiddenInput,
43749                     input,
43750                     {
43751                         tag: 'input',
43752                         cls: 'dial-code-holder',
43753                         disabled: true
43754                     }
43755                 ]
43756             };
43757             
43758             var container = {
43759                 cls: 'roo-select2-container input-group',
43760                 cn: [
43761                     flag_container,
43762                     box
43763                 ]
43764             };
43765             
43766             if (this.fieldLabel.length) {
43767                 var indicator = {
43768                     tag: 'i',
43769                     tooltip: 'This field is required'
43770                 };
43771                 
43772                 var label = {
43773                     tag: 'label',
43774                     'for':  id,
43775                     cls: 'control-label',
43776                     cn: []
43777                 };
43778                 
43779                 var label_text = {
43780                     tag: 'span',
43781                     html: this.fieldLabel
43782                 };
43783                 
43784                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43785                 label.cn = [
43786                     indicator,
43787                     label_text
43788                 ];
43789                 
43790                 if(this.indicatorpos == 'right') {
43791                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43792                     label.cn = [
43793                         label_text,
43794                         indicator
43795                     ];
43796                 }
43797                 
43798                 if(align == 'left') {
43799                     container = {
43800                         tag: 'div',
43801                         cn: [
43802                             container
43803                         ]
43804                     };
43805                     
43806                     if(this.labelWidth > 12){
43807                         label.style = "width: " + this.labelWidth + 'px';
43808                     }
43809                     if(this.labelWidth < 13 && this.labelmd == 0){
43810                         this.labelmd = this.labelWidth;
43811                     }
43812                     if(this.labellg > 0){
43813                         label.cls += ' col-lg-' + this.labellg;
43814                         input.cls += ' col-lg-' + (12 - this.labellg);
43815                     }
43816                     if(this.labelmd > 0){
43817                         label.cls += ' col-md-' + this.labelmd;
43818                         container.cls += ' col-md-' + (12 - this.labelmd);
43819                     }
43820                     if(this.labelsm > 0){
43821                         label.cls += ' col-sm-' + this.labelsm;
43822                         container.cls += ' col-sm-' + (12 - this.labelsm);
43823                     }
43824                     if(this.labelxs > 0){
43825                         label.cls += ' col-xs-' + this.labelxs;
43826                         container.cls += ' col-xs-' + (12 - this.labelxs);
43827                     }
43828                 }
43829             }
43830             
43831             cfg.cn = [
43832                 label,
43833                 container
43834             ];
43835             
43836             var settings = this;
43837             
43838             ['xs','sm','md','lg'].map(function(size){
43839                 if (settings[size]) {
43840                     cfg.cls += ' col-' + size + '-' + settings[size];
43841                 }
43842             });
43843             
43844             this.store = new Roo.data.Store({
43845                 proxy : new Roo.data.MemoryProxy({}),
43846                 reader : new Roo.data.JsonReader({
43847                     fields : [
43848                         {
43849                             'name' : 'name',
43850                             'type' : 'string'
43851                         },
43852                         {
43853                             'name' : 'iso2',
43854                             'type' : 'string'
43855                         },
43856                         {
43857                             'name' : 'dialCode',
43858                             'type' : 'string'
43859                         },
43860                         {
43861                             'name' : 'priority',
43862                             'type' : 'string'
43863                         },
43864                         {
43865                             'name' : 'areaCodes',
43866                             'type' : 'string'
43867                         }
43868                     ]
43869                 })
43870             });
43871             
43872             if(!this.preferedCountries) {
43873                 this.preferedCountries = [
43874                     'hk',
43875                     'gb',
43876                     'us'
43877                 ];
43878             }
43879             
43880             var p = this.preferedCountries.reverse();
43881             
43882             if(p) {
43883                 for (var i = 0; i < p.length; i++) {
43884                     for (var j = 0; j < this.allCountries.length; j++) {
43885                         if(this.allCountries[j].iso2 == p[i]) {
43886                             var t = this.allCountries[j];
43887                             this.allCountries.splice(j,1);
43888                             this.allCountries.unshift(t);
43889                         }
43890                     } 
43891                 }
43892             }
43893             
43894             this.store.proxy.data = {
43895                 success: true,
43896                 data: this.allCountries
43897             };
43898             
43899             return cfg;
43900         },
43901         
43902         initEvents : function()
43903         {
43904             this.createList();
43905             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43906             
43907             this.indicator = this.indicatorEl();
43908             this.flag = this.flagEl();
43909             this.dialCodeHolder = this.dialCodeHolderEl();
43910             
43911             this.trigger = this.el.select('div.flag-box',true).first();
43912             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43913             
43914             var _this = this;
43915             
43916             (function(){
43917                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43918                 _this.list.setWidth(lw);
43919             }).defer(100);
43920             
43921             this.list.on('mouseover', this.onViewOver, this);
43922             this.list.on('mousemove', this.onViewMove, this);
43923             this.inputEl().on("keyup", this.onKeyUp, this);
43924             this.inputEl().on("keypress", this.onKeyPress, this);
43925             
43926             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43927
43928             this.view = new Roo.View(this.list, this.tpl, {
43929                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43930             });
43931             
43932             this.view.on('click', this.onViewClick, this);
43933             this.setValue(this.defaultDialCode);
43934         },
43935         
43936         onTriggerClick : function(e)
43937         {
43938             Roo.log('trigger click');
43939             if(this.disabled){
43940                 return;
43941             }
43942             
43943             if(this.isExpanded()){
43944                 this.collapse();
43945                 this.hasFocus = false;
43946             }else {
43947                 this.store.load({});
43948                 this.hasFocus = true;
43949                 this.expand();
43950             }
43951         },
43952         
43953         isExpanded : function()
43954         {
43955             return this.list.isVisible();
43956         },
43957         
43958         collapse : function()
43959         {
43960             if(!this.isExpanded()){
43961                 return;
43962             }
43963             this.list.hide();
43964             Roo.get(document).un('mousedown', this.collapseIf, this);
43965             Roo.get(document).un('mousewheel', this.collapseIf, this);
43966             this.fireEvent('collapse', this);
43967             this.validate();
43968         },
43969         
43970         expand : function()
43971         {
43972             Roo.log('expand');
43973
43974             if(this.isExpanded() || !this.hasFocus){
43975                 return;
43976             }
43977             
43978             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43979             this.list.setWidth(lw);
43980             
43981             this.list.show();
43982             this.restrictHeight();
43983             
43984             Roo.get(document).on('mousedown', this.collapseIf, this);
43985             Roo.get(document).on('mousewheel', this.collapseIf, this);
43986             
43987             this.fireEvent('expand', this);
43988         },
43989         
43990         restrictHeight : function()
43991         {
43992             this.list.alignTo(this.inputEl(), this.listAlign);
43993             this.list.alignTo(this.inputEl(), this.listAlign);
43994         },
43995         
43996         onViewOver : function(e, t)
43997         {
43998             if(this.inKeyMode){
43999                 return;
44000             }
44001             var item = this.view.findItemFromChild(t);
44002             
44003             if(item){
44004                 var index = this.view.indexOf(item);
44005                 this.select(index, false);
44006             }
44007         },
44008
44009         // private
44010         onViewClick : function(view, doFocus, el, e)
44011         {
44012             var index = this.view.getSelectedIndexes()[0];
44013             
44014             var r = this.store.getAt(index);
44015             
44016             if(r){
44017                 this.onSelect(r, index);
44018             }
44019             if(doFocus !== false && !this.blockFocus){
44020                 this.inputEl().focus();
44021             }
44022         },
44023         
44024         onViewMove : function(e, t)
44025         {
44026             this.inKeyMode = false;
44027         },
44028         
44029         select : function(index, scrollIntoView)
44030         {
44031             this.selectedIndex = index;
44032             this.view.select(index);
44033             if(scrollIntoView !== false){
44034                 var el = this.view.getNode(index);
44035                 if(el){
44036                     this.list.scrollChildIntoView(el, false);
44037                 }
44038             }
44039         },
44040         
44041         createList : function()
44042         {
44043             this.list = Roo.get(document.body).createChild({
44044                 tag: 'ul',
44045                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44046                 style: 'display:none'
44047             });
44048             
44049             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44050         },
44051         
44052         collapseIf : function(e)
44053         {
44054             var in_combo  = e.within(this.el);
44055             var in_list =  e.within(this.list);
44056             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44057             
44058             if (in_combo || in_list || is_list) {
44059                 return;
44060             }
44061             this.collapse();
44062         },
44063         
44064         onSelect : function(record, index)
44065         {
44066             if(this.fireEvent('beforeselect', this, record, index) !== false){
44067                 
44068                 this.setFlagClass(record.data.iso2);
44069                 this.setDialCode(record.data.dialCode);
44070                 this.hasFocus = false;
44071                 this.collapse();
44072                 this.fireEvent('select', this, record, index);
44073             }
44074         },
44075         
44076         flagEl : function()
44077         {
44078             var flag = this.el.select('div.flag',true).first();
44079             if(!flag){
44080                 return false;
44081             }
44082             return flag;
44083         },
44084         
44085         dialCodeHolderEl : function()
44086         {
44087             var d = this.el.select('input.dial-code-holder',true).first();
44088             if(!d){
44089                 return false;
44090             }
44091             return d;
44092         },
44093         
44094         setDialCode : function(v)
44095         {
44096             this.dialCodeHolder.dom.value = '+'+v;
44097         },
44098         
44099         setFlagClass : function(n)
44100         {
44101             this.flag.dom.className = 'flag '+n;
44102         },
44103         
44104         getValue : function()
44105         {
44106             var v = this.inputEl().getValue();
44107             if(this.dialCodeHolder) {
44108                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44109             }
44110             return v;
44111         },
44112         
44113         setValue : function(v)
44114         {
44115             var d = this.getDialCode(v);
44116             
44117             //invalid dial code
44118             if(v.length == 0 || !d || d.length == 0) {
44119                 if(this.rendered){
44120                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44121                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44122                 }
44123                 return;
44124             }
44125             
44126             //valid dial code
44127             this.setFlagClass(this.dialCodeMapping[d].iso2);
44128             this.setDialCode(d);
44129             this.inputEl().dom.value = v.replace('+'+d,'');
44130             this.hiddenEl().dom.value = this.getValue();
44131             
44132             this.validate();
44133         },
44134         
44135         getDialCode : function(v)
44136         {
44137             v = v ||  '';
44138             
44139             if (v.length == 0) {
44140                 return this.dialCodeHolder.dom.value;
44141             }
44142             
44143             var dialCode = "";
44144             if (v.charAt(0) != "+") {
44145                 return false;
44146             }
44147             var numericChars = "";
44148             for (var i = 1; i < v.length; i++) {
44149               var c = v.charAt(i);
44150               if (!isNaN(c)) {
44151                 numericChars += c;
44152                 if (this.dialCodeMapping[numericChars]) {
44153                   dialCode = v.substr(1, i);
44154                 }
44155                 if (numericChars.length == 4) {
44156                   break;
44157                 }
44158               }
44159             }
44160             return dialCode;
44161         },
44162         
44163         reset : function()
44164         {
44165             this.setValue(this.defaultDialCode);
44166             this.validate();
44167         },
44168         
44169         hiddenEl : function()
44170         {
44171             return this.el.select('input.hidden-tel-input',true).first();
44172         },
44173         
44174         // after setting val
44175         onKeyUp : function(e){
44176             this.setValue(this.getValue());
44177         },
44178         
44179         onKeyPress : function(e){
44180             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44181                 e.stopEvent();
44182             }
44183         }
44184         
44185 });
44186 /**
44187  * @class Roo.bootstrap.MoneyField
44188  * @extends Roo.bootstrap.ComboBox
44189  * Bootstrap MoneyField class
44190  * 
44191  * @constructor
44192  * Create a new MoneyField.
44193  * @param {Object} config Configuration options
44194  */
44195
44196 Roo.bootstrap.MoneyField = function(config) {
44197     
44198     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44199     
44200 };
44201
44202 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44203     
44204     /**
44205      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44206      */
44207     allowDecimals : true,
44208     /**
44209      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44210      */
44211     decimalSeparator : ".",
44212     /**
44213      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44214      */
44215     decimalPrecision : 0,
44216     /**
44217      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44218      */
44219     allowNegative : true,
44220     /**
44221      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44222      */
44223     allowZero: true,
44224     /**
44225      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44226      */
44227     minValue : Number.NEGATIVE_INFINITY,
44228     /**
44229      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44230      */
44231     maxValue : Number.MAX_VALUE,
44232     /**
44233      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44234      */
44235     minText : "The minimum value for this field is {0}",
44236     /**
44237      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44238      */
44239     maxText : "The maximum value for this field is {0}",
44240     /**
44241      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44242      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44243      */
44244     nanText : "{0} is not a valid number",
44245     /**
44246      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44247      */
44248     castInt : true,
44249     /**
44250      * @cfg {String} defaults currency of the MoneyField
44251      * value should be in lkey
44252      */
44253     defaultCurrency : false,
44254     /**
44255      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44256      */
44257     thousandsDelimiter : false,
44258     /**
44259      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44260      */
44261     max_length: false,
44262     
44263     inputlg : 9,
44264     inputmd : 9,
44265     inputsm : 9,
44266     inputxs : 6,
44267     
44268     store : false,
44269     
44270     getAutoCreate : function()
44271     {
44272         var align = this.labelAlign || this.parentLabelAlign();
44273         
44274         var id = Roo.id();
44275
44276         var cfg = {
44277             cls: 'form-group',
44278             cn: []
44279         };
44280
44281         var input =  {
44282             tag: 'input',
44283             id : id,
44284             cls : 'form-control roo-money-amount-input',
44285             autocomplete: 'new-password'
44286         };
44287         
44288         var hiddenInput = {
44289             tag: 'input',
44290             type: 'hidden',
44291             id: Roo.id(),
44292             cls: 'hidden-number-input'
44293         };
44294         
44295         if(this.max_length) {
44296             input.maxlength = this.max_length; 
44297         }
44298         
44299         if (this.name) {
44300             hiddenInput.name = this.name;
44301         }
44302
44303         if (this.disabled) {
44304             input.disabled = true;
44305         }
44306
44307         var clg = 12 - this.inputlg;
44308         var cmd = 12 - this.inputmd;
44309         var csm = 12 - this.inputsm;
44310         var cxs = 12 - this.inputxs;
44311         
44312         var container = {
44313             tag : 'div',
44314             cls : 'row roo-money-field',
44315             cn : [
44316                 {
44317                     tag : 'div',
44318                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44319                     cn : [
44320                         {
44321                             tag : 'div',
44322                             cls: 'roo-select2-container input-group',
44323                             cn: [
44324                                 {
44325                                     tag : 'input',
44326                                     cls : 'form-control roo-money-currency-input',
44327                                     autocomplete: 'new-password',
44328                                     readOnly : 1,
44329                                     name : this.currencyName
44330                                 },
44331                                 {
44332                                     tag :'span',
44333                                     cls : 'input-group-addon',
44334                                     cn : [
44335                                         {
44336                                             tag: 'span',
44337                                             cls: 'caret'
44338                                         }
44339                                     ]
44340                                 }
44341                             ]
44342                         }
44343                     ]
44344                 },
44345                 {
44346                     tag : 'div',
44347                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44348                     cn : [
44349                         {
44350                             tag: 'div',
44351                             cls: this.hasFeedback ? 'has-feedback' : '',
44352                             cn: [
44353                                 input
44354                             ]
44355                         }
44356                     ]
44357                 }
44358             ]
44359             
44360         };
44361         
44362         if (this.fieldLabel.length) {
44363             var indicator = {
44364                 tag: 'i',
44365                 tooltip: 'This field is required'
44366             };
44367
44368             var label = {
44369                 tag: 'label',
44370                 'for':  id,
44371                 cls: 'control-label',
44372                 cn: []
44373             };
44374
44375             var label_text = {
44376                 tag: 'span',
44377                 html: this.fieldLabel
44378             };
44379
44380             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44381             label.cn = [
44382                 indicator,
44383                 label_text
44384             ];
44385
44386             if(this.indicatorpos == 'right') {
44387                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44388                 label.cn = [
44389                     label_text,
44390                     indicator
44391                 ];
44392             }
44393
44394             if(align == 'left') {
44395                 container = {
44396                     tag: 'div',
44397                     cn: [
44398                         container
44399                     ]
44400                 };
44401
44402                 if(this.labelWidth > 12){
44403                     label.style = "width: " + this.labelWidth + 'px';
44404                 }
44405                 if(this.labelWidth < 13 && this.labelmd == 0){
44406                     this.labelmd = this.labelWidth;
44407                 }
44408                 if(this.labellg > 0){
44409                     label.cls += ' col-lg-' + this.labellg;
44410                     input.cls += ' col-lg-' + (12 - this.labellg);
44411                 }
44412                 if(this.labelmd > 0){
44413                     label.cls += ' col-md-' + this.labelmd;
44414                     container.cls += ' col-md-' + (12 - this.labelmd);
44415                 }
44416                 if(this.labelsm > 0){
44417                     label.cls += ' col-sm-' + this.labelsm;
44418                     container.cls += ' col-sm-' + (12 - this.labelsm);
44419                 }
44420                 if(this.labelxs > 0){
44421                     label.cls += ' col-xs-' + this.labelxs;
44422                     container.cls += ' col-xs-' + (12 - this.labelxs);
44423                 }
44424             }
44425         }
44426
44427         cfg.cn = [
44428             label,
44429             container,
44430             hiddenInput
44431         ];
44432         
44433         var settings = this;
44434
44435         ['xs','sm','md','lg'].map(function(size){
44436             if (settings[size]) {
44437                 cfg.cls += ' col-' + size + '-' + settings[size];
44438             }
44439         });
44440         
44441         return cfg;
44442     },
44443     
44444     initEvents : function()
44445     {
44446         this.indicator = this.indicatorEl();
44447         
44448         this.initCurrencyEvent();
44449         
44450         this.initNumberEvent();
44451     },
44452     
44453     initCurrencyEvent : function()
44454     {
44455         if (!this.store) {
44456             throw "can not find store for combo";
44457         }
44458         
44459         this.store = Roo.factory(this.store, Roo.data);
44460         this.store.parent = this;
44461         
44462         this.createList();
44463         
44464         this.triggerEl = this.el.select('.input-group-addon', true).first();
44465         
44466         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44467         
44468         var _this = this;
44469         
44470         (function(){
44471             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44472             _this.list.setWidth(lw);
44473         }).defer(100);
44474         
44475         this.list.on('mouseover', this.onViewOver, this);
44476         this.list.on('mousemove', this.onViewMove, this);
44477         this.list.on('scroll', this.onViewScroll, this);
44478         
44479         if(!this.tpl){
44480             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44481         }
44482         
44483         this.view = new Roo.View(this.list, this.tpl, {
44484             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44485         });
44486         
44487         this.view.on('click', this.onViewClick, this);
44488         
44489         this.store.on('beforeload', this.onBeforeLoad, this);
44490         this.store.on('load', this.onLoad, this);
44491         this.store.on('loadexception', this.onLoadException, this);
44492         
44493         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44494             "up" : function(e){
44495                 this.inKeyMode = true;
44496                 this.selectPrev();
44497             },
44498
44499             "down" : function(e){
44500                 if(!this.isExpanded()){
44501                     this.onTriggerClick();
44502                 }else{
44503                     this.inKeyMode = true;
44504                     this.selectNext();
44505                 }
44506             },
44507
44508             "enter" : function(e){
44509                 this.collapse();
44510                 
44511                 if(this.fireEvent("specialkey", this, e)){
44512                     this.onViewClick(false);
44513                 }
44514                 
44515                 return true;
44516             },
44517
44518             "esc" : function(e){
44519                 this.collapse();
44520             },
44521
44522             "tab" : function(e){
44523                 this.collapse();
44524                 
44525                 if(this.fireEvent("specialkey", this, e)){
44526                     this.onViewClick(false);
44527                 }
44528                 
44529                 return true;
44530             },
44531
44532             scope : this,
44533
44534             doRelay : function(foo, bar, hname){
44535                 if(hname == 'down' || this.scope.isExpanded()){
44536                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44537                 }
44538                 return true;
44539             },
44540
44541             forceKeyDown: true
44542         });
44543         
44544         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44545         
44546     },
44547     
44548     initNumberEvent : function(e)
44549     {
44550         this.inputEl().on("keydown" , this.fireKey,  this);
44551         this.inputEl().on("focus", this.onFocus,  this);
44552         this.inputEl().on("blur", this.onBlur,  this);
44553         
44554         this.inputEl().relayEvent('keyup', this);
44555         
44556         if(this.indicator){
44557             this.indicator.addClass('invisible');
44558         }
44559  
44560         this.originalValue = this.getValue();
44561         
44562         if(this.validationEvent == 'keyup'){
44563             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44564             this.inputEl().on('keyup', this.filterValidation, this);
44565         }
44566         else if(this.validationEvent !== false){
44567             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44568         }
44569         
44570         if(this.selectOnFocus){
44571             this.on("focus", this.preFocus, this);
44572             
44573         }
44574         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44575             this.inputEl().on("keypress", this.filterKeys, this);
44576         } else {
44577             this.inputEl().relayEvent('keypress', this);
44578         }
44579         
44580         var allowed = "0123456789";
44581         
44582         if(this.allowDecimals){
44583             allowed += this.decimalSeparator;
44584         }
44585         
44586         if(this.allowNegative){
44587             allowed += "-";
44588         }
44589         
44590         if(this.thousandsDelimiter) {
44591             allowed += ",";
44592         }
44593         
44594         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44595         
44596         var keyPress = function(e){
44597             
44598             var k = e.getKey();
44599             
44600             var c = e.getCharCode();
44601             
44602             if(
44603                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44604                     allowed.indexOf(String.fromCharCode(c)) === -1
44605             ){
44606                 e.stopEvent();
44607                 return;
44608             }
44609             
44610             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44611                 return;
44612             }
44613             
44614             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44615                 e.stopEvent();
44616             }
44617         };
44618         
44619         this.inputEl().on("keypress", keyPress, this);
44620         
44621     },
44622     
44623     onTriggerClick : function(e)
44624     {   
44625         if(this.disabled){
44626             return;
44627         }
44628         
44629         this.page = 0;
44630         this.loadNext = false;
44631         
44632         if(this.isExpanded()){
44633             this.collapse();
44634             return;
44635         }
44636         
44637         this.hasFocus = true;
44638         
44639         if(this.triggerAction == 'all') {
44640             this.doQuery(this.allQuery, true);
44641             return;
44642         }
44643         
44644         this.doQuery(this.getRawValue());
44645     },
44646     
44647     getCurrency : function()
44648     {   
44649         var v = this.currencyEl().getValue();
44650         
44651         return v;
44652     },
44653     
44654     restrictHeight : function()
44655     {
44656         this.list.alignTo(this.currencyEl(), this.listAlign);
44657         this.list.alignTo(this.currencyEl(), this.listAlign);
44658     },
44659     
44660     onViewClick : function(view, doFocus, el, e)
44661     {
44662         var index = this.view.getSelectedIndexes()[0];
44663         
44664         var r = this.store.getAt(index);
44665         
44666         if(r){
44667             this.onSelect(r, index);
44668         }
44669     },
44670     
44671     onSelect : function(record, index){
44672         
44673         if(this.fireEvent('beforeselect', this, record, index) !== false){
44674         
44675             this.setFromCurrencyData(index > -1 ? record.data : false);
44676             
44677             this.collapse();
44678             
44679             this.fireEvent('select', this, record, index);
44680         }
44681     },
44682     
44683     setFromCurrencyData : function(o)
44684     {
44685         var currency = '';
44686         
44687         this.lastCurrency = o;
44688         
44689         if (this.currencyField) {
44690             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44691         } else {
44692             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44693         }
44694         
44695         this.lastSelectionText = currency;
44696         
44697         //setting default currency
44698         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44699             this.setCurrency(this.defaultCurrency);
44700             return;
44701         }
44702         
44703         this.setCurrency(currency);
44704     },
44705     
44706     setFromData : function(o)
44707     {
44708         var c = {};
44709         
44710         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44711         
44712         this.setFromCurrencyData(c);
44713         
44714         var value = '';
44715         
44716         if (this.name) {
44717             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44718         } else {
44719             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44720         }
44721         
44722         this.setValue(value);
44723         
44724     },
44725     
44726     setCurrency : function(v)
44727     {   
44728         this.currencyValue = v;
44729         
44730         if(this.rendered){
44731             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44732             this.validate();
44733         }
44734     },
44735     
44736     setValue : function(v)
44737     {
44738         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44739         
44740         this.value = v;
44741         
44742         if(this.rendered){
44743             
44744             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44745             
44746             this.inputEl().dom.value = (v == '') ? '' :
44747                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44748             
44749             if(!this.allowZero && v === '0') {
44750                 this.hiddenEl().dom.value = '';
44751                 this.inputEl().dom.value = '';
44752             }
44753             
44754             this.validate();
44755         }
44756     },
44757     
44758     getRawValue : function()
44759     {
44760         var v = this.inputEl().getValue();
44761         
44762         return v;
44763     },
44764     
44765     getValue : function()
44766     {
44767         return this.fixPrecision(this.parseValue(this.getRawValue()));
44768     },
44769     
44770     parseValue : function(value)
44771     {
44772         if(this.thousandsDelimiter) {
44773             value += "";
44774             r = new RegExp(",", "g");
44775             value = value.replace(r, "");
44776         }
44777         
44778         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44779         return isNaN(value) ? '' : value;
44780         
44781     },
44782     
44783     fixPrecision : function(value)
44784     {
44785         if(this.thousandsDelimiter) {
44786             value += "";
44787             r = new RegExp(",", "g");
44788             value = value.replace(r, "");
44789         }
44790         
44791         var nan = isNaN(value);
44792         
44793         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44794             return nan ? '' : value;
44795         }
44796         return parseFloat(value).toFixed(this.decimalPrecision);
44797     },
44798     
44799     decimalPrecisionFcn : function(v)
44800     {
44801         return Math.floor(v);
44802     },
44803     
44804     validateValue : function(value)
44805     {
44806         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44807             return false;
44808         }
44809         
44810         var num = this.parseValue(value);
44811         
44812         if(isNaN(num)){
44813             this.markInvalid(String.format(this.nanText, value));
44814             return false;
44815         }
44816         
44817         if(num < this.minValue){
44818             this.markInvalid(String.format(this.minText, this.minValue));
44819             return false;
44820         }
44821         
44822         if(num > this.maxValue){
44823             this.markInvalid(String.format(this.maxText, this.maxValue));
44824             return false;
44825         }
44826         
44827         return true;
44828     },
44829     
44830     validate : function()
44831     {
44832         if(this.disabled || this.allowBlank){
44833             this.markValid();
44834             return true;
44835         }
44836         
44837         var currency = this.getCurrency();
44838         
44839         if(this.validateValue(this.getRawValue()) && currency.length){
44840             this.markValid();
44841             return true;
44842         }
44843         
44844         this.markInvalid();
44845         return false;
44846     },
44847     
44848     getName: function()
44849     {
44850         return this.name;
44851     },
44852     
44853     beforeBlur : function()
44854     {
44855         if(!this.castInt){
44856             return;
44857         }
44858         
44859         var v = this.parseValue(this.getRawValue());
44860         
44861         if(v || v == 0){
44862             this.setValue(v);
44863         }
44864     },
44865     
44866     onBlur : function()
44867     {
44868         this.beforeBlur();
44869         
44870         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44871             //this.el.removeClass(this.focusClass);
44872         }
44873         
44874         this.hasFocus = false;
44875         
44876         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44877             this.validate();
44878         }
44879         
44880         var v = this.getValue();
44881         
44882         if(String(v) !== String(this.startValue)){
44883             this.fireEvent('change', this, v, this.startValue);
44884         }
44885         
44886         this.fireEvent("blur", this);
44887     },
44888     
44889     inputEl : function()
44890     {
44891         return this.el.select('.roo-money-amount-input', true).first();
44892     },
44893     
44894     currencyEl : function()
44895     {
44896         return this.el.select('.roo-money-currency-input', true).first();
44897     },
44898     
44899     hiddenEl : function()
44900     {
44901         return this.el.select('input.hidden-number-input',true).first();
44902     }
44903     
44904 });/**
44905  * @class Roo.bootstrap.BezierSignature
44906  * @extends Roo.bootstrap.Component
44907  * Bootstrap BezierSignature class
44908  * This script refer to:
44909  *    Title: Signature Pad
44910  *    Author: szimek
44911  *    Availability: https://github.com/szimek/signature_pad
44912  *
44913  * @constructor
44914  * Create a new BezierSignature
44915  * @param {Object} config The config object
44916  */
44917
44918 Roo.bootstrap.BezierSignature = function(config){
44919     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44920     this.addEvents({
44921         "resize" : true
44922     });
44923 };
44924
44925 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44926 {
44927      
44928     curve_data: [],
44929     
44930     is_empty: true,
44931     
44932     mouse_btn_down: true,
44933     
44934     /**
44935      * @cfg {int} canvas height
44936      */
44937     canvas_height: '200px',
44938     
44939     /**
44940      * @cfg {float|function} Radius of a single dot.
44941      */ 
44942     dot_size: false,
44943     
44944     /**
44945      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44946      */
44947     min_width: 0.5,
44948     
44949     /**
44950      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44951      */
44952     max_width: 2.5,
44953     
44954     /**
44955      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44956      */
44957     throttle: 16,
44958     
44959     /**
44960      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44961      */
44962     min_distance: 5,
44963     
44964     /**
44965      * @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.
44966      */
44967     bg_color: 'rgba(0, 0, 0, 0)',
44968     
44969     /**
44970      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44971      */
44972     dot_color: 'black',
44973     
44974     /**
44975      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44976      */ 
44977     velocity_filter_weight: 0.7,
44978     
44979     /**
44980      * @cfg {function} Callback when stroke begin. 
44981      */
44982     onBegin: false,
44983     
44984     /**
44985      * @cfg {function} Callback when stroke end.
44986      */
44987     onEnd: false,
44988     
44989     getAutoCreate : function()
44990     {
44991         var cls = 'roo-signature column';
44992         
44993         if(this.cls){
44994             cls += ' ' + this.cls;
44995         }
44996         
44997         var col_sizes = [
44998             'lg',
44999             'md',
45000             'sm',
45001             'xs'
45002         ];
45003         
45004         for(var i = 0; i < col_sizes.length; i++) {
45005             if(this[col_sizes[i]]) {
45006                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45007             }
45008         }
45009         
45010         var cfg = {
45011             tag: 'div',
45012             cls: cls,
45013             cn: [
45014                 {
45015                     tag: 'div',
45016                     cls: 'roo-signature-body',
45017                     cn: [
45018                         {
45019                             tag: 'canvas',
45020                             cls: 'roo-signature-body-canvas',
45021                             height: this.canvas_height,
45022                             width: this.canvas_width
45023                         }
45024                     ]
45025                 },
45026                 {
45027                     tag: 'input',
45028                     type: 'file',
45029                     style: 'display: none'
45030                 }
45031             ]
45032         };
45033         
45034         return cfg;
45035     },
45036     
45037     initEvents: function() 
45038     {
45039         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45040         
45041         var canvas = this.canvasEl();
45042         
45043         // mouse && touch event swapping...
45044         canvas.dom.style.touchAction = 'none';
45045         canvas.dom.style.msTouchAction = 'none';
45046         
45047         this.mouse_btn_down = false;
45048         canvas.on('mousedown', this._handleMouseDown, this);
45049         canvas.on('mousemove', this._handleMouseMove, this);
45050         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45051         
45052         if (window.PointerEvent) {
45053             canvas.on('pointerdown', this._handleMouseDown, this);
45054             canvas.on('pointermove', this._handleMouseMove, this);
45055             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45056         }
45057         
45058         if ('ontouchstart' in window) {
45059             canvas.on('touchstart', this._handleTouchStart, this);
45060             canvas.on('touchmove', this._handleTouchMove, this);
45061             canvas.on('touchend', this._handleTouchEnd, this);
45062         }
45063         
45064         Roo.EventManager.onWindowResize(this.resize, this, true);
45065         
45066         // file input event
45067         this.fileEl().on('change', this.uploadImage, this);
45068         
45069         this.clear();
45070         
45071         this.resize();
45072     },
45073     
45074     resize: function(){
45075         
45076         var canvas = this.canvasEl().dom;
45077         var ctx = this.canvasElCtx();
45078         var img_data = false;
45079         
45080         if(canvas.width > 0) {
45081             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45082         }
45083         // setting canvas width will clean img data
45084         canvas.width = 0;
45085         
45086         var style = window.getComputedStyle ? 
45087             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45088             
45089         var padding_left = parseInt(style.paddingLeft) || 0;
45090         var padding_right = parseInt(style.paddingRight) || 0;
45091         
45092         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45093         
45094         if(img_data) {
45095             ctx.putImageData(img_data, 0, 0);
45096         }
45097     },
45098     
45099     _handleMouseDown: function(e)
45100     {
45101         if (e.browserEvent.which === 1) {
45102             this.mouse_btn_down = true;
45103             this.strokeBegin(e);
45104         }
45105     },
45106     
45107     _handleMouseMove: function (e)
45108     {
45109         if (this.mouse_btn_down) {
45110             this.strokeMoveUpdate(e);
45111         }
45112     },
45113     
45114     _handleMouseUp: function (e)
45115     {
45116         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45117             this.mouse_btn_down = false;
45118             this.strokeEnd(e);
45119         }
45120     },
45121     
45122     _handleTouchStart: function (e) {
45123         
45124         e.preventDefault();
45125         if (e.browserEvent.targetTouches.length === 1) {
45126             // var touch = e.browserEvent.changedTouches[0];
45127             // this.strokeBegin(touch);
45128             
45129              this.strokeBegin(e); // assume e catching the correct xy...
45130         }
45131     },
45132     
45133     _handleTouchMove: function (e) {
45134         e.preventDefault();
45135         // var touch = event.targetTouches[0];
45136         // _this._strokeMoveUpdate(touch);
45137         this.strokeMoveUpdate(e);
45138     },
45139     
45140     _handleTouchEnd: function (e) {
45141         var wasCanvasTouched = e.target === this.canvasEl().dom;
45142         if (wasCanvasTouched) {
45143             e.preventDefault();
45144             // var touch = event.changedTouches[0];
45145             // _this._strokeEnd(touch);
45146             this.strokeEnd(e);
45147         }
45148     },
45149     
45150     reset: function () {
45151         this._lastPoints = [];
45152         this._lastVelocity = 0;
45153         this._lastWidth = (this.min_width + this.max_width) / 2;
45154         this.canvasElCtx().fillStyle = this.dot_color;
45155     },
45156     
45157     strokeMoveUpdate: function(e)
45158     {
45159         this.strokeUpdate(e);
45160         
45161         if (this.throttle) {
45162             this.throttleStroke(this.strokeUpdate, this.throttle);
45163         }
45164         else {
45165             this.strokeUpdate(e);
45166         }
45167     },
45168     
45169     strokeBegin: function(e)
45170     {
45171         var newPointGroup = {
45172             color: this.dot_color,
45173             points: []
45174         };
45175         
45176         if (typeof this.onBegin === 'function') {
45177             this.onBegin(e);
45178         }
45179         
45180         this.curve_data.push(newPointGroup);
45181         this.reset();
45182         this.strokeUpdate(e);
45183     },
45184     
45185     strokeUpdate: function(e)
45186     {
45187         var rect = this.canvasEl().dom.getBoundingClientRect();
45188         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45189         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45190         var lastPoints = lastPointGroup.points;
45191         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45192         var isLastPointTooClose = lastPoint
45193             ? point.distanceTo(lastPoint) <= this.min_distance
45194             : false;
45195         var color = lastPointGroup.color;
45196         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45197             var curve = this.addPoint(point);
45198             if (!lastPoint) {
45199                 this.drawDot({color: color, point: point});
45200             }
45201             else if (curve) {
45202                 this.drawCurve({color: color, curve: curve});
45203             }
45204             lastPoints.push({
45205                 time: point.time,
45206                 x: point.x,
45207                 y: point.y
45208             });
45209         }
45210     },
45211     
45212     strokeEnd: function(e)
45213     {
45214         this.strokeUpdate(e);
45215         if (typeof this.onEnd === 'function') {
45216             this.onEnd(e);
45217         }
45218     },
45219     
45220     addPoint:  function (point) {
45221         var _lastPoints = this._lastPoints;
45222         _lastPoints.push(point);
45223         if (_lastPoints.length > 2) {
45224             if (_lastPoints.length === 3) {
45225                 _lastPoints.unshift(_lastPoints[0]);
45226             }
45227             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45228             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45229             _lastPoints.shift();
45230             return curve;
45231         }
45232         return null;
45233     },
45234     
45235     calculateCurveWidths: function (startPoint, endPoint) {
45236         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45237             (1 - this.velocity_filter_weight) * this._lastVelocity;
45238
45239         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45240         var widths = {
45241             end: newWidth,
45242             start: this._lastWidth
45243         };
45244         
45245         this._lastVelocity = velocity;
45246         this._lastWidth = newWidth;
45247         return widths;
45248     },
45249     
45250     drawDot: function (_a) {
45251         var color = _a.color, point = _a.point;
45252         var ctx = this.canvasElCtx();
45253         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45254         ctx.beginPath();
45255         this.drawCurveSegment(point.x, point.y, width);
45256         ctx.closePath();
45257         ctx.fillStyle = color;
45258         ctx.fill();
45259     },
45260     
45261     drawCurve: function (_a) {
45262         var color = _a.color, curve = _a.curve;
45263         var ctx = this.canvasElCtx();
45264         var widthDelta = curve.endWidth - curve.startWidth;
45265         var drawSteps = Math.floor(curve.length()) * 2;
45266         ctx.beginPath();
45267         ctx.fillStyle = color;
45268         for (var i = 0; i < drawSteps; i += 1) {
45269         var t = i / drawSteps;
45270         var tt = t * t;
45271         var ttt = tt * t;
45272         var u = 1 - t;
45273         var uu = u * u;
45274         var uuu = uu * u;
45275         var x = uuu * curve.startPoint.x;
45276         x += 3 * uu * t * curve.control1.x;
45277         x += 3 * u * tt * curve.control2.x;
45278         x += ttt * curve.endPoint.x;
45279         var y = uuu * curve.startPoint.y;
45280         y += 3 * uu * t * curve.control1.y;
45281         y += 3 * u * tt * curve.control2.y;
45282         y += ttt * curve.endPoint.y;
45283         var width = curve.startWidth + ttt * widthDelta;
45284         this.drawCurveSegment(x, y, width);
45285         }
45286         ctx.closePath();
45287         ctx.fill();
45288     },
45289     
45290     drawCurveSegment: function (x, y, width) {
45291         var ctx = this.canvasElCtx();
45292         ctx.moveTo(x, y);
45293         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45294         this.is_empty = false;
45295     },
45296     
45297     clear: function()
45298     {
45299         var ctx = this.canvasElCtx();
45300         var canvas = this.canvasEl().dom;
45301         ctx.fillStyle = this.bg_color;
45302         ctx.clearRect(0, 0, canvas.width, canvas.height);
45303         ctx.fillRect(0, 0, canvas.width, canvas.height);
45304         this.curve_data = [];
45305         this.reset();
45306         this.is_empty = true;
45307     },
45308     
45309     fileEl: function()
45310     {
45311         return  this.el.select('input',true).first();
45312     },
45313     
45314     canvasEl: function()
45315     {
45316         return this.el.select('canvas',true).first();
45317     },
45318     
45319     canvasElCtx: function()
45320     {
45321         return this.el.select('canvas',true).first().dom.getContext('2d');
45322     },
45323     
45324     getImage: function(type)
45325     {
45326         if(this.is_empty) {
45327             return false;
45328         }
45329         
45330         // encryption ?
45331         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45332     },
45333     
45334     drawFromImage: function(img_src)
45335     {
45336         var img = new Image();
45337         
45338         img.onload = function(){
45339             this.canvasElCtx().drawImage(img, 0, 0);
45340         }.bind(this);
45341         
45342         img.src = img_src;
45343         
45344         this.is_empty = false;
45345     },
45346     
45347     selectImage: function()
45348     {
45349         this.fileEl().dom.click();
45350     },
45351     
45352     uploadImage: function(e)
45353     {
45354         var reader = new FileReader();
45355         
45356         reader.onload = function(e){
45357             var img = new Image();
45358             img.onload = function(){
45359                 this.reset();
45360                 this.canvasElCtx().drawImage(img, 0, 0);
45361             }.bind(this);
45362             img.src = e.target.result;
45363         }.bind(this);
45364         
45365         reader.readAsDataURL(e.target.files[0]);
45366     },
45367     
45368     // Bezier Point Constructor
45369     Point: (function () {
45370         function Point(x, y, time) {
45371             this.x = x;
45372             this.y = y;
45373             this.time = time || Date.now();
45374         }
45375         Point.prototype.distanceTo = function (start) {
45376             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45377         };
45378         Point.prototype.equals = function (other) {
45379             return this.x === other.x && this.y === other.y && this.time === other.time;
45380         };
45381         Point.prototype.velocityFrom = function (start) {
45382             return this.time !== start.time
45383             ? this.distanceTo(start) / (this.time - start.time)
45384             : 0;
45385         };
45386         return Point;
45387     }()),
45388     
45389     
45390     // Bezier Constructor
45391     Bezier: (function () {
45392         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45393             this.startPoint = startPoint;
45394             this.control2 = control2;
45395             this.control1 = control1;
45396             this.endPoint = endPoint;
45397             this.startWidth = startWidth;
45398             this.endWidth = endWidth;
45399         }
45400         Bezier.fromPoints = function (points, widths, scope) {
45401             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45402             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45403             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45404         };
45405         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45406             var dx1 = s1.x - s2.x;
45407             var dy1 = s1.y - s2.y;
45408             var dx2 = s2.x - s3.x;
45409             var dy2 = s2.y - s3.y;
45410             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45411             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45412             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45413             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45414             var dxm = m1.x - m2.x;
45415             var dym = m1.y - m2.y;
45416             var k = l2 / (l1 + l2);
45417             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45418             var tx = s2.x - cm.x;
45419             var ty = s2.y - cm.y;
45420             return {
45421                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45422                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45423             };
45424         };
45425         Bezier.prototype.length = function () {
45426             var steps = 10;
45427             var length = 0;
45428             var px;
45429             var py;
45430             for (var i = 0; i <= steps; i += 1) {
45431                 var t = i / steps;
45432                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45433                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45434                 if (i > 0) {
45435                     var xdiff = cx - px;
45436                     var ydiff = cy - py;
45437                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45438                 }
45439                 px = cx;
45440                 py = cy;
45441             }
45442             return length;
45443         };
45444         Bezier.prototype.point = function (t, start, c1, c2, end) {
45445             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45446             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45447             + (3.0 * c2 * (1.0 - t) * t * t)
45448             + (end * t * t * t);
45449         };
45450         return Bezier;
45451     }()),
45452     
45453     throttleStroke: function(fn, wait) {
45454       if (wait === void 0) { wait = 250; }
45455       var previous = 0;
45456       var timeout = null;
45457       var result;
45458       var storedContext;
45459       var storedArgs;
45460       var later = function () {
45461           previous = Date.now();
45462           timeout = null;
45463           result = fn.apply(storedContext, storedArgs);
45464           if (!timeout) {
45465               storedContext = null;
45466               storedArgs = [];
45467           }
45468       };
45469       return function wrapper() {
45470           var args = [];
45471           for (var _i = 0; _i < arguments.length; _i++) {
45472               args[_i] = arguments[_i];
45473           }
45474           var now = Date.now();
45475           var remaining = wait - (now - previous);
45476           storedContext = this;
45477           storedArgs = args;
45478           if (remaining <= 0 || remaining > wait) {
45479               if (timeout) {
45480                   clearTimeout(timeout);
45481                   timeout = null;
45482               }
45483               previous = now;
45484               result = fn.apply(storedContext, storedArgs);
45485               if (!timeout) {
45486                   storedContext = null;
45487                   storedArgs = [];
45488               }
45489           }
45490           else if (!timeout) {
45491               timeout = window.setTimeout(later, remaining);
45492           }
45493           return result;
45494       };
45495   }
45496   
45497 });
45498
45499  
45500
45501