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  * @children Roo.bootstrap.Component
1601  * Bootstrap Container class
1602  * @cfg {Boolean} jumbotron is it a jumbotron element
1603  * @cfg {String} html content of element
1604  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1605  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1606  * @cfg {String} header content of header (for panel)
1607  * @cfg {String} footer content of footer (for panel)
1608  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1609  * @cfg {String} tag (header|aside|section) type of HTML tag.
1610  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1611  * @cfg {String} fa font awesome icon
1612  * @cfg {String} icon (info-sign|check|...) glyphicon name
1613  * @cfg {Boolean} hidden (true|false) hide the element
1614  * @cfg {Boolean} expandable (true|false) default false
1615  * @cfg {Boolean} expanded (true|false) default true
1616  * @cfg {String} rheader contet on the right of header
1617  * @cfg {Boolean} clickable (true|false) default false
1618
1619  *     
1620  * @constructor
1621  * Create a new Container
1622  * @param {Object} config The config object
1623  */
1624
1625 Roo.bootstrap.Container = function(config){
1626     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1627     
1628     this.addEvents({
1629         // raw events
1630          /**
1631          * @event expand
1632          * After the panel has been expand
1633          * 
1634          * @param {Roo.bootstrap.Container} this
1635          */
1636         "expand" : true,
1637         /**
1638          * @event collapse
1639          * After the panel has been collapsed
1640          * 
1641          * @param {Roo.bootstrap.Container} this
1642          */
1643         "collapse" : true,
1644         /**
1645          * @event click
1646          * When a element is chick
1647          * @param {Roo.bootstrap.Container} this
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1655     
1656     jumbotron : false,
1657     well: '',
1658     panel : '',
1659     header: '',
1660     footer : '',
1661     sticky: '',
1662     tag : false,
1663     alert : false,
1664     fa: false,
1665     icon : false,
1666     expandable : false,
1667     rheader : '',
1668     expanded : true,
1669     clickable: false,
1670   
1671      
1672     getChildContainer : function() {
1673         
1674         if(!this.el){
1675             return false;
1676         }
1677         
1678         if (this.panel.length) {
1679             return this.el.select('.panel-body',true).first();
1680         }
1681         
1682         return this.el;
1683     },
1684     
1685     
1686     getAutoCreate : function(){
1687         
1688         var cfg = {
1689             tag : this.tag || 'div',
1690             html : '',
1691             cls : ''
1692         };
1693         if (this.jumbotron) {
1694             cfg.cls = 'jumbotron';
1695         }
1696         
1697         
1698         
1699         // - this is applied by the parent..
1700         //if (this.cls) {
1701         //    cfg.cls = this.cls + '';
1702         //}
1703         
1704         if (this.sticky.length) {
1705             
1706             var bd = Roo.get(document.body);
1707             if (!bd.hasClass('bootstrap-sticky')) {
1708                 bd.addClass('bootstrap-sticky');
1709                 Roo.select('html',true).setStyle('height', '100%');
1710             }
1711              
1712             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1713         }
1714         
1715         
1716         if (this.well.length) {
1717             switch (this.well) {
1718                 case 'lg':
1719                 case 'sm':
1720                     cfg.cls +=' well well-' +this.well;
1721                     break;
1722                 default:
1723                     cfg.cls +=' well';
1724                     break;
1725             }
1726         }
1727         
1728         if (this.hidden) {
1729             cfg.cls += ' hidden';
1730         }
1731         
1732         
1733         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1734             cfg.cls +=' alert alert-' + this.alert;
1735         }
1736         
1737         var body = cfg;
1738         
1739         if (this.panel.length) {
1740             cfg.cls += ' panel panel-' + this.panel;
1741             cfg.cn = [];
1742             if (this.header.length) {
1743                 
1744                 var h = [];
1745                 
1746                 if(this.expandable){
1747                     
1748                     cfg.cls = cfg.cls + ' expandable';
1749                     
1750                     h.push({
1751                         tag: 'i',
1752                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1753                     });
1754                     
1755                 }
1756                 
1757                 h.push(
1758                     {
1759                         tag: 'span',
1760                         cls : 'panel-title',
1761                         html : (this.expandable ? '&nbsp;' : '') + this.header
1762                     },
1763                     {
1764                         tag: 'span',
1765                         cls: 'panel-header-right',
1766                         html: this.rheader
1767                     }
1768                 );
1769                 
1770                 cfg.cn.push({
1771                     cls : 'panel-heading',
1772                     style : this.expandable ? 'cursor: pointer' : '',
1773                     cn : h
1774                 });
1775                 
1776             }
1777             
1778             body = false;
1779             cfg.cn.push({
1780                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1781                 html : this.html
1782             });
1783             
1784             
1785             if (this.footer.length) {
1786                 cfg.cn.push({
1787                     cls : 'panel-footer',
1788                     html : this.footer
1789                     
1790                 });
1791             }
1792             
1793         }
1794         
1795         if (body) {
1796             body.html = this.html || cfg.html;
1797             // prefix with the icons..
1798             if (this.fa) {
1799                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1800             }
1801             if (this.icon) {
1802                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1803             }
1804             
1805             
1806         }
1807         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1808             cfg.cls =  'container';
1809         }
1810         
1811         return cfg;
1812     },
1813     
1814     initEvents: function() 
1815     {
1816         if(this.expandable){
1817             var headerEl = this.headerEl();
1818         
1819             if(headerEl){
1820                 headerEl.on('click', this.onToggleClick, this);
1821             }
1822         }
1823         
1824         if(this.clickable){
1825             this.el.on('click', this.onClick, this);
1826         }
1827         
1828     },
1829     
1830     onToggleClick : function()
1831     {
1832         var headerEl = this.headerEl();
1833         
1834         if(!headerEl){
1835             return;
1836         }
1837         
1838         if(this.expanded){
1839             this.collapse();
1840             return;
1841         }
1842         
1843         this.expand();
1844     },
1845     
1846     expand : function()
1847     {
1848         if(this.fireEvent('expand', this)) {
1849             
1850             this.expanded = true;
1851             
1852             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1853             
1854             this.el.select('.panel-body',true).first().removeClass('hide');
1855             
1856             var toggleEl = this.toggleEl();
1857
1858             if(!toggleEl){
1859                 return;
1860             }
1861
1862             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1863         }
1864         
1865     },
1866     
1867     collapse : function()
1868     {
1869         if(this.fireEvent('collapse', this)) {
1870             
1871             this.expanded = false;
1872             
1873             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1874             this.el.select('.panel-body',true).first().addClass('hide');
1875         
1876             var toggleEl = this.toggleEl();
1877
1878             if(!toggleEl){
1879                 return;
1880             }
1881
1882             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1883         }
1884     },
1885     
1886     toggleEl : function()
1887     {
1888         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1889             return;
1890         }
1891         
1892         return this.el.select('.panel-heading .fa',true).first();
1893     },
1894     
1895     headerEl : function()
1896     {
1897         if(!this.el || !this.panel.length || !this.header.length){
1898             return;
1899         }
1900         
1901         return this.el.select('.panel-heading',true).first()
1902     },
1903     
1904     bodyEl : function()
1905     {
1906         if(!this.el || !this.panel.length){
1907             return;
1908         }
1909         
1910         return this.el.select('.panel-body',true).first()
1911     },
1912     
1913     titleEl : function()
1914     {
1915         if(!this.el || !this.panel.length || !this.header.length){
1916             return;
1917         }
1918         
1919         return this.el.select('.panel-title',true).first();
1920     },
1921     
1922     setTitle : function(v)
1923     {
1924         var titleEl = this.titleEl();
1925         
1926         if(!titleEl){
1927             return;
1928         }
1929         
1930         titleEl.dom.innerHTML = v;
1931     },
1932     
1933     getTitle : function()
1934     {
1935         
1936         var titleEl = this.titleEl();
1937         
1938         if(!titleEl){
1939             return '';
1940         }
1941         
1942         return titleEl.dom.innerHTML;
1943     },
1944     
1945     setRightTitle : function(v)
1946     {
1947         var t = this.el.select('.panel-header-right',true).first();
1948         
1949         if(!t){
1950             return;
1951         }
1952         
1953         t.dom.innerHTML = v;
1954     },
1955     
1956     onClick : function(e)
1957     {
1958         e.preventDefault();
1959         
1960         this.fireEvent('click', this, e);
1961     }
1962 });
1963
1964  /*
1965  *  - LGPL
1966  *
1967  *  This is BS4's Card element.. - similar to our containers probably..
1968  * 
1969  */
1970 /**
1971  * @class Roo.bootstrap.Card
1972  * @extends Roo.bootstrap.Component
1973  * @children Roo.bootstrap.Component
1974  * Bootstrap Card class
1975  *
1976  *
1977  * possible... may not be implemented..
1978  * @cfg {String} header_image  src url of image.
1979  * @cfg {String|Object} header
1980  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1981  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1982  * 
1983  * @cfg {String} title
1984  * @cfg {String} subtitle
1985  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1986  * @cfg {String} footer
1987  
1988  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1989  * 
1990  * @cfg {String} margin (0|1|2|3|4|5|auto)
1991  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1992  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1993  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1997  *
1998  * @cfg {String} padding (0|1|2|3|4|5)
1999  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2000  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2001  * @cfg {String} padding_left (0|1|2|3|4|5)
2002  * @cfg {String} padding_right (0|1|2|3|4|5)
2003  * @cfg {String} padding_x (0|1|2|3|4|5)
2004  * @cfg {String} padding_y (0|1|2|3|4|5)
2005  *
2006  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2008  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2009  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011  
2012  * @config {Boolean} dragable  if this card can be dragged.
2013  * @config {String} drag_group  group for drag
2014  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2015  * @config {String} drop_group  group for drag
2016  * 
2017  * @config {Boolean} collapsable can the body be collapsed.
2018  * @config {Boolean} collapsed is the body collapsed when rendered...
2019  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2020  * @config {Boolean} rotated is the body rotated when rendered...
2021  * 
2022  * @constructor
2023  * Create a new Container
2024  * @param {Object} config The config object
2025  */
2026
2027 Roo.bootstrap.Card = function(config){
2028     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2029     
2030     this.addEvents({
2031          // raw events
2032         /**
2033          * @event drop
2034          * When a element a card is dropped
2035          * @param {Roo.bootstrap.Card} this
2036          *
2037          * 
2038          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2039          * @param {String} position 'above' or 'below'
2040          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2041         
2042          */
2043         'drop' : true,
2044          /**
2045          * @event rotate
2046          * When a element a card is rotate
2047          * @param {Roo.bootstrap.Card} this
2048          * @param {Roo.Element} n the node being dropped?
2049          * @param {Boolean} rotate status
2050          */
2051         'rotate' : true,
2052         /**
2053          * @event cardover
2054          * When a card element is dragged over ready to drop (return false to block dropable)
2055          * @param {Roo.bootstrap.Card} this
2056          * @param {Object} data from dragdrop 
2057          */
2058          'cardover' : true
2059          
2060     });
2061 };
2062
2063
2064 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2065     
2066     
2067     weight : '',
2068     
2069     margin: '', /// may be better in component?
2070     margin_top: '', 
2071     margin_bottom: '', 
2072     margin_left: '',
2073     margin_right: '',
2074     margin_x: '',
2075     margin_y: '',
2076     
2077     padding : '',
2078     padding_top: '', 
2079     padding_bottom: '', 
2080     padding_left: '',
2081     padding_right: '',
2082     padding_x: '',
2083     padding_y: '',
2084     
2085     display: '', 
2086     display_xs: '', 
2087     display_sm: '', 
2088     display_lg: '',
2089     display_xl: '',
2090  
2091     header_image  : '',
2092     header : '',
2093     header_size : 0,
2094     title : '',
2095     subtitle : '',
2096     html : '',
2097     footer: '',
2098
2099     collapsable : false,
2100     collapsed : false,
2101     rotateable : false,
2102     rotated : false,
2103     
2104     dragable : false,
2105     drag_group : false,
2106     dropable : false,
2107     drop_group : false,
2108     childContainer : false,
2109     dropEl : false, /// the dom placeholde element that indicates drop location.
2110     containerEl: false, // body container
2111     bodyEl: false, // card-body
2112     headerContainerEl : false, //
2113     headerEl : false,
2114     header_imageEl : false,
2115     
2116     
2117     layoutCls : function()
2118     {
2119         var cls = '';
2120         var t = this;
2121         Roo.log(this.margin_bottom.length);
2122         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2123             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2124             
2125             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2126                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2127             }
2128             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2130             }
2131         });
2132         
2133         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2134             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2135                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2136             }
2137         });
2138         
2139         // more generic support?
2140         if (this.hidden) {
2141             cls += ' d-none';
2142         }
2143         
2144         return cls;
2145     },
2146  
2147        // Roo.log("Call onRender: " + this.xtype);
2148         /*  We are looking at something like this.
2149 <div class="card">
2150     <img src="..." class="card-img-top" alt="...">
2151     <div class="card-body">
2152         <h5 class="card-title">Card title</h5>
2153          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2154
2155         >> this bit is really the body...
2156         <div> << we will ad dthis in hopefully it will not break shit.
2157         
2158         ** card text does not actually have any styling...
2159         
2160             <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>
2161         
2162         </div> <<
2163           <a href="#" class="card-link">Card link</a>
2164           
2165     </div>
2166     <div class="card-footer">
2167         <small class="text-muted">Last updated 3 mins ago</small>
2168     </div>
2169 </div>
2170          */
2171     getAutoCreate : function(){
2172         
2173         var cfg = {
2174             tag : 'div',
2175             cls : 'card',
2176             cn : [ ]
2177         };
2178         
2179         if (this.weight.length && this.weight != 'light') {
2180             cfg.cls += ' text-white';
2181         } else {
2182             cfg.cls += ' text-dark'; // need as it's nested..
2183         }
2184         if (this.weight.length) {
2185             cfg.cls += ' bg-' + this.weight;
2186         }
2187         
2188         cfg.cls += ' ' + this.layoutCls(); 
2189         
2190         var hdr = false;
2191         var hdr_ctr = false;
2192         if (this.header.length) {
2193             hdr = {
2194                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2195                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196                 cn : []
2197             };
2198             cfg.cn.push(hdr);
2199             hdr_ctr = hdr;
2200         } else {
2201             hdr = {
2202                 tag : 'div',
2203                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2204                 cn : []
2205             };
2206             cfg.cn.push(hdr);
2207             hdr_ctr = hdr;
2208         }
2209         if (this.collapsable) {
2210             hdr_ctr = {
2211             tag : 'a',
2212             cls : 'd-block user-select-none',
2213             cn: [
2214                     {
2215                         tag: 'i',
2216                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2217                     }
2218                    
2219                 ]
2220             };
2221             hdr.cn.push(hdr_ctr);
2222         }
2223         
2224         hdr_ctr.cn.push(        {
2225             tag: 'span',
2226             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2227             html : this.header
2228         });
2229         
2230         
2231         if (this.header_image.length) {
2232             cfg.cn.push({
2233                 tag : 'img',
2234                 cls : 'card-img-top',
2235                 src: this.header_image // escape?
2236             });
2237         } else {
2238             cfg.cn.push({
2239                     tag : 'div',
2240                     cls : 'card-img-top d-none' 
2241                 });
2242         }
2243             
2244         var body = {
2245             tag : 'div',
2246             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2247             cn : []
2248         };
2249         var obody = body;
2250         if (this.collapsable || this.rotateable) {
2251             obody = {
2252                 tag: 'div',
2253                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2254                 cn : [  body ]
2255             };
2256         }
2257         
2258         cfg.cn.push(obody);
2259         
2260         if (this.title.length) {
2261             body.cn.push({
2262                 tag : 'div',
2263                 cls : 'card-title',
2264                 src: this.title // escape?
2265             });
2266         }  
2267         
2268         if (this.subtitle.length) {
2269             body.cn.push({
2270                 tag : 'div',
2271                 cls : 'card-title',
2272                 src: this.subtitle // escape?
2273             });
2274         }
2275         
2276         body.cn.push({
2277             tag : 'div',
2278             cls : 'roo-card-body-ctr'
2279         });
2280         
2281         if (this.html.length) {
2282             body.cn.push({
2283                 tag: 'div',
2284                 html : this.html
2285             });
2286         }
2287         // fixme ? handle objects?
2288         
2289         if (this.footer.length) {
2290            
2291             cfg.cn.push({
2292                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2293                 html : this.footer
2294             });
2295             
2296         } else {
2297             cfg.cn.push({cls : 'card-footer d-none'});
2298         }
2299         
2300         // footer...
2301         
2302         return cfg;
2303     },
2304     
2305     
2306     getCardHeader : function()
2307     {
2308         var  ret = this.el.select('.card-header',true).first();
2309         if (ret.hasClass('d-none')) {
2310             ret.removeClass('d-none');
2311         }
2312         
2313         return ret;
2314     },
2315     getCardFooter : function()
2316     {
2317         var  ret = this.el.select('.card-footer',true).first();
2318         if (ret.hasClass('d-none')) {
2319             ret.removeClass('d-none');
2320         }
2321         
2322         return ret;
2323     },
2324     getCardImageTop : function()
2325     {
2326         var  ret = this.header_imageEl;
2327         if (ret.hasClass('d-none')) {
2328             ret.removeClass('d-none');
2329         }
2330             
2331         return ret;
2332     },
2333     
2334     getChildContainer : function()
2335     {
2336         
2337         if(!this.el){
2338             return false;
2339         }
2340         return this.el.select('.roo-card-body-ctr',true).first();    
2341     },
2342     
2343     initEvents: function() 
2344     {
2345         this.bodyEl = this.el.select('.card-body',true).first(); 
2346         this.containerEl = this.getChildContainer();
2347         if(this.dragable){
2348             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2349                     containerScroll: true,
2350                     ddGroup: this.drag_group || 'default_card_drag_group'
2351             });
2352             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2353         }
2354         if (this.dropable) {
2355             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2356                 containerScroll: true,
2357                 ddGroup: this.drop_group || 'default_card_drag_group'
2358             });
2359             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2360             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2361             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2362             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2363             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2364         }
2365         
2366         if (this.collapsable) {
2367             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2368         }
2369         if (this.rotateable) {
2370             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2371         }
2372         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2373          
2374         this.footerEl = this.el.select('.card-footer',true).first();
2375         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2376         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2377         this.headerEl = this.el.select('.card-header',true).first();
2378         
2379         if (this.rotated) {
2380             this.el.addClass('roo-card-rotated');
2381             this.fireEvent('rotate', this, true);
2382         }
2383         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2384         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2385         
2386     },
2387     getDragData : function(e)
2388     {
2389         var target = this.getEl();
2390         if (target) {
2391             //this.handleSelection(e);
2392             
2393             var dragData = {
2394                 source: this,
2395                 copy: false,
2396                 nodes: this.getEl(),
2397                 records: []
2398             };
2399             
2400             
2401             dragData.ddel = target.dom ;    // the div element
2402             Roo.log(target.getWidth( ));
2403             dragData.ddel.style.width = target.getWidth() + 'px';
2404             
2405             return dragData;
2406         }
2407         return false;
2408     },
2409     /**
2410     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2411     *    whole Element becomes the target, and this causes the drop gesture to append.
2412     *
2413     *    Returns an object:
2414     *     {
2415            
2416            position : 'below' or 'above'
2417            card  : relateive to card OBJECT (or true for no cards listed)
2418            items_n : relative to nth item in list
2419            card_n : relative to  nth card in list
2420     }
2421     *
2422     *    
2423     */
2424     getTargetFromEvent : function(e, dragged_card_el)
2425     {
2426         var target = e.getTarget();
2427         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2428             target = target.parentNode;
2429         }
2430         
2431         var ret = {
2432             position: '',
2433             cards : [],
2434             card_n : -1,
2435             items_n : -1,
2436             card : false 
2437         };
2438         
2439         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2440         // see if target is one of the 'cards'...
2441         
2442         
2443         //Roo.log(this.items.length);
2444         var pos = false;
2445         
2446         var last_card_n = 0;
2447         var cards_len  = 0;
2448         for (var i = 0;i< this.items.length;i++) {
2449             
2450             if (!this.items[i].el.hasClass('card')) {
2451                  continue;
2452             }
2453             pos = this.getDropPoint(e, this.items[i].el.dom);
2454             
2455             cards_len = ret.cards.length;
2456             //Roo.log(this.items[i].el.dom.id);
2457             ret.cards.push(this.items[i]);
2458             last_card_n  = i;
2459             if (ret.card_n < 0 && pos == 'above') {
2460                 ret.position = cards_len > 0 ? 'below' : pos;
2461                 ret.items_n = i > 0 ? i - 1 : 0;
2462                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2463                 ret.card = ret.cards[ret.card_n];
2464             }
2465         }
2466         if (!ret.cards.length) {
2467             ret.card = true;
2468             ret.position = 'below';
2469             ret.items_n;
2470             return ret;
2471         }
2472         // could not find a card.. stick it at the end..
2473         if (ret.card_n < 0) {
2474             ret.card_n = last_card_n;
2475             ret.card = ret.cards[last_card_n];
2476             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2477             ret.position = 'below';
2478         }
2479         
2480         if (this.items[ret.items_n].el == dragged_card_el) {
2481             return false;
2482         }
2483         
2484         if (ret.position == 'below') {
2485             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2486             
2487             if (card_after  && card_after.el == dragged_card_el) {
2488                 return false;
2489             }
2490             return ret;
2491         }
2492         
2493         // its's after ..
2494         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2495         
2496         if (card_before  && card_before.el == dragged_card_el) {
2497             return false;
2498         }
2499         
2500         return ret;
2501     },
2502     
2503     onNodeEnter : function(n, dd, e, data){
2504         return false;
2505     },
2506     onNodeOver : function(n, dd, e, data)
2507     {
2508        
2509         var target_info = this.getTargetFromEvent(e,data.source.el);
2510         if (target_info === false) {
2511             this.dropPlaceHolder('hide');
2512             return false;
2513         }
2514         Roo.log(['getTargetFromEvent', target_info ]);
2515         
2516         
2517         if (this.fireEvent('cardover', this, [ data ]) === false) {
2518             return false;
2519         }
2520         
2521         this.dropPlaceHolder('show', target_info,data);
2522         
2523         return false; 
2524     },
2525     onNodeOut : function(n, dd, e, data){
2526         this.dropPlaceHolder('hide');
2527      
2528     },
2529     onNodeDrop : function(n, dd, e, data)
2530     {
2531         
2532         // call drop - return false if
2533         
2534         // this could actually fail - if the Network drops..
2535         // we will ignore this at present..- client should probably reload
2536         // the whole set of cards if stuff like that fails.
2537         
2538         
2539         var info = this.getTargetFromEvent(e,data.source.el);
2540         if (info === false) {
2541             return false;
2542         }
2543         this.dropPlaceHolder('hide');
2544   
2545           
2546     
2547         this.acceptCard(data.source, info.position, info.card, info.items_n);
2548         return true;
2549          
2550     },
2551     firstChildCard : function()
2552     {
2553         for (var i = 0;i< this.items.length;i++) {
2554             
2555             if (!this.items[i].el.hasClass('card')) {
2556                  continue;
2557             }
2558             return this.items[i];
2559         }
2560         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2561     },
2562     /**
2563      * accept card
2564      *
2565      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2566      */
2567     acceptCard : function(move_card,  position, next_to_card )
2568     {
2569         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2570             return false;
2571         }
2572         
2573         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2574         
2575         move_card.parent().removeCard(move_card);
2576         
2577         
2578         var dom = move_card.el.dom;
2579         dom.style.width = ''; // clear with - which is set by drag.
2580         
2581         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2582             var cardel = next_to_card.el.dom;
2583             
2584             if (position == 'above' ) {
2585                 cardel.parentNode.insertBefore(dom, cardel);
2586             } else if (cardel.nextSibling) {
2587                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2588             } else {
2589                 cardel.parentNode.append(dom);
2590             }
2591         } else {
2592             // card container???
2593             this.containerEl.dom.append(dom);
2594         }
2595         
2596         //FIXME HANDLE card = true 
2597         
2598         // add this to the correct place in items.
2599         
2600         // remove Card from items.
2601         
2602        
2603         if (this.items.length) {
2604             var nitems = [];
2605             //Roo.log([info.items_n, info.position, this.items.length]);
2606             for (var i =0; i < this.items.length; i++) {
2607                 if (i == to_items_n && position == 'above') {
2608                     nitems.push(move_card);
2609                 }
2610                 nitems.push(this.items[i]);
2611                 if (i == to_items_n && position == 'below') {
2612                     nitems.push(move_card);
2613                 }
2614             }
2615             this.items = nitems;
2616             Roo.log(this.items);
2617         } else {
2618             this.items.push(move_card);
2619         }
2620         
2621         move_card.parentId = this.id;
2622         
2623         return true;
2624         
2625         
2626     },
2627     removeCard : function(c)
2628     {
2629         this.items = this.items.filter(function(e) { return e != c });
2630  
2631         var dom = c.el.dom;
2632         dom.parentNode.removeChild(dom);
2633         dom.style.width = ''; // clear with - which is set by drag.
2634         c.parentId = false;
2635         
2636     },
2637     
2638     /**    Decide whether to drop above or below a View node. */
2639     getDropPoint : function(e, n, dd)
2640     {
2641         if (dd) {
2642              return false;
2643         }
2644         if (n == this.containerEl.dom) {
2645             return "above";
2646         }
2647         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2648         var c = t + (b - t) / 2;
2649         var y = Roo.lib.Event.getPageY(e);
2650         if(y <= c) {
2651             return "above";
2652         }else{
2653             return "below";
2654         }
2655     },
2656     onToggleCollapse : function(e)
2657         {
2658         if (this.collapsed) {
2659             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2660             this.collapsableEl.addClass('show');
2661             this.collapsed = false;
2662             return;
2663         }
2664         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2665         this.collapsableEl.removeClass('show');
2666         this.collapsed = true;
2667         
2668     
2669     },
2670     
2671     onToggleRotate : function(e)
2672     {
2673         this.collapsableEl.removeClass('show');
2674         this.footerEl.removeClass('d-none');
2675         this.el.removeClass('roo-card-rotated');
2676         this.el.removeClass('d-none');
2677         if (this.rotated) {
2678             
2679             this.collapsableEl.addClass('show');
2680             this.rotated = false;
2681             this.fireEvent('rotate', this, this.rotated);
2682             return;
2683         }
2684         this.el.addClass('roo-card-rotated');
2685         this.footerEl.addClass('d-none');
2686         this.el.select('.roo-collapsable').removeClass('show');
2687         
2688         this.rotated = true;
2689         this.fireEvent('rotate', this, this.rotated);
2690     
2691     },
2692     
2693     dropPlaceHolder: function (action, info, data)
2694     {
2695         if (this.dropEl === false) {
2696             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2697             cls : 'd-none'
2698             },true);
2699         }
2700         this.dropEl.removeClass(['d-none', 'd-block']);        
2701         if (action == 'hide') {
2702             
2703             this.dropEl.addClass('d-none');
2704             return;
2705         }
2706         // FIXME - info.card == true!!!
2707         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2708         
2709         if (info.card !== true) {
2710             var cardel = info.card.el.dom;
2711             
2712             if (info.position == 'above') {
2713                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2714             } else if (cardel.nextSibling) {
2715                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2716             } else {
2717                 cardel.parentNode.append(this.dropEl.dom);
2718             }
2719         } else {
2720             // card container???
2721             this.containerEl.dom.append(this.dropEl.dom);
2722         }
2723         
2724         this.dropEl.addClass('d-block roo-card-dropzone');
2725         
2726         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2727         
2728         
2729     
2730     
2731     
2732     },
2733     setHeaderText: function(html)
2734     {
2735         this.header = html;
2736         if (this.headerContainerEl) {
2737             this.headerContainerEl.dom.innerHTML = html;
2738         }
2739     },
2740     onHeaderImageLoad : function(ev, he)
2741     {
2742         if (!this.header_image_fit_square) {
2743             return;
2744         }
2745         
2746         var hw = he.naturalHeight / he.naturalWidth;
2747         // wide image = < 0
2748         // tall image = > 1
2749         //var w = he.dom.naturalWidth;
2750         var ww = he.width;
2751         he.style.left =  0;
2752         he.style.position =  'relative';
2753         if (hw > 1) {
2754             var nw = (ww * (1/hw));
2755             Roo.get(he).setSize( ww * (1/hw),  ww);
2756             he.style.left =  ((ww - nw)/ 2) + 'px';
2757             he.style.position =  'relative';
2758         }
2759
2760     }
2761
2762     
2763 });
2764
2765 /*
2766  * - LGPL
2767  *
2768  * Card header - holder for the card header elements.
2769  * 
2770  */
2771
2772 /**
2773  * @class Roo.bootstrap.CardHeader
2774  * @extends Roo.bootstrap.Element
2775  * Bootstrap CardHeader class
2776  * @constructor
2777  * Create a new Card Header - that you can embed children into
2778  * @param {Object} config The config object
2779  */
2780
2781 Roo.bootstrap.CardHeader = function(config){
2782     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2783 };
2784
2785 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2786     
2787     
2788     container_method : 'getCardHeader' 
2789     
2790      
2791     
2792     
2793    
2794 });
2795
2796  
2797
2798  /*
2799  * - LGPL
2800  *
2801  * Card footer - holder for the card footer elements.
2802  * 
2803  */
2804
2805 /**
2806  * @class Roo.bootstrap.CardFooter
2807  * @extends Roo.bootstrap.Element
2808  * Bootstrap CardFooter class
2809  * @constructor
2810  * Create a new Card Footer - that you can embed children into
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.CardFooter = function(config){
2815     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2816 };
2817
2818 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2819     
2820     
2821     container_method : 'getCardFooter' 
2822     
2823      
2824     
2825     
2826    
2827 });
2828
2829  
2830
2831  /*
2832  * - LGPL
2833  *
2834  * Card header - holder for the card header elements.
2835  * 
2836  */
2837
2838 /**
2839  * @class Roo.bootstrap.CardImageTop
2840  * @extends Roo.bootstrap.Element
2841  * Bootstrap CardImageTop class
2842  * @constructor
2843  * Create a new Card Image Top container
2844  * @param {Object} config The config object
2845  */
2846
2847 Roo.bootstrap.CardImageTop = function(config){
2848     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2849 };
2850
2851 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2852     
2853    
2854     container_method : 'getCardImageTop' 
2855     
2856      
2857     
2858    
2859 });
2860
2861  
2862
2863  
2864 /*
2865 * Licence: LGPL
2866 */
2867
2868 /**
2869  * @class Roo.bootstrap.ButtonUploader
2870  * @extends Roo.bootstrap.Button
2871  * Bootstrap Button Uploader class - it's a button which when you add files to it
2872  *
2873  * 
2874  * @cfg {Number} errorTimeout default 3000
2875  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2876  * @cfg {Array}  html The button text.
2877  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2878  *
2879  * @constructor
2880  * Create a new CardUploader
2881  * @param {Object} config The config object
2882  */
2883
2884 Roo.bootstrap.ButtonUploader = function(config){
2885     
2886  
2887     
2888     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2889     
2890      
2891      this.addEvents({
2892          // raw events
2893         /**
2894          * @event beforeselect
2895          * When button is pressed, before show upload files dialog is shown
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          *
2898          */
2899         'beforeselect' : true,
2900          /**
2901          * @event fired when files have been selected, 
2902          * When a the download link is clicked
2903          * @param {Roo.bootstrap.UploaderButton} this
2904          * @param {Array} Array of files that have been uploaded
2905          */
2906         'uploaded' : true
2907         
2908     });
2909 };
2910  
2911 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2912     
2913      
2914     errorTimeout : 3000,
2915      
2916     images : false,
2917    
2918     fileCollection : false,
2919     allowBlank : true,
2920     
2921     multiple : true,
2922     
2923     getAutoCreate : function()
2924     {
2925         var im = {
2926             tag: 'input',
2927             type : 'file',
2928             cls : 'd-none  roo-card-upload-selector' 
2929           
2930         };
2931         if (this.multiple) {
2932             im.multiple = 'multiple';
2933         }
2934         
2935         return  {
2936             cls :'div' ,
2937             cn : [
2938                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2939                 im
2940
2941             ]
2942         };
2943            
2944          
2945     },
2946      
2947    
2948     initEvents : function()
2949     {
2950         
2951         Roo.bootstrap.Button.prototype.initEvents.call(this);
2952         
2953         
2954         
2955         
2956         
2957         this.urlAPI = (window.createObjectURL && window) || 
2958                                 (window.URL && URL.revokeObjectURL && URL) || 
2959                                 (window.webkitURL && webkitURL);
2960                         
2961          
2962          
2963          
2964         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2965         
2966         this.selectorEl.on('change', this.onFileSelected, this);
2967          
2968          
2969        
2970     },
2971     
2972    
2973     onClick : function(e)
2974     {
2975         e.preventDefault();
2976         
2977         if ( this.fireEvent('beforeselect', this) === false) {
2978             return;
2979         }
2980          
2981         this.selectorEl.dom.click();
2982          
2983     },
2984     
2985     onFileSelected : function(e)
2986     {
2987         e.preventDefault();
2988         
2989         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2990             return;
2991         }
2992         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2993         this.selectorEl.dom.value  = '';// hopefully reset..
2994         
2995         this.fireEvent('uploaded', this,  files );
2996         
2997     },
2998     
2999        
3000    
3001     
3002     /**
3003      * addCard - add an Attachment to the uploader
3004      * @param data - the data about the image to upload
3005      *
3006      * {
3007           id : 123
3008           title : "Title of file",
3009           is_uploaded : false,
3010           src : "http://.....",
3011           srcfile : { the File upload object },
3012           mimetype : file.type,
3013           preview : false,
3014           is_deleted : 0
3015           .. any other data...
3016         }
3017      *
3018      * 
3019     */
3020      
3021     reset: function()
3022     {
3023          
3024          this.selectorEl
3025     } 
3026     
3027     
3028     
3029     
3030 });
3031  /*
3032  * - LGPL
3033  *
3034  * image
3035  * 
3036  */
3037
3038
3039 /**
3040  * @class Roo.bootstrap.Img
3041  * @extends Roo.bootstrap.Component
3042  * Bootstrap Img class
3043  * @cfg {Boolean} imgResponsive false | true
3044  * @cfg {String} border rounded | circle | thumbnail
3045  * @cfg {String} src image source
3046  * @cfg {String} alt image alternative text
3047  * @cfg {String} href a tag href
3048  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3049  * @cfg {String} xsUrl xs image source
3050  * @cfg {String} smUrl sm image source
3051  * @cfg {String} mdUrl md image source
3052  * @cfg {String} lgUrl lg image source
3053  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3054  * 
3055  * @constructor
3056  * Create a new Input
3057  * @param {Object} config The config object
3058  */
3059
3060 Roo.bootstrap.Img = function(config){
3061     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3062     
3063     this.addEvents({
3064         // img events
3065         /**
3066          * @event click
3067          * The img click event for the img.
3068          * @param {Roo.EventObject} e
3069          */
3070         "click" : true,
3071         /**
3072          * @event load
3073          * The when any image loads
3074          * @param {Roo.EventObject} e
3075          */
3076         "load" : true
3077     });
3078 };
3079
3080 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3081     
3082     imgResponsive: true,
3083     border: '',
3084     src: 'about:blank',
3085     href: false,
3086     target: false,
3087     xsUrl: '',
3088     smUrl: '',
3089     mdUrl: '',
3090     lgUrl: '',
3091     backgroundContain : false,
3092
3093     getAutoCreate : function()
3094     {   
3095         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3096             return this.createSingleImg();
3097         }
3098         
3099         var cfg = {
3100             tag: 'div',
3101             cls: 'roo-image-responsive-group',
3102             cn: []
3103         };
3104         var _this = this;
3105         
3106         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3107             
3108             if(!_this[size + 'Url']){
3109                 return;
3110             }
3111             
3112             var img = {
3113                 tag: 'img',
3114                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3115                 html: _this.html || cfg.html,
3116                 src: _this[size + 'Url']
3117             };
3118             
3119             img.cls += ' roo-image-responsive-' + size;
3120             
3121             var s = ['xs', 'sm', 'md', 'lg'];
3122             
3123             s.splice(s.indexOf(size), 1);
3124             
3125             Roo.each(s, function(ss){
3126                 img.cls += ' hidden-' + ss;
3127             });
3128             
3129             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3130                 cfg.cls += ' img-' + _this.border;
3131             }
3132             
3133             if(_this.alt){
3134                 cfg.alt = _this.alt;
3135             }
3136             
3137             if(_this.href){
3138                 var a = {
3139                     tag: 'a',
3140                     href: _this.href,
3141                     cn: [
3142                         img
3143                     ]
3144                 };
3145
3146                 if(this.target){
3147                     a.target = _this.target;
3148                 }
3149             }
3150             
3151             cfg.cn.push((_this.href) ? a : img);
3152             
3153         });
3154         
3155         return cfg;
3156     },
3157     
3158     createSingleImg : function()
3159     {
3160         var cfg = {
3161             tag: 'img',
3162             cls: (this.imgResponsive) ? 'img-responsive' : '',
3163             html : null,
3164             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3165         };
3166         
3167         if (this.backgroundContain) {
3168             cfg.cls += ' background-contain';
3169         }
3170         
3171         cfg.html = this.html || cfg.html;
3172         
3173         if (this.backgroundContain) {
3174             cfg.style="background-image: url(" + this.src + ')';
3175         } else {
3176             cfg.src = this.src || cfg.src;
3177         }
3178         
3179         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3180             cfg.cls += ' img-' + this.border;
3181         }
3182         
3183         if(this.alt){
3184             cfg.alt = this.alt;
3185         }
3186         
3187         if(this.href){
3188             var a = {
3189                 tag: 'a',
3190                 href: this.href,
3191                 cn: [
3192                     cfg
3193                 ]
3194             };
3195             
3196             if(this.target){
3197                 a.target = this.target;
3198             }
3199             
3200         }
3201         
3202         return (this.href) ? a : cfg;
3203     },
3204     
3205     initEvents: function() 
3206     {
3207         if(!this.href){
3208             this.el.on('click', this.onClick, this);
3209         }
3210         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3211             this.el.on('load', this.onImageLoad, this);
3212         } else {
3213             // not sure if this works.. not tested
3214             this.el.select('img', true).on('load', this.onImageLoad, this);
3215         }
3216         
3217     },
3218     
3219     onClick : function(e)
3220     {
3221         Roo.log('img onclick');
3222         this.fireEvent('click', this, e);
3223     },
3224     onImageLoad: function(e)
3225     {
3226         Roo.log('img load');
3227         this.fireEvent('load', this, e);
3228     },
3229     
3230     /**
3231      * Sets the url of the image - used to update it
3232      * @param {String} url the url of the image
3233      */
3234     
3235     setSrc : function(url)
3236     {
3237         this.src =  url;
3238         
3239         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3240             if (this.backgroundContain) {
3241                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3242             } else {
3243                 this.el.dom.src =  url;
3244             }
3245             return;
3246         }
3247         
3248         this.el.select('img', true).first().dom.src =  url;
3249     }
3250     
3251     
3252    
3253 });
3254
3255  /*
3256  * - LGPL
3257  *
3258  * image
3259  * 
3260  */
3261
3262
3263 /**
3264  * @class Roo.bootstrap.Link
3265  * @extends Roo.bootstrap.Component
3266  * Bootstrap Link Class
3267  * @cfg {String} alt image alternative text
3268  * @cfg {String} href a tag href
3269  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3270  * @cfg {String} html the content of the link.
3271  * @cfg {String} anchor name for the anchor link
3272  * @cfg {String} fa - favicon
3273
3274  * @cfg {Boolean} preventDefault (true | false) default false
3275
3276  * 
3277  * @constructor
3278  * Create a new Input
3279  * @param {Object} config The config object
3280  */
3281
3282 Roo.bootstrap.Link = function(config){
3283     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3284     
3285     this.addEvents({
3286         // img events
3287         /**
3288          * @event click
3289          * The img click event for the img.
3290          * @param {Roo.EventObject} e
3291          */
3292         "click" : true
3293     });
3294 };
3295
3296 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3297     
3298     href: false,
3299     target: false,
3300     preventDefault: false,
3301     anchor : false,
3302     alt : false,
3303     fa: false,
3304
3305
3306     getAutoCreate : function()
3307     {
3308         var html = this.html || '';
3309         
3310         if (this.fa !== false) {
3311             html = '<i class="fa fa-' + this.fa + '"></i>';
3312         }
3313         var cfg = {
3314             tag: 'a'
3315         };
3316         // anchor's do not require html/href...
3317         if (this.anchor === false) {
3318             cfg.html = html;
3319             cfg.href = this.href || '#';
3320         } else {
3321             cfg.name = this.anchor;
3322             if (this.html !== false || this.fa !== false) {
3323                 cfg.html = html;
3324             }
3325             if (this.href !== false) {
3326                 cfg.href = this.href;
3327             }
3328         }
3329         
3330         if(this.alt !== false){
3331             cfg.alt = this.alt;
3332         }
3333         
3334         
3335         if(this.target !== false) {
3336             cfg.target = this.target;
3337         }
3338         
3339         return cfg;
3340     },
3341     
3342     initEvents: function() {
3343         
3344         if(!this.href || this.preventDefault){
3345             this.el.on('click', this.onClick, this);
3346         }
3347     },
3348     
3349     onClick : function(e)
3350     {
3351         if(this.preventDefault){
3352             e.preventDefault();
3353         }
3354         //Roo.log('img onclick');
3355         this.fireEvent('click', this, e);
3356     }
3357    
3358 });
3359
3360  /*
3361  * - LGPL
3362  *
3363  * header
3364  * 
3365  */
3366
3367 /**
3368  * @class Roo.bootstrap.Header
3369  * @extends Roo.bootstrap.Component
3370  * Bootstrap Header class
3371  * @cfg {String} html content of header
3372  * @cfg {Number} level (1|2|3|4|5|6) default 1
3373  * 
3374  * @constructor
3375  * Create a new Header
3376  * @param {Object} config The config object
3377  */
3378
3379
3380 Roo.bootstrap.Header  = function(config){
3381     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3382 };
3383
3384 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3385     
3386     //href : false,
3387     html : false,
3388     level : 1,
3389     
3390     
3391     
3392     getAutoCreate : function(){
3393         
3394         
3395         
3396         var cfg = {
3397             tag: 'h' + (1 *this.level),
3398             html: this.html || ''
3399         } ;
3400         
3401         return cfg;
3402     }
3403    
3404 });
3405
3406  
3407
3408  /*
3409  * Based on:
3410  * Ext JS Library 1.1.1
3411  * Copyright(c) 2006-2007, Ext JS, LLC.
3412  *
3413  * Originally Released Under LGPL - original licence link has changed is not relivant.
3414  *
3415  * Fork - LGPL
3416  * <script type="text/javascript">
3417  */
3418  
3419 /**
3420  * @class Roo.bootstrap.MenuMgr
3421  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3422  * @singleton
3423  */
3424 Roo.bootstrap.MenuMgr = function(){
3425    var menus, active, groups = {}, attached = false, lastShow = new Date();
3426
3427    // private - called when first menu is created
3428    function init(){
3429        menus = {};
3430        active = new Roo.util.MixedCollection();
3431        Roo.get(document).addKeyListener(27, function(){
3432            if(active.length > 0){
3433                hideAll();
3434            }
3435        });
3436    }
3437
3438    // private
3439    function hideAll(){
3440        if(active && active.length > 0){
3441            var c = active.clone();
3442            c.each(function(m){
3443                m.hide();
3444            });
3445        }
3446    }
3447
3448    // private
3449    function onHide(m){
3450        active.remove(m);
3451        if(active.length < 1){
3452            Roo.get(document).un("mouseup", onMouseDown);
3453             
3454            attached = false;
3455        }
3456    }
3457
3458    // private
3459    function onShow(m){
3460        var last = active.last();
3461        lastShow = new Date();
3462        active.add(m);
3463        if(!attached){
3464           Roo.get(document).on("mouseup", onMouseDown);
3465            
3466            attached = true;
3467        }
3468        if(m.parentMenu){
3469           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3470           m.parentMenu.activeChild = m;
3471        }else if(last && last.isVisible()){
3472           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3473        }
3474    }
3475
3476    // private
3477    function onBeforeHide(m){
3478        if(m.activeChild){
3479            m.activeChild.hide();
3480        }
3481        if(m.autoHideTimer){
3482            clearTimeout(m.autoHideTimer);
3483            delete m.autoHideTimer;
3484        }
3485    }
3486
3487    // private
3488    function onBeforeShow(m){
3489        var pm = m.parentMenu;
3490        if(!pm && !m.allowOtherMenus){
3491            hideAll();
3492        }else if(pm && pm.activeChild && active != m){
3493            pm.activeChild.hide();
3494        }
3495    }
3496
3497    // private this should really trigger on mouseup..
3498    function onMouseDown(e){
3499         Roo.log("on Mouse Up");
3500         
3501         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3502             Roo.log("MenuManager hideAll");
3503             hideAll();
3504             e.stopEvent();
3505         }
3506         
3507         
3508    }
3509
3510    // private
3511    function onBeforeCheck(mi, state){
3512        if(state){
3513            var g = groups[mi.group];
3514            for(var i = 0, l = g.length; i < l; i++){
3515                if(g[i] != mi){
3516                    g[i].setChecked(false);
3517                }
3518            }
3519        }
3520    }
3521
3522    return {
3523
3524        /**
3525         * Hides all menus that are currently visible
3526         */
3527        hideAll : function(){
3528             hideAll();  
3529        },
3530
3531        // private
3532        register : function(menu){
3533            if(!menus){
3534                init();
3535            }
3536            menus[menu.id] = menu;
3537            menu.on("beforehide", onBeforeHide);
3538            menu.on("hide", onHide);
3539            menu.on("beforeshow", onBeforeShow);
3540            menu.on("show", onShow);
3541            var g = menu.group;
3542            if(g && menu.events["checkchange"]){
3543                if(!groups[g]){
3544                    groups[g] = [];
3545                }
3546                groups[g].push(menu);
3547                menu.on("checkchange", onCheck);
3548            }
3549        },
3550
3551         /**
3552          * Returns a {@link Roo.menu.Menu} object
3553          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3554          * be used to generate and return a new Menu instance.
3555          */
3556        get : function(menu){
3557            if(typeof menu == "string"){ // menu id
3558                return menus[menu];
3559            }else if(menu.events){  // menu instance
3560                return menu;
3561            }
3562            /*else if(typeof menu.length == 'number'){ // array of menu items?
3563                return new Roo.bootstrap.Menu({items:menu});
3564            }else{ // otherwise, must be a config
3565                return new Roo.bootstrap.Menu(menu);
3566            }
3567            */
3568            return false;
3569        },
3570
3571        // private
3572        unregister : function(menu){
3573            delete menus[menu.id];
3574            menu.un("beforehide", onBeforeHide);
3575            menu.un("hide", onHide);
3576            menu.un("beforeshow", onBeforeShow);
3577            menu.un("show", onShow);
3578            var g = menu.group;
3579            if(g && menu.events["checkchange"]){
3580                groups[g].remove(menu);
3581                menu.un("checkchange", onCheck);
3582            }
3583        },
3584
3585        // private
3586        registerCheckable : function(menuItem){
3587            var g = menuItem.group;
3588            if(g){
3589                if(!groups[g]){
3590                    groups[g] = [];
3591                }
3592                groups[g].push(menuItem);
3593                menuItem.on("beforecheckchange", onBeforeCheck);
3594            }
3595        },
3596
3597        // private
3598        unregisterCheckable : function(menuItem){
3599            var g = menuItem.group;
3600            if(g){
3601                groups[g].remove(menuItem);
3602                menuItem.un("beforecheckchange", onBeforeCheck);
3603            }
3604        }
3605    };
3606 }();/*
3607  * - LGPL
3608  *
3609  * menu
3610  * 
3611  */
3612
3613 /**
3614  * @class Roo.bootstrap.Menu
3615  * @extends Roo.bootstrap.Component
3616  * Bootstrap Menu class - container for MenuItems
3617  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3618  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3619  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3620  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3621   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3622   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3623  
3624  * @constructor
3625  * Create a new Menu
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.Menu = function(config){
3631     
3632     if (config.type == 'treeview') {
3633         // normally menu's are drawn attached to the document to handle layering etc..
3634         // however treeview (used by the docs menu is drawn into the parent element)
3635         this.container_method = 'getChildContainer'; 
3636     }
3637     
3638     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3639     if (this.registerMenu && this.type != 'treeview')  {
3640         Roo.bootstrap.MenuMgr.register(this);
3641     }
3642     
3643     
3644     this.addEvents({
3645         /**
3646          * @event beforeshow
3647          * Fires before this menu is displayed (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforeshow : true,
3651         /**
3652          * @event beforehide
3653          * Fires before this menu is hidden (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforehide : true,
3657         /**
3658          * @event show
3659          * Fires after this menu is displayed
3660          * @param {Roo.menu.Menu} this
3661          */
3662         show : true,
3663         /**
3664          * @event hide
3665          * Fires after this menu is hidden
3666          * @param {Roo.menu.Menu} this
3667          */
3668         hide : true,
3669         /**
3670          * @event click
3671          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3672          * @param {Roo.menu.Menu} this
3673          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3674          * @param {Roo.EventObject} e
3675          */
3676         click : true,
3677         /**
3678          * @event mouseover
3679          * Fires when the mouse is hovering over this menu
3680          * @param {Roo.menu.Menu} this
3681          * @param {Roo.EventObject} e
3682          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3683          */
3684         mouseover : true,
3685         /**
3686          * @event mouseout
3687          * Fires when the mouse exits this menu
3688          * @param {Roo.menu.Menu} this
3689          * @param {Roo.EventObject} e
3690          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691          */
3692         mouseout : true,
3693         /**
3694          * @event itemclick
3695          * Fires when a menu item contained in this menu is clicked
3696          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3697          * @param {Roo.EventObject} e
3698          */
3699         itemclick: true
3700     });
3701     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3702 };
3703
3704 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3705     
3706    /// html : false,
3707    
3708     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3709     type: false,
3710     /**
3711      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3712      */
3713     registerMenu : true,
3714     
3715     menuItems :false, // stores the menu items..
3716     
3717     hidden:true,
3718         
3719     parentMenu : false,
3720     
3721     stopEvent : true,
3722     
3723     isLink : false,
3724     
3725     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3726     
3727     hideTrigger : false,
3728     
3729     align : 'tl-bl?',
3730     
3731     
3732     getChildContainer : function() {
3733         return this.el;  
3734     },
3735     
3736     getAutoCreate : function(){
3737          
3738         //if (['right'].indexOf(this.align)!==-1) {
3739         //    cfg.cn[1].cls += ' pull-right'
3740         //}
3741          
3742         var cfg = {
3743             tag : 'ul',
3744             cls : 'dropdown-menu shadow' ,
3745             style : 'z-index:1000'
3746             
3747         };
3748         
3749         if (this.type === 'submenu') {
3750             cfg.cls = 'submenu active';
3751         }
3752         if (this.type === 'treeview') {
3753             cfg.cls = 'treeview-menu';
3754         }
3755         
3756         return cfg;
3757     },
3758     initEvents : function() {
3759         
3760        // Roo.log("ADD event");
3761        // Roo.log(this.triggerEl.dom);
3762         if (this.triggerEl) {
3763             
3764             this.triggerEl.on('click', this.onTriggerClick, this);
3765             
3766             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3767             
3768             if (!this.hideTrigger) {
3769                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3770                     // dropdown toggle on the 'a' in BS4?
3771                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3772                 } else {
3773                     this.triggerEl.addClass('dropdown-toggle');
3774                 }
3775             }
3776         }
3777         
3778         if (Roo.isTouch) {
3779             this.el.on('touchstart'  , this.onTouch, this);
3780         }
3781         this.el.on('click' , this.onClick, this);
3782
3783         this.el.on("mouseover", this.onMouseOver, this);
3784         this.el.on("mouseout", this.onMouseOut, this);
3785         
3786     },
3787     
3788     findTargetItem : function(e)
3789     {
3790         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3791         if(!t){
3792             return false;
3793         }
3794         //Roo.log(t);         Roo.log(t.id);
3795         if(t && t.id){
3796             //Roo.log(this.menuitems);
3797             return this.menuitems.get(t.id);
3798             
3799             //return this.items.get(t.menuItemId);
3800         }
3801         
3802         return false;
3803     },
3804     
3805     onTouch : function(e) 
3806     {
3807         Roo.log("menu.onTouch");
3808         //e.stopEvent(); this make the user popdown broken
3809         this.onClick(e);
3810     },
3811     
3812     onClick : function(e)
3813     {
3814         Roo.log("menu.onClick");
3815         
3816         var t = this.findTargetItem(e);
3817         if(!t || t.isContainer){
3818             return;
3819         }
3820         Roo.log(e);
3821         /*
3822         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3823             if(t == this.activeItem && t.shouldDeactivate(e)){
3824                 this.activeItem.deactivate();
3825                 delete this.activeItem;
3826                 return;
3827             }
3828             if(t.canActivate){
3829                 this.setActiveItem(t, true);
3830             }
3831             return;
3832             
3833             
3834         }
3835         */
3836        
3837         Roo.log('pass click event');
3838         
3839         t.onClick(e);
3840         
3841         this.fireEvent("click", this, t, e);
3842         
3843         var _this = this;
3844         
3845         if(!t.href.length || t.href == '#'){
3846             (function() { _this.hide(); }).defer(100);
3847         }
3848         
3849     },
3850     
3851     onMouseOver : function(e){
3852         var t  = this.findTargetItem(e);
3853         //Roo.log(t);
3854         //if(t){
3855         //    if(t.canActivate && !t.disabled){
3856         //        this.setActiveItem(t, true);
3857         //    }
3858         //}
3859         
3860         this.fireEvent("mouseover", this, e, t);
3861     },
3862     isVisible : function(){
3863         return !this.hidden;
3864     },
3865     onMouseOut : function(e){
3866         var t  = this.findTargetItem(e);
3867         
3868         //if(t ){
3869         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3870         //        this.activeItem.deactivate();
3871         //        delete this.activeItem;
3872         //    }
3873         //}
3874         this.fireEvent("mouseout", this, e, t);
3875     },
3876     
3877     
3878     /**
3879      * Displays this menu relative to another element
3880      * @param {String/HTMLElement/Roo.Element} element The element to align to
3881      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3882      * the element (defaults to this.defaultAlign)
3883      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3884      */
3885     show : function(el, pos, parentMenu)
3886     {
3887         if (false === this.fireEvent("beforeshow", this)) {
3888             Roo.log("show canceled");
3889             return;
3890         }
3891         this.parentMenu = parentMenu;
3892         if(!this.el){
3893             this.render();
3894         }
3895         this.el.addClass('show'); // show otherwise we do not know how big we are..
3896          
3897         var xy = this.el.getAlignToXY(el, pos);
3898         
3899         // bl-tl << left align  below
3900         // tl-bl << left align 
3901         
3902         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3903             // if it goes to far to the right.. -> align left.
3904             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3905         }
3906         if(xy[0] < 0){
3907             // was left align - go right?
3908             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3909         }
3910         
3911         // goes down the bottom
3912         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3913            xy[1]  < 0 ){
3914             var a = this.align.replace('?', '').split('-');
3915             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3916             
3917         }
3918         
3919         this.showAt(  xy , parentMenu, false);
3920     },
3921      /**
3922      * Displays this menu at a specific xy position
3923      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3924      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3925      */
3926     showAt : function(xy, parentMenu, /* private: */_e){
3927         this.parentMenu = parentMenu;
3928         if(!this.el){
3929             this.render();
3930         }
3931         if(_e !== false){
3932             this.fireEvent("beforeshow", this);
3933             //xy = this.el.adjustForConstraints(xy);
3934         }
3935         
3936         //this.el.show();
3937         this.hideMenuItems();
3938         this.hidden = false;
3939         if (this.triggerEl) {
3940             this.triggerEl.addClass('open');
3941         }
3942         
3943         this.el.addClass('show');
3944         
3945         
3946         
3947         // reassign x when hitting right
3948         
3949         // reassign y when hitting bottom
3950         
3951         // but the list may align on trigger left or trigger top... should it be a properity?
3952         
3953         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3954             this.el.setXY(xy);
3955         }
3956         
3957         this.focus();
3958         this.fireEvent("show", this);
3959     },
3960     
3961     focus : function(){
3962         return;
3963         if(!this.hidden){
3964             this.doFocus.defer(50, this);
3965         }
3966     },
3967
3968     doFocus : function(){
3969         if(!this.hidden){
3970             this.focusEl.focus();
3971         }
3972     },
3973
3974     /**
3975      * Hides this menu and optionally all parent menus
3976      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3977      */
3978     hide : function(deep)
3979     {
3980         if (false === this.fireEvent("beforehide", this)) {
3981             Roo.log("hide canceled");
3982             return;
3983         }
3984         this.hideMenuItems();
3985         if(this.el && this.isVisible()){
3986            
3987             if(this.activeItem){
3988                 this.activeItem.deactivate();
3989                 this.activeItem = null;
3990             }
3991             if (this.triggerEl) {
3992                 this.triggerEl.removeClass('open');
3993             }
3994             
3995             this.el.removeClass('show');
3996             this.hidden = true;
3997             this.fireEvent("hide", this);
3998         }
3999         if(deep === true && this.parentMenu){
4000             this.parentMenu.hide(true);
4001         }
4002     },
4003     
4004     onTriggerClick : function(e)
4005     {
4006         Roo.log('trigger click');
4007         
4008         var target = e.getTarget();
4009         
4010         Roo.log(target.nodeName.toLowerCase());
4011         
4012         if(target.nodeName.toLowerCase() === 'i'){
4013             e.preventDefault();
4014         }
4015         
4016     },
4017     
4018     onTriggerPress  : function(e)
4019     {
4020         Roo.log('trigger press');
4021         //Roo.log(e.getTarget());
4022        // Roo.log(this.triggerEl.dom);
4023        
4024         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4025         var pel = Roo.get(e.getTarget());
4026         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4027             Roo.log('is treeview or dropdown?');
4028             return;
4029         }
4030         
4031         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4032             return;
4033         }
4034         
4035         if (this.isVisible()) {
4036             Roo.log('hide');
4037             this.hide();
4038         } else {
4039             Roo.log('show');
4040             
4041             this.show(this.triggerEl, this.align, false);
4042         }
4043         
4044         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4045             e.stopEvent();
4046         }
4047         
4048     },
4049        
4050     
4051     hideMenuItems : function()
4052     {
4053         Roo.log("hide Menu Items");
4054         if (!this.el) { 
4055             return;
4056         }
4057         
4058         this.el.select('.open',true).each(function(aa) {
4059             
4060             aa.removeClass('open');
4061          
4062         });
4063     },
4064     addxtypeChild : function (tree, cntr) {
4065         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4066           
4067         this.menuitems.add(comp);
4068         return comp;
4069
4070     },
4071     getEl : function()
4072     {
4073         Roo.log(this.el);
4074         return this.el;
4075     },
4076     
4077     clear : function()
4078     {
4079         this.getEl().dom.innerHTML = '';
4080         this.menuitems.clear();
4081     }
4082 });
4083
4084  
4085  /*
4086  * - LGPL
4087  *
4088  * menu item
4089  * 
4090  */
4091
4092
4093 /**
4094  * @class Roo.bootstrap.MenuItem
4095  * @extends Roo.bootstrap.Component
4096  * Bootstrap MenuItem class
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.MenuItem = function(config){
4113     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.MenuItem} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         Roo.log('item on click ');
4200         
4201         if(this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  /*
4217  * - LGPL
4218  *
4219  * menu separator
4220  * 
4221  */
4222
4223
4224 /**
4225  * @class Roo.bootstrap.MenuSeparator
4226  * @extends Roo.bootstrap.Component
4227  * Bootstrap MenuSeparator class
4228  * 
4229  * @constructor
4230  * Create a new MenuItem
4231  * @param {Object} config The config object
4232  */
4233
4234
4235 Roo.bootstrap.MenuSeparator = function(config){
4236     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4237 };
4238
4239 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4240     
4241     getAutoCreate : function(){
4242         var cfg = {
4243             cls: 'divider',
4244             tag : 'li'
4245         };
4246         
4247         return cfg;
4248     }
4249    
4250 });
4251
4252  
4253
4254  
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @builder-top
4263  * @parent none
4264  * @children Roo.bootstrap.Component
4265  * Bootstrap Modal class
4266  * @cfg {String} title Title of dialog
4267  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4269  * @cfg {Boolean} specificTitle default false
4270  * @cfg {Array} buttons Array of buttons or standard button set..
4271  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272  * @cfg {Boolean} animate default true
4273  * @cfg {Boolean} allow_close default true
4274  * @cfg {Boolean} fitwindow default false
4275  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278  * @cfg {String} size (sm|lg|xl) default empty
4279  * @cfg {Number} max_width set the max width of modal
4280  * @cfg {Boolean} editableTitle can the title be edited
4281
4282  *
4283  *
4284  * @constructor
4285  * Create a new Modal Dialog
4286  * @param {Object} config The config object
4287  */
4288
4289 Roo.bootstrap.Modal = function(config){
4290     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event btnclick
4295          * The raw btnclick event for the button
4296          * @param {Roo.EventObject} e
4297          */
4298         "btnclick" : true,
4299         /**
4300          * @event resize
4301          * Fire when dialog resize
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} e
4304          */
4305         "resize" : true,
4306         /**
4307          * @event titlechanged
4308          * Fire when the editable title has been changed
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} value
4311          */
4312         "titlechanged" : true 
4313         
4314     });
4315     this.buttons = this.buttons || [];
4316
4317     if (this.tmpl) {
4318         this.tmpl = Roo.factory(this.tmpl);
4319     }
4320
4321 };
4322
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4324
4325     title : 'test dialog',
4326
4327     buttons : false,
4328
4329     // set on load...
4330
4331     html: false,
4332
4333     tmp: false,
4334
4335     specificTitle: false,
4336
4337     buttonPosition: 'right',
4338
4339     allow_close : true,
4340
4341     animate : true,
4342
4343     fitwindow: false,
4344     
4345      // private
4346     dialogEl: false,
4347     bodyEl:  false,
4348     footerEl:  false,
4349     titleEl:  false,
4350     closeEl:  false,
4351
4352     size: '',
4353     
4354     max_width: 0,
4355     
4356     max_height: 0,
4357     
4358     fit_content: false,
4359     editableTitle  : false,
4360
4361     onRender : function(ct, position)
4362     {
4363         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364
4365         if(!this.el){
4366             var cfg = Roo.apply({},  this.getAutoCreate());
4367             cfg.id = Roo.id();
4368             //if(!cfg.name){
4369             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370             //}
4371             //if (!cfg.name.length) {
4372             //    delete cfg.name;
4373            // }
4374             if (this.cls) {
4375                 cfg.cls += ' ' + this.cls;
4376             }
4377             if (this.style) {
4378                 cfg.style = this.style;
4379             }
4380             this.el = Roo.get(document.body).createChild(cfg, position);
4381         }
4382         //var type = this.el.dom.type;
4383
4384
4385         if(this.tabIndex !== undefined){
4386             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387         }
4388
4389         this.dialogEl = this.el.select('.modal-dialog',true).first();
4390         this.bodyEl = this.el.select('.modal-body',true).first();
4391         this.closeEl = this.el.select('.modal-header .close', true).first();
4392         this.headerEl = this.el.select('.modal-header',true).first();
4393         this.titleEl = this.el.select('.modal-title',true).first();
4394         this.footerEl = this.el.select('.modal-footer',true).first();
4395
4396         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397         
4398         //this.el.addClass("x-dlg-modal");
4399
4400         if (this.buttons.length) {
4401             Roo.each(this.buttons, function(bb) {
4402                 var b = Roo.apply({}, bb);
4403                 b.xns = b.xns || Roo.bootstrap;
4404                 b.xtype = b.xtype || 'Button';
4405                 if (typeof(b.listeners) == 'undefined') {
4406                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4407                 }
4408
4409                 var btn = Roo.factory(b);
4410
4411                 btn.render(this.getButtonContainer());
4412
4413             },this);
4414         }
4415         // render the children.
4416         var nitems = [];
4417
4418         if(typeof(this.items) != 'undefined'){
4419             var items = this.items;
4420             delete this.items;
4421
4422             for(var i =0;i < items.length;i++) {
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630     },
4631
4632     show : function() {
4633
4634         if (!this.rendered) {
4635             this.render();
4636         }
4637         this.toggleHeaderInput(false);
4638         //this.el.setStyle('display', 'block');
4639         this.el.removeClass('hideing');
4640         this.el.dom.style.display='block';
4641         
4642         Roo.get(document.body).addClass('modal-open');
4643  
4644         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4645             
4646             (function(){
4647                 this.el.addClass('show');
4648                 this.el.addClass('in');
4649             }).defer(50, this);
4650         }else{
4651             this.el.addClass('show');
4652             this.el.addClass('in');
4653         }
4654
4655         // not sure how we can show data in here..
4656         //if (this.tmpl) {
4657         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658         //}
4659
4660         Roo.get(document.body).addClass("x-body-masked");
4661         
4662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4663         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664         this.maskEl.dom.style.display = 'block';
4665         this.maskEl.addClass('show');
4666         
4667         
4668         this.resize();
4669         
4670         this.fireEvent('show', this);
4671
4672         // set zindex here - otherwise it appears to be ignored...
4673         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674
4675         (function () {
4676             this.items.forEach( function(e) {
4677                 e.layout ? e.layout() : false;
4678
4679             });
4680         }).defer(100,this);
4681
4682     },
4683     hide : function()
4684     {
4685         if(this.fireEvent("beforehide", this) !== false){
4686             
4687             this.maskEl.removeClass('show');
4688             
4689             this.maskEl.dom.style.display = '';
4690             Roo.get(document.body).removeClass("x-body-masked");
4691             this.el.removeClass('in');
4692             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4693
4694             if(this.animate){ // why
4695                 this.el.addClass('hideing');
4696                 this.el.removeClass('show');
4697                 (function(){
4698                     if (!this.el.hasClass('hideing')) {
4699                         return; // it's been shown again...
4700                     }
4701                     
4702                     this.el.dom.style.display='';
4703
4704                     Roo.get(document.body).removeClass('modal-open');
4705                     this.el.removeClass('hideing');
4706                 }).defer(150,this);
4707                 
4708             }else{
4709                 this.el.removeClass('show');
4710                 this.el.dom.style.display='';
4711                 Roo.get(document.body).removeClass('modal-open');
4712
4713             }
4714             this.fireEvent('hide', this);
4715         }
4716     },
4717     isVisible : function()
4718     {
4719         
4720         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721         
4722     },
4723
4724     addButton : function(str, cb)
4725     {
4726
4727
4728         var b = Roo.apply({}, { html : str } );
4729         b.xns = b.xns || Roo.bootstrap;
4730         b.xtype = b.xtype || 'Button';
4731         if (typeof(b.listeners) == 'undefined') {
4732             b.listeners = { click : cb.createDelegate(this)  };
4733         }
4734
4735         var btn = Roo.factory(b);
4736
4737         btn.render(this.getButtonContainer());
4738
4739         return btn;
4740
4741     },
4742
4743     setDefaultButton : function(btn)
4744     {
4745         //this.el.select('.modal-footer').()
4746     },
4747
4748     resizeTo: function(w,h)
4749     {
4750         this.dialogEl.setWidth(w);
4751         
4752         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4753
4754         this.bodyEl.setHeight(h - diff);
4755         
4756         this.fireEvent('resize', this);
4757     },
4758     
4759     setContentSize  : function(w, h)
4760     {
4761
4762     },
4763     onButtonClick: function(btn,e)
4764     {
4765         //Roo.log([a,b,c]);
4766         this.fireEvent('btnclick', btn.name, e);
4767     },
4768      /**
4769      * Set the title of the Dialog
4770      * @param {String} str new Title
4771      */
4772     setTitle: function(str) {
4773         this.titleEl.dom.innerHTML = str;
4774         this.title = str;
4775     },
4776     /**
4777      * Set the body of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setBody: function(str) {
4781         this.bodyEl.dom.innerHTML = str;
4782     },
4783     /**
4784      * Set the body of the Dialog using the template
4785      * @param {Obj} data - apply this data to the template and replace the body contents.
4786      */
4787     applyBody: function(obj)
4788     {
4789         if (!this.tmpl) {
4790             Roo.log("Error - using apply Body without a template");
4791             //code
4792         }
4793         this.tmpl.overwrite(this.bodyEl, obj);
4794     },
4795     
4796     getChildHeight : function(child_nodes)
4797     {
4798         if(
4799             !child_nodes ||
4800             child_nodes.length == 0
4801         ) {
4802             return 0;
4803         }
4804         
4805         var child_height = 0;
4806         
4807         for(var i = 0; i < child_nodes.length; i++) {
4808             
4809             /*
4810             * for modal with tabs...
4811             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4812                 
4813                 var layout_childs = child_nodes[i].childNodes;
4814                 
4815                 for(var j = 0; j < layout_childs.length; j++) {
4816                     
4817                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4818                         
4819                         var layout_body_childs = layout_childs[j].childNodes;
4820                         
4821                         for(var k = 0; k < layout_body_childs.length; k++) {
4822                             
4823                             if(layout_body_childs[k].classList.contains('navbar')) {
4824                                 child_height += layout_body_childs[k].offsetHeight;
4825                                 continue;
4826                             }
4827                             
4828                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4829                                 
4830                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4831                                 
4832                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4833                                     
4834                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4836                                         continue;
4837                                     }
4838                                     
4839                                 }
4840                                 
4841                             }
4842                             
4843                         }
4844                     }
4845                 }
4846                 continue;
4847             }
4848             */
4849             
4850             child_height += child_nodes[i].offsetHeight;
4851             // Roo.log(child_nodes[i].offsetHeight);
4852         }
4853         
4854         return child_height;
4855     },
4856     toggleHeaderInput : function(is_edit)
4857     {
4858         if (!this.editableTitle) {
4859             return; // not editable.
4860         }
4861         if (is_edit && this.is_header_editing) {
4862             return; // already editing..
4863         }
4864         if (is_edit) {
4865     
4866             this.headerEditEl.dom.value = this.title;
4867             this.headerEditEl.removeClass('d-none');
4868             this.headerEditEl.dom.focus();
4869             this.titleEl.addClass('d-none');
4870             
4871             this.is_header_editing = true;
4872             return
4873         }
4874         // flip back to not editing.
4875         this.title = this.headerEditEl.dom.value;
4876         this.headerEditEl.addClass('d-none');
4877         this.titleEl.removeClass('d-none');
4878         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879         this.is_header_editing = false;
4880         this.fireEvent('titlechanged', this, this.title);
4881     
4882             
4883         
4884     }
4885
4886 });
4887
4888
4889 Roo.apply(Roo.bootstrap.Modal,  {
4890     /**
4891          * Button config that displays a single OK button
4892          * @type Object
4893          */
4894         OK :  [{
4895             name : 'ok',
4896             weight : 'primary',
4897             html : 'OK'
4898         }],
4899         /**
4900          * Button config that displays Yes and No buttons
4901          * @type Object
4902          */
4903         YESNO : [
4904             {
4905                 name  : 'no',
4906                 html : 'No'
4907             },
4908             {
4909                 name  :'yes',
4910                 weight : 'primary',
4911                 html : 'Yes'
4912             }
4913         ],
4914
4915         /**
4916          * Button config that displays OK and Cancel buttons
4917          * @type Object
4918          */
4919         OKCANCEL : [
4920             {
4921                name : 'cancel',
4922                 html : 'Cancel'
4923             },
4924             {
4925                 name : 'ok',
4926                 weight : 'primary',
4927                 html : 'OK'
4928             }
4929         ],
4930         /**
4931          * Button config that displays Yes, No and Cancel buttons
4932          * @type Object
4933          */
4934         YESNOCANCEL : [
4935             {
4936                 name : 'yes',
4937                 weight : 'primary',
4938                 html : 'Yes'
4939             },
4940             {
4941                 name : 'no',
4942                 html : 'No'
4943             },
4944             {
4945                 name : 'cancel',
4946                 html : 'Cancel'
4947             }
4948         ],
4949         
4950         zIndex : 10001
4951 });
4952
4953 /*
4954  * - LGPL
4955  *
4956  * messagebox - can be used as a replace
4957  * 
4958  */
4959 /**
4960  * @class Roo.MessageBox
4961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4962  * Example usage:
4963  *<pre><code>
4964 // Basic alert:
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4966
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4969     if (btn == 'ok'){
4970         // process text value...
4971     }
4972 });
4973
4974 // Show a dialog using config options:
4975 Roo.Msg.show({
4976    title:'Save Changes?',
4977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978    buttons: Roo.Msg.YESNOCANCEL,
4979    fn: processResult,
4980    animEl: 'elId'
4981 });
4982 </code></pre>
4983  * @singleton
4984  */
4985 Roo.bootstrap.MessageBox = function(){
4986     var dlg, opt, mask, waitTimer;
4987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988     var buttons, activeTextEl, bwidth;
4989
4990     
4991     // private
4992     var handleButton = function(button){
4993         dlg.hide();
4994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995     };
4996
4997     // private
4998     var handleHide = function(){
4999         if(opt && opt.cls){
5000             dlg.el.removeClass(opt.cls);
5001         }
5002         //if(waitTimer){
5003         //    Roo.TaskMgr.stop(waitTimer);
5004         //    waitTimer = null;
5005         //}
5006     };
5007
5008     // private
5009     var updateButtons = function(b){
5010         var width = 0;
5011         if(!b){
5012             buttons["ok"].hide();
5013             buttons["cancel"].hide();
5014             buttons["yes"].hide();
5015             buttons["no"].hide();
5016             dlg.footerEl.hide();
5017             
5018             return width;
5019         }
5020         dlg.footerEl.show();
5021         for(var k in buttons){
5022             if(typeof buttons[k] != "function"){
5023                 if(b[k]){
5024                     buttons[k].show();
5025                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026                     width += buttons[k].el.getWidth()+15;
5027                 }else{
5028                     buttons[k].hide();
5029                 }
5030             }
5031         }
5032         return width;
5033     };
5034
5035     // private
5036     var handleEsc = function(d, k, e){
5037         if(opt && opt.closable !== false){
5038             dlg.hide();
5039         }
5040         if(e){
5041             e.stopEvent();
5042         }
5043     };
5044
5045     return {
5046         /**
5047          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048          * @return {Roo.BasicDialog} The BasicDialog element
5049          */
5050         getDialog : function(){
5051            if(!dlg){
5052                 dlg = new Roo.bootstrap.Modal( {
5053                     //draggable: true,
5054                     //resizable:false,
5055                     //constraintoviewport:false,
5056                     //fixedcenter:true,
5057                     //collapsible : false,
5058                     //shim:true,
5059                     //modal: true,
5060                 //    width: 'auto',
5061                   //  height:100,
5062                     //buttonAlign:"center",
5063                     closeClick : function(){
5064                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065                             handleButton("no");
5066                         }else{
5067                             handleButton("cancel");
5068                         }
5069                     }
5070                 });
5071                 dlg.render();
5072                 dlg.on("hide", handleHide);
5073                 mask = dlg.mask;
5074                 //dlg.addKeyListener(27, handleEsc);
5075                 buttons = {};
5076                 this.buttons = buttons;
5077                 var bt = this.buttonText;
5078                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5082                 //Roo.log(buttons);
5083                 bodyEl = dlg.bodyEl.createChild({
5084
5085                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086                         '<textarea class="roo-mb-textarea"></textarea>' +
5087                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5088                 });
5089                 msgEl = bodyEl.dom.firstChild;
5090                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091                 textboxEl.enableDisplayMode();
5092                 textboxEl.addKeyListener([10,13], function(){
5093                     if(dlg.isVisible() && opt && opt.buttons){
5094                         if(opt.buttons.ok){
5095                             handleButton("ok");
5096                         }else if(opt.buttons.yes){
5097                             handleButton("yes");
5098                         }
5099                     }
5100                 });
5101                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102                 textareaEl.enableDisplayMode();
5103                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104                 progressEl.enableDisplayMode();
5105                 
5106                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107                 var pf = progressEl.dom.firstChild;
5108                 if (pf) {
5109                     pp = Roo.get(pf.firstChild);
5110                     pp.setHeight(pf.offsetHeight);
5111                 }
5112                 
5113             }
5114             return dlg;
5115         },
5116
5117         /**
5118          * Updates the message box body text
5119          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120          * the XHTML-compliant non-breaking space character '&amp;#160;')
5121          * @return {Roo.MessageBox} This message box
5122          */
5123         updateText : function(text)
5124         {
5125             if(!dlg.isVisible() && !opt.width){
5126                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5128             }
5129             msgEl.innerHTML = text || '&#160;';
5130       
5131             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5133             var w = Math.max(
5134                     Math.min(opt.width || cw , this.maxWidth), 
5135                     Math.max(opt.minWidth || this.minWidth, bwidth)
5136             );
5137             if(opt.prompt){
5138                 activeTextEl.setWidth(w);
5139             }
5140             if(dlg.isVisible()){
5141                 dlg.fixedcenter = false;
5142             }
5143             // to big, make it scroll. = But as usual stupid IE does not support
5144             // !important..
5145             
5146             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5149             } else {
5150                 bodyEl.dom.style.height = '';
5151                 bodyEl.dom.style.overflowY = '';
5152             }
5153             if (cw > w) {
5154                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5155             } else {
5156                 bodyEl.dom.style.overflowX = '';
5157             }
5158             
5159             dlg.setContentSize(w, bodyEl.getHeight());
5160             if(dlg.isVisible()){
5161                 dlg.fixedcenter = true;
5162             }
5163             return this;
5164         },
5165
5166         /**
5167          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5168          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171          * @return {Roo.MessageBox} This message box
5172          */
5173         updateProgress : function(value, text){
5174             if(text){
5175                 this.updateText(text);
5176             }
5177             
5178             if (pp) { // weird bug on my firefox - for some reason this is not defined
5179                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5181             }
5182             return this;
5183         },        
5184
5185         /**
5186          * Returns true if the message box is currently displayed
5187          * @return {Boolean} True if the message box is visible, else false
5188          */
5189         isVisible : function(){
5190             return dlg && dlg.isVisible();  
5191         },
5192
5193         /**
5194          * Hides the message box if it is displayed
5195          */
5196         hide : function(){
5197             if(this.isVisible()){
5198                 dlg.hide();
5199             }  
5200         },
5201
5202         /**
5203          * Displays a new message box, or reinitializes an existing message box, based on the config options
5204          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205          * The following config object properties are supported:
5206          * <pre>
5207 Property    Type             Description
5208 ----------  ---------------  ------------------------------------------------------------------------------------
5209 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5210                                    closes (defaults to undefined)
5211 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5214                                    progress and wait dialogs will ignore this property and always hide the
5215                                    close button as they can only be closed programmatically.
5216 cls               String           A custom CSS class to apply to the message box element
5217 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5218                                    displayed (defaults to 75)
5219 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5220                                    function will be btn (the name of the button that was clicked, if applicable,
5221                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5222                                    Progress and wait dialogs will ignore this option since they do not respond to
5223                                    user actions and can only be closed programmatically, so any required function
5224                                    should be called by the same code after it closes the dialog.
5225 icon              String           A CSS class that provides a background image to be used as an icon for
5226                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5228 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5229 modal             Boolean          False to allow user interaction with the page while the message box is
5230                                    displayed (defaults to true)
5231 msg               String           A string that will replace the existing message box body text (defaults
5232                                    to the XHTML-compliant non-breaking space character '&#160;')
5233 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5234 progress          Boolean          True to display a progress bar (defaults to false)
5235 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5238 title             String           The title text
5239 value             String           The string value to set into the active textbox element if displayed
5240 wait              Boolean          True to display a progress bar (defaults to false)
5241 width             Number           The width of the dialog in pixels
5242 </pre>
5243          *
5244          * Example usage:
5245          * <pre><code>
5246 Roo.Msg.show({
5247    title: 'Address',
5248    msg: 'Please enter your address:',
5249    width: 300,
5250    buttons: Roo.MessageBox.OKCANCEL,
5251    multiline: true,
5252    fn: saveAddress,
5253    animEl: 'addAddressBtn'
5254 });
5255 </code></pre>
5256          * @param {Object} config Configuration options
5257          * @return {Roo.MessageBox} This message box
5258          */
5259         show : function(options)
5260         {
5261             
5262             // this causes nightmares if you show one dialog after another
5263             // especially on callbacks..
5264              
5265             if(this.isVisible()){
5266                 
5267                 this.hide();
5268                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5270                 Roo.log("New Dialog Message:" +  options.msg )
5271                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273                 
5274             }
5275             var d = this.getDialog();
5276             opt = options;
5277             d.setTitle(opt.title || "&#160;");
5278             d.closeEl.setDisplayed(opt.closable !== false);
5279             activeTextEl = textboxEl;
5280             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5281             if(opt.prompt){
5282                 if(opt.multiline){
5283                     textboxEl.hide();
5284                     textareaEl.show();
5285                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5286                         opt.multiline : this.defaultTextHeight);
5287                     activeTextEl = textareaEl;
5288                 }else{
5289                     textboxEl.show();
5290                     textareaEl.hide();
5291                 }
5292             }else{
5293                 textboxEl.hide();
5294                 textareaEl.hide();
5295             }
5296             progressEl.setDisplayed(opt.progress === true);
5297             if (opt.progress) {
5298                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5299             }
5300             this.updateProgress(0);
5301             activeTextEl.dom.value = opt.value || "";
5302             if(opt.prompt){
5303                 dlg.setDefaultButton(activeTextEl);
5304             }else{
5305                 var bs = opt.buttons;
5306                 var db = null;
5307                 if(bs && bs.ok){
5308                     db = buttons["ok"];
5309                 }else if(bs && bs.yes){
5310                     db = buttons["yes"];
5311                 }
5312                 dlg.setDefaultButton(db);
5313             }
5314             bwidth = updateButtons(opt.buttons);
5315             this.updateText(opt.msg);
5316             if(opt.cls){
5317                 d.el.addClass(opt.cls);
5318             }
5319             d.proxyDrag = opt.proxyDrag === true;
5320             d.modal = opt.modal !== false;
5321             d.mask = opt.modal !== false ? mask : false;
5322             if(!d.isVisible()){
5323                 // force it to the end of the z-index stack so it gets a cursor in FF
5324                 document.body.appendChild(dlg.el.dom);
5325                 d.animateTarget = null;
5326                 d.show(options.animEl);
5327             }
5328             return this;
5329         },
5330
5331         /**
5332          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5333          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334          * and closing the message box when the process is complete.
5335          * @param {String} title The title bar text
5336          * @param {String} msg The message box body text
5337          * @return {Roo.MessageBox} This message box
5338          */
5339         progress : function(title, msg){
5340             this.show({
5341                 title : title,
5342                 msg : msg,
5343                 buttons: false,
5344                 progress:true,
5345                 closable:false,
5346                 minWidth: this.minProgressWidth,
5347                 modal : true
5348             });
5349             return this;
5350         },
5351
5352         /**
5353          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354          * If a callback function is passed it will be called after the user clicks the button, and the
5355          * id of the button that was clicked will be passed as the only parameter to the callback
5356          * (could also be the top-right close button).
5357          * @param {String} title The title bar text
5358          * @param {String} msg The message box body text
5359          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360          * @param {Object} scope (optional) The scope of the callback function
5361          * @return {Roo.MessageBox} This message box
5362          */
5363         alert : function(title, msg, fn, scope)
5364         {
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: this.OK,
5369                 fn: fn,
5370                 closable : false,
5371                 scope : scope,
5372                 modal : true
5373             });
5374             return this;
5375         },
5376
5377         /**
5378          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5379          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380          * You are responsible for closing the message box when the process is complete.
5381          * @param {String} msg The message box body text
5382          * @param {String} title (optional) The title bar text
5383          * @return {Roo.MessageBox} This message box
5384          */
5385         wait : function(msg, title){
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: false,
5390                 closable:false,
5391                 progress:true,
5392                 modal:true,
5393                 width:300,
5394                 wait:true
5395             });
5396             waitTimer = Roo.TaskMgr.start({
5397                 run: function(i){
5398                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5399                 },
5400                 interval: 1000
5401             });
5402             return this;
5403         },
5404
5405         /**
5406          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409          * @param {String} title The title bar text
5410          * @param {String} msg The message box body text
5411          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412          * @param {Object} scope (optional) The scope of the callback function
5413          * @return {Roo.MessageBox} This message box
5414          */
5415         confirm : function(title, msg, fn, scope){
5416             this.show({
5417                 title : title,
5418                 msg : msg,
5419                 buttons: this.YESNO,
5420                 fn: fn,
5421                 scope : scope,
5422                 modal : true
5423             });
5424             return this;
5425         },
5426
5427         /**
5428          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5430          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431          * (could also be the top-right close button) and the text that was entered will be passed as the two
5432          * parameters to the callback.
5433          * @param {String} title The title bar text
5434          * @param {String} msg The message box body text
5435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436          * @param {Object} scope (optional) The scope of the callback function
5437          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439          * @return {Roo.MessageBox} This message box
5440          */
5441         prompt : function(title, msg, fn, scope, multiline){
5442             this.show({
5443                 title : title,
5444                 msg : msg,
5445                 buttons: this.OKCANCEL,
5446                 fn: fn,
5447                 minWidth:250,
5448                 scope : scope,
5449                 prompt:true,
5450                 multiline: multiline,
5451                 modal : true
5452             });
5453             return this;
5454         },
5455
5456         /**
5457          * Button config that displays a single OK button
5458          * @type Object
5459          */
5460         OK : {ok:true},
5461         /**
5462          * Button config that displays Yes and No buttons
5463          * @type Object
5464          */
5465         YESNO : {yes:true, no:true},
5466         /**
5467          * Button config that displays OK and Cancel buttons
5468          * @type Object
5469          */
5470         OKCANCEL : {ok:true, cancel:true},
5471         /**
5472          * Button config that displays Yes, No and Cancel buttons
5473          * @type Object
5474          */
5475         YESNOCANCEL : {yes:true, no:true, cancel:true},
5476
5477         /**
5478          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479          * @type Number
5480          */
5481         defaultTextHeight : 75,
5482         /**
5483          * The maximum width in pixels of the message box (defaults to 600)
5484          * @type Number
5485          */
5486         maxWidth : 600,
5487         /**
5488          * The minimum width in pixels of the message box (defaults to 100)
5489          * @type Number
5490          */
5491         minWidth : 100,
5492         /**
5493          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5494          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495          * @type Number
5496          */
5497         minProgressWidth : 250,
5498         /**
5499          * An object containing the default button text strings that can be overriden for localized language support.
5500          * Supported properties are: ok, cancel, yes and no.
5501          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5502          * @type Object
5503          */
5504         buttonText : {
5505             ok : "OK",
5506             cancel : "Cancel",
5507             yes : "Yes",
5508             no : "No"
5509         }
5510     };
5511 }();
5512
5513 /**
5514  * Shorthand for {@link Roo.MessageBox}
5515  */
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5518 /*
5519  * - LGPL
5520  *
5521  * navbar
5522  * 
5523  */
5524
5525 /**
5526  * @class Roo.bootstrap.Navbar
5527  * @extends Roo.bootstrap.Component
5528  * Bootstrap Navbar class
5529
5530  * @constructor
5531  * Create a new Navbar
5532  * @param {Object} config The config object
5533  */
5534
5535
5536 Roo.bootstrap.Navbar = function(config){
5537     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5538     this.addEvents({
5539         // raw events
5540         /**
5541          * @event beforetoggle
5542          * Fire before toggle the menu
5543          * @param {Roo.EventObject} e
5544          */
5545         "beforetoggle" : true
5546     });
5547 };
5548
5549 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5550     
5551     
5552    
5553     // private
5554     navItems : false,
5555     loadMask : false,
5556     
5557     
5558     getAutoCreate : function(){
5559         
5560         
5561         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5562         
5563     },
5564     
5565     initEvents :function ()
5566     {
5567         //Roo.log(this.el.select('.navbar-toggle',true));
5568         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5569         
5570         var mark = {
5571             tag: "div",
5572             cls:"x-dlg-mask"
5573         };
5574         
5575         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5576         
5577         var size = this.el.getSize();
5578         this.maskEl.setSize(size.width, size.height);
5579         this.maskEl.enableDisplayMode("block");
5580         this.maskEl.hide();
5581         
5582         if(this.loadMask){
5583             this.maskEl.show();
5584         }
5585     },
5586     
5587     
5588     getChildContainer : function()
5589     {
5590         if (this.el && this.el.select('.collapse').getCount()) {
5591             return this.el.select('.collapse',true).first();
5592         }
5593         
5594         return this.el;
5595     },
5596     
5597     mask : function()
5598     {
5599         this.maskEl.show();
5600     },
5601     
5602     unmask : function()
5603     {
5604         this.maskEl.hide();
5605     },
5606     onToggle : function()
5607     {
5608         
5609         if(this.fireEvent('beforetoggle', this) === false){
5610             return;
5611         }
5612         var ce = this.el.select('.navbar-collapse',true).first();
5613       
5614         if (!ce.hasClass('show')) {
5615            this.expand();
5616         } else {
5617             this.collapse();
5618         }
5619         
5620         
5621     
5622     },
5623     /**
5624      * Expand the navbar pulldown 
5625      */
5626     expand : function ()
5627     {
5628        
5629         var ce = this.el.select('.navbar-collapse',true).first();
5630         if (ce.hasClass('collapsing')) {
5631             return;
5632         }
5633         ce.dom.style.height = '';
5634                // show it...
5635         ce.addClass('in'); // old...
5636         ce.removeClass('collapse');
5637         ce.addClass('show');
5638         var h = ce.getHeight();
5639         Roo.log(h);
5640         ce.removeClass('show');
5641         // at this point we should be able to see it..
5642         ce.addClass('collapsing');
5643         
5644         ce.setHeight(0); // resize it ...
5645         ce.on('transitionend', function() {
5646             //Roo.log('done transition');
5647             ce.removeClass('collapsing');
5648             ce.addClass('show');
5649             ce.removeClass('collapse');
5650
5651             ce.dom.style.height = '';
5652         }, this, { single: true} );
5653         ce.setHeight(h);
5654         ce.dom.scrollTop = 0;
5655     },
5656     /**
5657      * Collapse the navbar pulldown 
5658      */
5659     collapse : function()
5660     {
5661          var ce = this.el.select('.navbar-collapse',true).first();
5662        
5663         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5664             // it's collapsed or collapsing..
5665             return;
5666         }
5667         ce.removeClass('in'); // old...
5668         ce.setHeight(ce.getHeight());
5669         ce.removeClass('show');
5670         ce.addClass('collapsing');
5671         
5672         ce.on('transitionend', function() {
5673             ce.dom.style.height = '';
5674             ce.removeClass('collapsing');
5675             ce.addClass('collapse');
5676         }, this, { single: true} );
5677         ce.setHeight(0);
5678     }
5679     
5680     
5681     
5682 });
5683
5684
5685
5686  
5687
5688  /*
5689  * - LGPL
5690  *
5691  * navbar
5692  * 
5693  */
5694
5695 /**
5696  * @class Roo.bootstrap.NavSimplebar
5697  * @extends Roo.bootstrap.Navbar
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.NavSimplebar = function(config){
5721     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.NavHeaderbar
5825  * @extends Roo.bootstrap.NavSimplebar
5826  * Bootstrap Sidebar class
5827  *
5828  * @cfg {String} brand what is brand
5829  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5830  * @cfg {String} brand_href href of the brand
5831  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5832  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5833  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5834  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5835  * 
5836  * @constructor
5837  * Create a new Sidebar
5838  * @param {Object} config The config object
5839  */
5840
5841
5842 Roo.bootstrap.NavHeaderbar = function(config){
5843     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5844       
5845 };
5846
5847 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5848     
5849     position: '',
5850     brand: '',
5851     brand_href: false,
5852     srButton : true,
5853     autohide : false,
5854     desktopCenter : false,
5855    
5856     
5857     getAutoCreate : function(){
5858         
5859         var   cfg = {
5860             tag: this.nav || 'nav',
5861             cls: 'navbar navbar-expand-md',
5862             role: 'navigation',
5863             cn: []
5864         };
5865         
5866         var cn = cfg.cn;
5867         if (this.desktopCenter) {
5868             cn.push({cls : 'container', cn : []});
5869             cn = cn[0].cn;
5870         }
5871         
5872         if(this.srButton){
5873             var btn = {
5874                 tag: 'button',
5875                 type: 'button',
5876                 cls: 'navbar-toggle navbar-toggler',
5877                 'data-toggle': 'collapse',
5878                 cn: [
5879                     {
5880                         tag: 'span',
5881                         cls: 'sr-only',
5882                         html: 'Toggle navigation'
5883                     },
5884                     {
5885                         tag: 'span',
5886                         cls: 'icon-bar navbar-toggler-icon'
5887                     },
5888                     {
5889                         tag: 'span',
5890                         cls: 'icon-bar'
5891                     },
5892                     {
5893                         tag: 'span',
5894                         cls: 'icon-bar'
5895                     }
5896                 ]
5897             };
5898             
5899             cn.push( Roo.bootstrap.version == 4 ? btn : {
5900                 tag: 'div',
5901                 cls: 'navbar-header',
5902                 cn: [
5903                     btn
5904                 ]
5905             });
5906         }
5907         
5908         cn.push({
5909             tag: 'div',
5910             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5911             cn : []
5912         });
5913         
5914         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5915         
5916         if (['light','white'].indexOf(this.weight) > -1) {
5917             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5918         }
5919         cfg.cls += ' bg-' + this.weight;
5920         
5921         
5922         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5923             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5924             
5925             // tag can override this..
5926             
5927             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5928         }
5929         
5930         if (this.brand !== '') {
5931             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5932             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5933                 tag: 'a',
5934                 href: this.brand_href ? this.brand_href : '#',
5935                 cls: 'navbar-brand',
5936                 cn: [
5937                 this.brand
5938                 ]
5939             });
5940         }
5941         
5942         if(this.main){
5943             cfg.cls += ' main-nav';
5944         }
5945         
5946         
5947         return cfg;
5948
5949         
5950     },
5951     getHeaderChildContainer : function()
5952     {
5953         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5954             return this.el.select('.navbar-header',true).first();
5955         }
5956         
5957         return this.getChildContainer();
5958     },
5959     
5960     getChildContainer : function()
5961     {
5962          
5963         return this.el.select('.roo-navbar-collapse',true).first();
5964          
5965         
5966     },
5967     
5968     initEvents : function()
5969     {
5970         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5971         
5972         if (this.autohide) {
5973             
5974             var prevScroll = 0;
5975             var ft = this.el;
5976             
5977             Roo.get(document).on('scroll',function(e) {
5978                 var ns = Roo.get(document).getScroll().top;
5979                 var os = prevScroll;
5980                 prevScroll = ns;
5981                 
5982                 if(ns > os){
5983                     ft.removeClass('slideDown');
5984                     ft.addClass('slideUp');
5985                     return;
5986                 }
5987                 ft.removeClass('slideUp');
5988                 ft.addClass('slideDown');
5989                  
5990               
5991           },this);
5992         }
5993     }    
5994     
5995 });
5996
5997
5998
5999  
6000
6001  /*
6002  * - LGPL
6003  *
6004  * navbar
6005  * 
6006  */
6007
6008 /**
6009  * @class Roo.bootstrap.NavSidebar
6010  * @extends Roo.bootstrap.Navbar
6011  * Bootstrap Sidebar class
6012  * 
6013  * @constructor
6014  * Create a new Sidebar
6015  * @param {Object} config The config object
6016  */
6017
6018
6019 Roo.bootstrap.NavSidebar = function(config){
6020     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6021 };
6022
6023 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6024     
6025     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6026     
6027     getAutoCreate : function(){
6028         
6029         
6030         return  {
6031             tag: 'div',
6032             cls: 'sidebar sidebar-nav'
6033         };
6034     
6035         
6036     }
6037     
6038     
6039     
6040 });
6041
6042
6043
6044  
6045
6046  /*
6047  * - LGPL
6048  *
6049  * nav group
6050  * 
6051  */
6052
6053 /**
6054  * @class Roo.bootstrap.NavGroup
6055  * @extends Roo.bootstrap.Component
6056  * Bootstrap NavGroup class
6057  * @cfg {String} align (left|right)
6058  * @cfg {Boolean} inverse
6059  * @cfg {String} type (nav|pills|tab) default nav
6060  * @cfg {String} navId - reference Id for navbar.
6061  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6062  * 
6063  * @constructor
6064  * Create a new nav group
6065  * @param {Object} config The config object
6066  */
6067
6068 Roo.bootstrap.NavGroup = function(config){
6069     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6070     this.navItems = [];
6071    
6072     Roo.bootstrap.NavGroup.register(this);
6073      this.addEvents({
6074         /**
6075              * @event changed
6076              * Fires when the active item changes
6077              * @param {Roo.bootstrap.NavGroup} this
6078              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6079              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6080          */
6081         'changed': true
6082      });
6083     
6084 };
6085
6086 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6087     
6088     align: '',
6089     inverse: false,
6090     form: false,
6091     type: 'nav',
6092     navId : '',
6093     // private
6094     pilltype : true,
6095     
6096     navItems : false, 
6097     
6098     getAutoCreate : function()
6099     {
6100         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6101         
6102         cfg = {
6103             tag : 'ul',
6104             cls: 'nav' 
6105         };
6106         if (Roo.bootstrap.version == 4) {
6107             if (['tabs','pills'].indexOf(this.type) != -1) {
6108                 cfg.cls += ' nav-' + this.type; 
6109             } else {
6110                 // trying to remove so header bar can right align top?
6111                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6112                     // do not use on header bar... 
6113                     cfg.cls += ' navbar-nav';
6114                 }
6115             }
6116             
6117         } else {
6118             if (['tabs','pills'].indexOf(this.type) != -1) {
6119                 cfg.cls += ' nav-' + this.type
6120             } else {
6121                 if (this.type !== 'nav') {
6122                     Roo.log('nav type must be nav/tabs/pills')
6123                 }
6124                 cfg.cls += ' navbar-nav'
6125             }
6126         }
6127         
6128         if (this.parent() && this.parent().sidebar) {
6129             cfg = {
6130                 tag: 'ul',
6131                 cls: 'dashboard-menu sidebar-menu'
6132             };
6133             
6134             return cfg;
6135         }
6136         
6137         if (this.form === true) {
6138             cfg = {
6139                 tag: 'form',
6140                 cls: 'navbar-form form-inline'
6141             };
6142             //nav navbar-right ml-md-auto
6143             if (this.align === 'right') {
6144                 cfg.cls += ' navbar-right ml-md-auto';
6145             } else {
6146                 cfg.cls += ' navbar-left';
6147             }
6148         }
6149         
6150         if (this.align === 'right') {
6151             cfg.cls += ' navbar-right ml-md-auto';
6152         } else {
6153             cfg.cls += ' mr-auto';
6154         }
6155         
6156         if (this.inverse) {
6157             cfg.cls += ' navbar-inverse';
6158             
6159         }
6160         
6161         
6162         return cfg;
6163     },
6164     /**
6165     * sets the active Navigation item
6166     * @param {Roo.bootstrap.NavItem} the new current navitem
6167     */
6168     setActiveItem : function(item)
6169     {
6170         var prev = false;
6171         Roo.each(this.navItems, function(v){
6172             if (v == item) {
6173                 return ;
6174             }
6175             if (v.isActive()) {
6176                 v.setActive(false, true);
6177                 prev = v;
6178                 
6179             }
6180             
6181         });
6182
6183         item.setActive(true, true);
6184         this.fireEvent('changed', this, item, prev);
6185         
6186         
6187     },
6188     /**
6189     * gets the active Navigation item
6190     * @return {Roo.bootstrap.NavItem} the current navitem
6191     */
6192     getActive : function()
6193     {
6194         
6195         var prev = false;
6196         Roo.each(this.navItems, function(v){
6197             
6198             if (v.isActive()) {
6199                 prev = v;
6200                 
6201             }
6202             
6203         });
6204         return prev;
6205     },
6206     
6207     indexOfNav : function()
6208     {
6209         
6210         var prev = false;
6211         Roo.each(this.navItems, function(v,i){
6212             
6213             if (v.isActive()) {
6214                 prev = i;
6215                 
6216             }
6217             
6218         });
6219         return prev;
6220     },
6221     /**
6222     * adds a Navigation item
6223     * @param {Roo.bootstrap.NavItem} the navitem to add
6224     */
6225     addItem : function(cfg)
6226     {
6227         if (this.form && Roo.bootstrap.version == 4) {
6228             cfg.tag = 'div';
6229         }
6230         var cn = new Roo.bootstrap.NavItem(cfg);
6231         this.register(cn);
6232         cn.parentId = this.id;
6233         cn.onRender(this.el, null);
6234         return cn;
6235     },
6236     /**
6237     * register a Navigation item
6238     * @param {Roo.bootstrap.NavItem} the navitem to add
6239     */
6240     register : function(item)
6241     {
6242         this.navItems.push( item);
6243         item.navId = this.navId;
6244     
6245     },
6246     
6247     /**
6248     * clear all the Navigation item
6249     */
6250    
6251     clearAll : function()
6252     {
6253         this.navItems = [];
6254         this.el.dom.innerHTML = '';
6255     },
6256     
6257     getNavItem: function(tabId)
6258     {
6259         var ret = false;
6260         Roo.each(this.navItems, function(e) {
6261             if (e.tabId == tabId) {
6262                ret =  e;
6263                return false;
6264             }
6265             return true;
6266             
6267         });
6268         return ret;
6269     },
6270     
6271     setActiveNext : function()
6272     {
6273         var i = this.indexOfNav(this.getActive());
6274         if (i > this.navItems.length) {
6275             return;
6276         }
6277         this.setActiveItem(this.navItems[i+1]);
6278     },
6279     setActivePrev : function()
6280     {
6281         var i = this.indexOfNav(this.getActive());
6282         if (i  < 1) {
6283             return;
6284         }
6285         this.setActiveItem(this.navItems[i-1]);
6286     },
6287     clearWasActive : function(except) {
6288         Roo.each(this.navItems, function(e) {
6289             if (e.tabId != except.tabId && e.was_active) {
6290                e.was_active = false;
6291                return false;
6292             }
6293             return true;
6294             
6295         });
6296     },
6297     getWasActive : function ()
6298     {
6299         var r = false;
6300         Roo.each(this.navItems, function(e) {
6301             if (e.was_active) {
6302                r = e;
6303                return false;
6304             }
6305             return true;
6306             
6307         });
6308         return r;
6309     }
6310     
6311     
6312 });
6313
6314  
6315 Roo.apply(Roo.bootstrap.NavGroup, {
6316     
6317     groups: {},
6318      /**
6319     * register a Navigation Group
6320     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6321     */
6322     register : function(navgrp)
6323     {
6324         this.groups[navgrp.navId] = navgrp;
6325         
6326     },
6327     /**
6328     * fetch a Navigation Group based on the navigation ID
6329     * @param {string} the navgroup to add
6330     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6331     */
6332     get: function(navId) {
6333         if (typeof(this.groups[navId]) == 'undefined') {
6334             return false;
6335             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6336         }
6337         return this.groups[navId] ;
6338     }
6339     
6340     
6341     
6342 });
6343
6344  /*
6345  * - LGPL
6346  *
6347  * row
6348  * 
6349  */
6350
6351 /**
6352  * @class Roo.bootstrap.NavItem
6353  * @extends Roo.bootstrap.Component
6354  * Bootstrap Navbar.NavItem class
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371   
6372  * @constructor
6373  * Create a new Navbar Item
6374  * @param {Object} config The config object
6375  */
6376 Roo.bootstrap.NavItem = function(config){
6377     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6378     this.addEvents({
6379         // raw events
6380         /**
6381          * @event click
6382          * The raw click event for the entire grid.
6383          * @param {Roo.EventObject} e
6384          */
6385         "click" : true,
6386          /**
6387             * @event changed
6388             * Fires when the active item active state changes
6389             * @param {Roo.bootstrap.NavItem} this
6390             * @param {boolean} state the new state
6391              
6392          */
6393         'changed': true,
6394         /**
6395             * @event scrollto
6396             * Fires when scroll to element
6397             * @param {Roo.bootstrap.NavItem} this
6398             * @param {Object} options
6399             * @param {Roo.EventObject} e
6400              
6401          */
6402         'scrollto': true
6403     });
6404    
6405 };
6406
6407 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6408     
6409     href: false,
6410     html: '',
6411     badge: '',
6412     icon: false,
6413     fa : false,
6414     glyphicon: false,
6415     active: false,
6416     preventDefault : false,
6417     tabId : false,
6418     tagtype : 'a',
6419     tag: 'li',
6420     disabled : false,
6421     animateRef : false,
6422     was_active : false,
6423     button_weight : '',
6424     button_outline : false,
6425     linkcls : '',
6426     navLink: false,
6427     
6428     getAutoCreate : function(){
6429          
6430         var cfg = {
6431             tag: this.tag,
6432             cls: 'nav-item'
6433         };
6434         
6435         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6436         
6437         if (this.active) {
6438             cfg.cls +=  ' active' ;
6439         }
6440         if (this.disabled) {
6441             cfg.cls += ' disabled';
6442         }
6443         
6444         // BS4 only?
6445         if (this.button_weight.length) {
6446             cfg.tag = this.href ? 'a' : 'button';
6447             cfg.html = this.html || '';
6448             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6449             if (this.href) {
6450                 cfg.href = this.href;
6451             }
6452             if (this.fa) {
6453                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6454             } else {
6455                 cfg.cls += " nav-html";
6456             }
6457             
6458             // menu .. should add dropdown-menu class - so no need for carat..
6459             
6460             if (this.badge !== '') {
6461                  
6462                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6463             }
6464             return cfg;
6465         }
6466         
6467         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6468             cfg.cn = [
6469                 {
6470                     tag: this.tagtype,
6471                     href : this.href || "#",
6472                     html: this.html || '',
6473                     cls : ''
6474                 }
6475             ];
6476             if (this.tagtype == 'a') {
6477                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6478         
6479             }
6480             if (this.icon) {
6481                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6482             } else  if (this.fa) {
6483                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6484             } else if(this.glyphicon) {
6485                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6486             } else {
6487                 cfg.cn[0].cls += " nav-html";
6488             }
6489             
6490             if (this.menu) {
6491                 cfg.cn[0].html += " <span class='caret'></span>";
6492              
6493             }
6494             
6495             if (this.badge !== '') {
6496                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6497             }
6498         }
6499         
6500         
6501         
6502         return cfg;
6503     },
6504     onRender : function(ct, position)
6505     {
6506        // Roo.log("Call onRender: " + this.xtype);
6507         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6508             this.tag = 'div';
6509         }
6510         
6511         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6512         this.navLink = this.el.select('.nav-link',true).first();
6513         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6514         return ret;
6515     },
6516       
6517     
6518     initEvents: function() 
6519     {
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         //if(this.tagtype == 'span'){
6529         //    this.el.select('span',true).on('click', this.onClick, this);
6530         //}
6531        
6532         // at this point parent should be available..
6533         this.parent().register(this);
6534     },
6535     
6536     onClick : function(e)
6537     {
6538         if (e.getTarget('.dropdown-menu-item')) {
6539             // did you click on a menu itemm.... - then don't trigger onclick..
6540             return;
6541         }
6542         
6543         if(
6544                 this.preventDefault || 
6545                 this.href == '#' 
6546         ){
6547             Roo.log("NavItem - prevent Default?");
6548             e.preventDefault();
6549         }
6550         
6551         if (this.disabled) {
6552             return;
6553         }
6554         
6555         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6556         if (tg && tg.transition) {
6557             Roo.log("waiting for the transitionend");
6558             return;
6559         }
6560         
6561         
6562         
6563         //Roo.log("fire event clicked");
6564         if(this.fireEvent('click', this, e) === false){
6565             return;
6566         };
6567         
6568         if(this.tagtype == 'span'){
6569             return;
6570         }
6571         
6572         //Roo.log(this.href);
6573         var ael = this.el.select('a',true).first();
6574         //Roo.log(ael);
6575         
6576         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6577             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6578             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6579                 return; // ignore... - it's a 'hash' to another page.
6580             }
6581             Roo.log("NavItem - prevent Default?");
6582             e.preventDefault();
6583             this.scrollToElement(e);
6584         }
6585         
6586         
6587         var p =  this.parent();
6588    
6589         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6590             if (typeof(p.setActiveItem) !== 'undefined') {
6591                 p.setActiveItem(this);
6592             }
6593         }
6594         
6595         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6596         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6597             // remove the collapsed menu expand...
6598             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6599         }
6600     },
6601     
6602     isActive: function () {
6603         return this.active
6604     },
6605     setActive : function(state, fire, is_was_active)
6606     {
6607         if (this.active && !state && this.navId) {
6608             this.was_active = true;
6609             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6610             if (nv) {
6611                 nv.clearWasActive(this);
6612             }
6613             
6614         }
6615         this.active = state;
6616         
6617         if (!state ) {
6618             this.el.removeClass('active');
6619             this.navLink ? this.navLink.removeClass('active') : false;
6620         } else if (!this.el.hasClass('active')) {
6621             
6622             this.el.addClass('active');
6623             if (Roo.bootstrap.version == 4 && this.navLink ) {
6624                 this.navLink.addClass('active');
6625             }
6626             
6627         }
6628         if (fire) {
6629             this.fireEvent('changed', this, state);
6630         }
6631         
6632         // show a panel if it's registered and related..
6633         
6634         if (!this.navId || !this.tabId || !state || is_was_active) {
6635             return;
6636         }
6637         
6638         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6639         if (!tg) {
6640             return;
6641         }
6642         var pan = tg.getPanelByName(this.tabId);
6643         if (!pan) {
6644             return;
6645         }
6646         // if we can not flip to new panel - go back to old nav highlight..
6647         if (false == tg.showPanel(pan)) {
6648             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6649             if (nv) {
6650                 var onav = nv.getWasActive();
6651                 if (onav) {
6652                     onav.setActive(true, false, true);
6653                 }
6654             }
6655             
6656         }
6657         
6658         
6659         
6660     },
6661      // this should not be here...
6662     setDisabled : function(state)
6663     {
6664         this.disabled = state;
6665         if (!state ) {
6666             this.el.removeClass('disabled');
6667         } else if (!this.el.hasClass('disabled')) {
6668             this.el.addClass('disabled');
6669         }
6670         
6671     },
6672     
6673     /**
6674      * Fetch the element to display the tooltip on.
6675      * @return {Roo.Element} defaults to this.el
6676      */
6677     tooltipEl : function()
6678     {
6679         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6680     },
6681     
6682     scrollToElement : function(e)
6683     {
6684         var c = document.body;
6685         
6686         /*
6687          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6688          */
6689         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6690             c = document.documentElement;
6691         }
6692         
6693         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6694         
6695         if(!target){
6696             return;
6697         }
6698
6699         var o = target.calcOffsetsTo(c);
6700         
6701         var options = {
6702             target : target,
6703             value : o[1]
6704         };
6705         
6706         this.fireEvent('scrollto', this, options, e);
6707         
6708         Roo.get(c).scrollTo('top', options.value, true);
6709         
6710         return;
6711     },
6712     /**
6713      * Set the HTML (text content) of the item
6714      * @param {string} html  content for the nav item
6715      */
6716     setHtml : function(html)
6717     {
6718         this.html = html;
6719         this.htmlEl.dom.innerHTML = html;
6720         
6721     } 
6722 });
6723  
6724
6725  /*
6726  * - LGPL
6727  *
6728  * sidebar item
6729  *
6730  *  li
6731  *    <span> icon </span>
6732  *    <span> text </span>
6733  *    <span>badge </span>
6734  */
6735
6736 /**
6737  * @class Roo.bootstrap.NavSidebarItem
6738  * @extends Roo.bootstrap.NavItem
6739  * Bootstrap Navbar.NavSidebarItem class
6740  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6741  * {Boolean} open is the menu open
6742  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6743  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6744  * {String} buttonSize (sm|md|lg)the extra classes for the button
6745  * {Boolean} showArrow show arrow next to the text (default true)
6746  * @constructor
6747  * Create a new Navbar Button
6748  * @param {Object} config The config object
6749  */
6750 Roo.bootstrap.NavSidebarItem = function(config){
6751     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6752     this.addEvents({
6753         // raw events
6754         /**
6755          * @event click
6756          * The raw click event for the entire grid.
6757          * @param {Roo.EventObject} e
6758          */
6759         "click" : true,
6760          /**
6761             * @event changed
6762             * Fires when the active item active state changes
6763             * @param {Roo.bootstrap.NavSidebarItem} this
6764             * @param {boolean} state the new state
6765              
6766          */
6767         'changed': true
6768     });
6769    
6770 };
6771
6772 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6773     
6774     badgeWeight : 'default',
6775     
6776     open: false,
6777     
6778     buttonView : false,
6779     
6780     buttonWeight : 'default',
6781     
6782     buttonSize : 'md',
6783     
6784     showArrow : true,
6785     
6786     getAutoCreate : function(){
6787         
6788         
6789         var a = {
6790                 tag: 'a',
6791                 href : this.href || '#',
6792                 cls: '',
6793                 html : '',
6794                 cn : []
6795         };
6796         
6797         if(this.buttonView){
6798             a = {
6799                 tag: 'button',
6800                 href : this.href || '#',
6801                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6802                 html : this.html,
6803                 cn : []
6804             };
6805         }
6806         
6807         var cfg = {
6808             tag: 'li',
6809             cls: '',
6810             cn: [ a ]
6811         };
6812         
6813         if (this.active) {
6814             cfg.cls += ' active';
6815         }
6816         
6817         if (this.disabled) {
6818             cfg.cls += ' disabled';
6819         }
6820         if (this.open) {
6821             cfg.cls += ' open x-open';
6822         }
6823         // left icon..
6824         if (this.glyphicon || this.icon) {
6825             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6826             a.cn.push({ tag : 'i', cls : c }) ;
6827         }
6828         
6829         if(!this.buttonView){
6830             var span = {
6831                 tag: 'span',
6832                 html : this.html || ''
6833             };
6834
6835             a.cn.push(span);
6836             
6837         }
6838         
6839         if (this.badge !== '') {
6840             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6841         }
6842         
6843         if (this.menu) {
6844             
6845             if(this.showArrow){
6846                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6847             }
6848             
6849             a.cls += ' dropdown-toggle treeview' ;
6850         }
6851         
6852         return cfg;
6853     },
6854     
6855     initEvents : function()
6856     { 
6857         if (typeof (this.menu) != 'undefined') {
6858             this.menu.parentType = this.xtype;
6859             this.menu.triggerEl = this.el;
6860             this.menu = this.addxtype(Roo.apply({}, this.menu));
6861         }
6862         
6863         this.el.on('click', this.onClick, this);
6864         
6865         if(this.badge !== ''){
6866             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6867         }
6868         
6869     },
6870     
6871     onClick : function(e)
6872     {
6873         if(this.disabled){
6874             e.preventDefault();
6875             return;
6876         }
6877         
6878         if(this.preventDefault){
6879             e.preventDefault();
6880         }
6881         
6882         this.fireEvent('click', this, e);
6883     },
6884     
6885     disable : function()
6886     {
6887         this.setDisabled(true);
6888     },
6889     
6890     enable : function()
6891     {
6892         this.setDisabled(false);
6893     },
6894     
6895     setDisabled : function(state)
6896     {
6897         if(this.disabled == state){
6898             return;
6899         }
6900         
6901         this.disabled = state;
6902         
6903         if (state) {
6904             this.el.addClass('disabled');
6905             return;
6906         }
6907         
6908         this.el.removeClass('disabled');
6909         
6910         return;
6911     },
6912     
6913     setActive : function(state)
6914     {
6915         if(this.active == state){
6916             return;
6917         }
6918         
6919         this.active = state;
6920         
6921         if (state) {
6922             this.el.addClass('active');
6923             return;
6924         }
6925         
6926         this.el.removeClass('active');
6927         
6928         return;
6929     },
6930     
6931     isActive: function () 
6932     {
6933         return this.active;
6934     },
6935     
6936     setBadge : function(str)
6937     {
6938         if(!this.badgeEl){
6939             return;
6940         }
6941         
6942         this.badgeEl.dom.innerHTML = str;
6943     }
6944     
6945    
6946      
6947  
6948 });
6949  
6950
6951  /*
6952  * - LGPL
6953  *
6954  *  Breadcrumb Nav
6955  * 
6956  */
6957 Roo.namespace('Roo.bootstrap.breadcrumb');
6958
6959
6960 /**
6961  * @class Roo.bootstrap.breadcrumb.Nav
6962  * @extends Roo.bootstrap.Component
6963  * Bootstrap Breadcrumb Nav Class
6964  *  
6965  * @children Roo.bootstrap.breadcrumb.Item
6966  * 
6967  * @constructor
6968  * Create a new breadcrumb.Nav
6969  * @param {Object} config The config object
6970  */
6971
6972
6973 Roo.bootstrap.breadcrumb.Nav = function(config){
6974     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6975     
6976     
6977 };
6978
6979 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6980     
6981     getAutoCreate : function()
6982     {
6983
6984         var cfg = {
6985             tag: 'nav',
6986             cn : [
6987                 {
6988                     tag : 'ol',
6989                     cls : 'breadcrumb'
6990                 }
6991             ]
6992             
6993         };
6994           
6995         return cfg;
6996     },
6997     
6998     initEvents: function()
6999     {
7000         this.olEl = this.el.select('ol',true).first();    
7001     },
7002     getChildContainer : function()
7003     {
7004         return this.olEl;  
7005     }
7006     
7007 });
7008
7009  /*
7010  * - LGPL
7011  *
7012  *  Breadcrumb Item
7013  * 
7014  */
7015
7016
7017 /**
7018  * @class Roo.bootstrap.breadcrumb.Nav
7019  * @extends Roo.bootstrap.Component
7020  * Bootstrap Breadcrumb Nav Class
7021  *  
7022  * @children Roo.bootstrap.breadcrumb.Component
7023  * @cfg {String} html the content of the link.
7024  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7025  * @cfg {Boolean} active is it active
7026
7027  * 
7028  * @constructor
7029  * Create a new breadcrumb.Nav
7030  * @param {Object} config The config object
7031  */
7032
7033 Roo.bootstrap.breadcrumb.Item = function(config){
7034     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7035     this.addEvents({
7036         // img events
7037         /**
7038          * @event click
7039          * The img click event for the img.
7040          * @param {Roo.EventObject} e
7041          */
7042         "click" : true
7043     });
7044     
7045 };
7046
7047 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7048     
7049     href: false,
7050     html : '',
7051     
7052     getAutoCreate : function()
7053     {
7054
7055         var cfg = {
7056             tag: 'li',
7057             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7058         };
7059         if (this.href !== false) {
7060             cfg.cn = [{
7061                 tag : 'a',
7062                 href : this.href,
7063                 html : this.html
7064             }];
7065         } else {
7066             cfg.html = this.html;
7067         }
7068         
7069         return cfg;
7070     },
7071     
7072     initEvents: function()
7073     {
7074         if (this.href) {
7075             this.el.select('a', true).first().on('click',this.onClick, this)
7076         }
7077         
7078     },
7079     onClick : function(e)
7080     {
7081         e.preventDefault();
7082         this.fireEvent('click',this,  e);
7083     }
7084     
7085 });
7086
7087  /*
7088  * - LGPL
7089  *
7090  * row
7091  * 
7092  */
7093
7094 /**
7095  * @class Roo.bootstrap.Row
7096  * @extends Roo.bootstrap.Component
7097  * Bootstrap Row class (contains columns...)
7098  * 
7099  * @constructor
7100  * Create a new Row
7101  * @param {Object} config The config object
7102  */
7103
7104 Roo.bootstrap.Row = function(config){
7105     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7106 };
7107
7108 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7109     
7110     getAutoCreate : function(){
7111        return {
7112             cls: 'row clearfix'
7113        };
7114     }
7115     
7116     
7117 });
7118
7119  
7120
7121  /*
7122  * - LGPL
7123  *
7124  * pagination
7125  * 
7126  */
7127
7128 /**
7129  * @class Roo.bootstrap.Pagination
7130  * @extends Roo.bootstrap.Component
7131  * Bootstrap Pagination class
7132  * @cfg {String} size xs | sm | md | lg
7133  * @cfg {Boolean} inverse false | true
7134  * 
7135  * @constructor
7136  * Create a new Pagination
7137  * @param {Object} config The config object
7138  */
7139
7140 Roo.bootstrap.Pagination = function(config){
7141     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7142 };
7143
7144 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7145     
7146     cls: false,
7147     size: false,
7148     inverse: false,
7149     
7150     getAutoCreate : function(){
7151         var cfg = {
7152             tag: 'ul',
7153                 cls: 'pagination'
7154         };
7155         if (this.inverse) {
7156             cfg.cls += ' inverse';
7157         }
7158         if (this.html) {
7159             cfg.html=this.html;
7160         }
7161         if (this.cls) {
7162             cfg.cls += " " + this.cls;
7163         }
7164         return cfg;
7165     }
7166    
7167 });
7168
7169  
7170
7171  /*
7172  * - LGPL
7173  *
7174  * Pagination item
7175  * 
7176  */
7177
7178
7179 /**
7180  * @class Roo.bootstrap.PaginationItem
7181  * @extends Roo.bootstrap.Component
7182  * Bootstrap PaginationItem class
7183  * @cfg {String} html text
7184  * @cfg {String} href the link
7185  * @cfg {Boolean} preventDefault (true | false) default true
7186  * @cfg {Boolean} active (true | false) default false
7187  * @cfg {Boolean} disabled default false
7188  * 
7189  * 
7190  * @constructor
7191  * Create a new PaginationItem
7192  * @param {Object} config The config object
7193  */
7194
7195
7196 Roo.bootstrap.PaginationItem = function(config){
7197     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7198     this.addEvents({
7199         // raw events
7200         /**
7201          * @event click
7202          * The raw click event for the entire grid.
7203          * @param {Roo.EventObject} e
7204          */
7205         "click" : true
7206     });
7207 };
7208
7209 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7210     
7211     href : false,
7212     html : false,
7213     preventDefault: true,
7214     active : false,
7215     cls : false,
7216     disabled: false,
7217     
7218     getAutoCreate : function(){
7219         var cfg= {
7220             tag: 'li',
7221             cn: [
7222                 {
7223                     tag : 'a',
7224                     href : this.href ? this.href : '#',
7225                     html : this.html ? this.html : ''
7226                 }
7227             ]
7228         };
7229         
7230         if(this.cls){
7231             cfg.cls = this.cls;
7232         }
7233         
7234         if(this.disabled){
7235             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7236         }
7237         
7238         if(this.active){
7239             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7240         }
7241         
7242         return cfg;
7243     },
7244     
7245     initEvents: function() {
7246         
7247         this.el.on('click', this.onClick, this);
7248         
7249     },
7250     onClick : function(e)
7251     {
7252         Roo.log('PaginationItem on click ');
7253         if(this.preventDefault){
7254             e.preventDefault();
7255         }
7256         
7257         if(this.disabled){
7258             return;
7259         }
7260         
7261         this.fireEvent('click', this, e);
7262     }
7263    
7264 });
7265
7266  
7267
7268  /*
7269  * - LGPL
7270  *
7271  * slider
7272  * 
7273  */
7274
7275
7276 /**
7277  * @class Roo.bootstrap.Slider
7278  * @extends Roo.bootstrap.Component
7279  * Bootstrap Slider class
7280  *    
7281  * @constructor
7282  * Create a new Slider
7283  * @param {Object} config The config object
7284  */
7285
7286 Roo.bootstrap.Slider = function(config){
7287     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7288 };
7289
7290 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7291     
7292     getAutoCreate : function(){
7293         
7294         var cfg = {
7295             tag: 'div',
7296             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7297             cn: [
7298                 {
7299                     tag: 'a',
7300                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7301                 }
7302             ]
7303         };
7304         
7305         return cfg;
7306     }
7307    
7308 });
7309
7310  /*
7311  * Based on:
7312  * Ext JS Library 1.1.1
7313  * Copyright(c) 2006-2007, Ext JS, LLC.
7314  *
7315  * Originally Released Under LGPL - original licence link has changed is not relivant.
7316  *
7317  * Fork - LGPL
7318  * <script type="text/javascript">
7319  */
7320  /**
7321  * @extends Roo.dd.DDProxy
7322  * @class Roo.grid.SplitDragZone
7323  * Support for Column Header resizing
7324  * @constructor
7325  * @param {Object} config
7326  */
7327 // private
7328 // This is a support class used internally by the Grid components
7329 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7330     this.grid = grid;
7331     this.view = grid.getView();
7332     this.proxy = this.view.resizeProxy;
7333     Roo.grid.SplitDragZone.superclass.constructor.call(
7334         this,
7335         hd, // ID
7336         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7337         {  // CONFIG
7338             dragElId : Roo.id(this.proxy.dom),
7339             resizeFrame:false
7340         }
7341     );
7342     
7343     this.setHandleElId(Roo.id(hd));
7344     if (hd2 !== false) {
7345         this.setOuterHandleElId(Roo.id(hd2));
7346     }
7347     
7348     this.scroll = false;
7349 };
7350 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7351     fly: Roo.Element.fly,
7352
7353     b4StartDrag : function(x, y){
7354         this.view.headersDisabled = true;
7355         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7356                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7357         );
7358         this.proxy.setHeight(h);
7359         
7360         // for old system colWidth really stored the actual width?
7361         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7362         // which in reality did not work.. - it worked only for fixed sizes
7363         // for resizable we need to use actual sizes.
7364         var w = this.cm.getColumnWidth(this.cellIndex);
7365         if (!this.view.mainWrap) {
7366             // bootstrap.
7367             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7368         }
7369         
7370         
7371         
7372         // this was w-this.grid.minColumnWidth;
7373         // doesnt really make sense? - w = thie curren width or the rendered one?
7374         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7375         this.resetConstraints();
7376         this.setXConstraint(minw, 1000);
7377         this.setYConstraint(0, 0);
7378         this.minX = x - minw;
7379         this.maxX = x + 1000;
7380         this.startPos = x;
7381         if (!this.view.mainWrap) { // this is Bootstrap code..
7382             this.getDragEl().style.display='block';
7383         }
7384         
7385         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7386     },
7387
7388
7389     handleMouseDown : function(e){
7390         ev = Roo.EventObject.setEvent(e);
7391         var t = this.fly(ev.getTarget());
7392         if(t.hasClass("x-grid-split")){
7393             this.cellIndex = this.view.getCellIndex(t.dom);
7394             this.split = t.dom;
7395             this.cm = this.grid.colModel;
7396             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7397                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7398             }
7399         }
7400     },
7401
7402     endDrag : function(e){
7403         this.view.headersDisabled = false;
7404         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7405         var diff = endX - this.startPos;
7406         // 
7407         var w = this.cm.getColumnWidth(this.cellIndex);
7408         if (!this.view.mainWrap) {
7409             w = 0;
7410         }
7411         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7412     },
7413
7414     autoOffset : function(){
7415         this.setDelta(0,0);
7416     }
7417 });/*
7418  * Based on:
7419  * Ext JS Library 1.1.1
7420  * Copyright(c) 2006-2007, Ext JS, LLC.
7421  *
7422  * Originally Released Under LGPL - original licence link has changed is not relivant.
7423  *
7424  * Fork - LGPL
7425  * <script type="text/javascript">
7426  */
7427
7428 /**
7429  * @class Roo.grid.AbstractSelectionModel
7430  * @extends Roo.util.Observable
7431  * @abstract
7432  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7433  * implemented by descendant classes.  This class should not be directly instantiated.
7434  * @constructor
7435  */
7436 Roo.grid.AbstractSelectionModel = function(){
7437     this.locked = false;
7438     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7439 };
7440
7441 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7442     /** @ignore Called by the grid automatically. Do not call directly. */
7443     init : function(grid){
7444         this.grid = grid;
7445         this.initEvents();
7446     },
7447
7448     /**
7449      * Locks the selections.
7450      */
7451     lock : function(){
7452         this.locked = true;
7453     },
7454
7455     /**
7456      * Unlocks the selections.
7457      */
7458     unlock : function(){
7459         this.locked = false;
7460     },
7461
7462     /**
7463      * Returns true if the selections are locked.
7464      * @return {Boolean}
7465      */
7466     isLocked : function(){
7467         return this.locked;
7468     }
7469 });/*
7470  * Based on:
7471  * Ext JS Library 1.1.1
7472  * Copyright(c) 2006-2007, Ext JS, LLC.
7473  *
7474  * Originally Released Under LGPL - original licence link has changed is not relivant.
7475  *
7476  * Fork - LGPL
7477  * <script type="text/javascript">
7478  */
7479 /**
7480  * @extends Roo.grid.AbstractSelectionModel
7481  * @class Roo.grid.RowSelectionModel
7482  * The default SelectionModel used by {@link Roo.grid.Grid}.
7483  * It supports multiple selections and keyboard selection/navigation. 
7484  * @constructor
7485  * @param {Object} config
7486  */
7487 Roo.grid.RowSelectionModel = function(config){
7488     Roo.apply(this, config);
7489     this.selections = new Roo.util.MixedCollection(false, function(o){
7490         return o.id;
7491     });
7492
7493     this.last = false;
7494     this.lastActive = false;
7495
7496     this.addEvents({
7497         /**
7498         * @event selectionchange
7499         * Fires when the selection changes
7500         * @param {SelectionModel} this
7501         */
7502        "selectionchange" : true,
7503        /**
7504         * @event afterselectionchange
7505         * Fires after the selection changes (eg. by key press or clicking)
7506         * @param {SelectionModel} this
7507         */
7508        "afterselectionchange" : true,
7509        /**
7510         * @event beforerowselect
7511         * Fires when a row is selected being selected, return false to cancel.
7512         * @param {SelectionModel} this
7513         * @param {Number} rowIndex The selected index
7514         * @param {Boolean} keepExisting False if other selections will be cleared
7515         */
7516        "beforerowselect" : true,
7517        /**
7518         * @event rowselect
7519         * Fires when a row is selected.
7520         * @param {SelectionModel} this
7521         * @param {Number} rowIndex The selected index
7522         * @param {Roo.data.Record} r The record
7523         */
7524        "rowselect" : true,
7525        /**
7526         * @event rowdeselect
7527         * Fires when a row is deselected.
7528         * @param {SelectionModel} this
7529         * @param {Number} rowIndex The selected index
7530         */
7531         "rowdeselect" : true
7532     });
7533     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7534     this.locked = false;
7535 };
7536
7537 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7538     /**
7539      * @cfg {Boolean} singleSelect
7540      * True to allow selection of only one row at a time (defaults to false)
7541      */
7542     singleSelect : false,
7543
7544     // private
7545     initEvents : function(){
7546
7547         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7548             this.grid.on("mousedown", this.handleMouseDown, this);
7549         }else{ // allow click to work like normal
7550             this.grid.on("rowclick", this.handleDragableRowClick, this);
7551         }
7552         // bootstrap does not have a view..
7553         var view = this.grid.view ? this.grid.view : this.grid;
7554         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7555             "up" : function(e){
7556                 if(!e.shiftKey){
7557                     this.selectPrevious(e.shiftKey);
7558                 }else if(this.last !== false && this.lastActive !== false){
7559                     var last = this.last;
7560                     this.selectRange(this.last,  this.lastActive-1);
7561                     view.focusRow(this.lastActive);
7562                     if(last !== false){
7563                         this.last = last;
7564                     }
7565                 }else{
7566                     this.selectFirstRow();
7567                 }
7568                 this.fireEvent("afterselectionchange", this);
7569             },
7570             "down" : function(e){
7571                 if(!e.shiftKey){
7572                     this.selectNext(e.shiftKey);
7573                 }else if(this.last !== false && this.lastActive !== false){
7574                     var last = this.last;
7575                     this.selectRange(this.last,  this.lastActive+1);
7576                     view.focusRow(this.lastActive);
7577                     if(last !== false){
7578                         this.last = last;
7579                     }
7580                 }else{
7581                     this.selectFirstRow();
7582                 }
7583                 this.fireEvent("afterselectionchange", this);
7584             },
7585             scope: this
7586         });
7587
7588          
7589         view.on("refresh", this.onRefresh, this);
7590         view.on("rowupdated", this.onRowUpdated, this);
7591         view.on("rowremoved", this.onRemove, this);
7592     },
7593
7594     // private
7595     onRefresh : function(){
7596         var ds = this.grid.ds, i, v = this.grid.view;
7597         var s = this.selections;
7598         s.each(function(r){
7599             if((i = ds.indexOfId(r.id)) != -1){
7600                 v.onRowSelect(i);
7601                 s.add(ds.getAt(i)); // updating the selection relate data
7602             }else{
7603                 s.remove(r);
7604             }
7605         });
7606     },
7607
7608     // private
7609     onRemove : function(v, index, r){
7610         this.selections.remove(r);
7611     },
7612
7613     // private
7614     onRowUpdated : function(v, index, r){
7615         if(this.isSelected(r)){
7616             v.onRowSelect(index);
7617         }
7618     },
7619
7620     /**
7621      * Select records.
7622      * @param {Array} records The records to select
7623      * @param {Boolean} keepExisting (optional) True to keep existing selections
7624      */
7625     selectRecords : function(records, keepExisting){
7626         if(!keepExisting){
7627             this.clearSelections();
7628         }
7629         var ds = this.grid.ds;
7630         for(var i = 0, len = records.length; i < len; i++){
7631             this.selectRow(ds.indexOf(records[i]), true);
7632         }
7633     },
7634
7635     /**
7636      * Gets the number of selected rows.
7637      * @return {Number}
7638      */
7639     getCount : function(){
7640         return this.selections.length;
7641     },
7642
7643     /**
7644      * Selects the first row in the grid.
7645      */
7646     selectFirstRow : function(){
7647         this.selectRow(0);
7648     },
7649
7650     /**
7651      * Select the last row.
7652      * @param {Boolean} keepExisting (optional) True to keep existing selections
7653      */
7654     selectLastRow : function(keepExisting){
7655         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7656     },
7657
7658     /**
7659      * Selects the row immediately following the last selected row.
7660      * @param {Boolean} keepExisting (optional) True to keep existing selections
7661      */
7662     selectNext : function(keepExisting){
7663         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7664             this.selectRow(this.last+1, keepExisting);
7665             var view = this.grid.view ? this.grid.view : this.grid;
7666             view.focusRow(this.last);
7667         }
7668     },
7669
7670     /**
7671      * Selects the row that precedes the last selected row.
7672      * @param {Boolean} keepExisting (optional) True to keep existing selections
7673      */
7674     selectPrevious : function(keepExisting){
7675         if(this.last){
7676             this.selectRow(this.last-1, keepExisting);
7677             var view = this.grid.view ? this.grid.view : this.grid;
7678             view.focusRow(this.last);
7679         }
7680     },
7681
7682     /**
7683      * Returns the selected records
7684      * @return {Array} Array of selected records
7685      */
7686     getSelections : function(){
7687         return [].concat(this.selections.items);
7688     },
7689
7690     /**
7691      * Returns the first selected record.
7692      * @return {Record}
7693      */
7694     getSelected : function(){
7695         return this.selections.itemAt(0);
7696     },
7697
7698
7699     /**
7700      * Clears all selections.
7701      */
7702     clearSelections : function(fast){
7703         if(this.locked) {
7704             return;
7705         }
7706         if(fast !== true){
7707             var ds = this.grid.ds;
7708             var s = this.selections;
7709             s.each(function(r){
7710                 this.deselectRow(ds.indexOfId(r.id));
7711             }, this);
7712             s.clear();
7713         }else{
7714             this.selections.clear();
7715         }
7716         this.last = false;
7717     },
7718
7719
7720     /**
7721      * Selects all rows.
7722      */
7723     selectAll : function(){
7724         if(this.locked) {
7725             return;
7726         }
7727         this.selections.clear();
7728         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7729             this.selectRow(i, true);
7730         }
7731     },
7732
7733     /**
7734      * Returns True if there is a selection.
7735      * @return {Boolean}
7736      */
7737     hasSelection : function(){
7738         return this.selections.length > 0;
7739     },
7740
7741     /**
7742      * Returns True if the specified row is selected.
7743      * @param {Number/Record} record The record or index of the record to check
7744      * @return {Boolean}
7745      */
7746     isSelected : function(index){
7747         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7748         return (r && this.selections.key(r.id) ? true : false);
7749     },
7750
7751     /**
7752      * Returns True if the specified record id is selected.
7753      * @param {String} id The id of record to check
7754      * @return {Boolean}
7755      */
7756     isIdSelected : function(id){
7757         return (this.selections.key(id) ? true : false);
7758     },
7759
7760     // private
7761     handleMouseDown : function(e, t)
7762     {
7763         var view = this.grid.view ? this.grid.view : this.grid;
7764         var rowIndex;
7765         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7766             return;
7767         };
7768         if(e.shiftKey && this.last !== false){
7769             var last = this.last;
7770             this.selectRange(last, rowIndex, e.ctrlKey);
7771             this.last = last; // reset the last
7772             view.focusRow(rowIndex);
7773         }else{
7774             var isSelected = this.isSelected(rowIndex);
7775             if(e.button !== 0 && isSelected){
7776                 view.focusRow(rowIndex);
7777             }else if(e.ctrlKey && isSelected){
7778                 this.deselectRow(rowIndex);
7779             }else if(!isSelected){
7780                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7781                 view.focusRow(rowIndex);
7782             }
7783         }
7784         this.fireEvent("afterselectionchange", this);
7785     },
7786     // private
7787     handleDragableRowClick :  function(grid, rowIndex, e) 
7788     {
7789         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7790             this.selectRow(rowIndex, false);
7791             var view = this.grid.view ? this.grid.view : this.grid;
7792             view.focusRow(rowIndex);
7793              this.fireEvent("afterselectionchange", this);
7794         }
7795     },
7796     
7797     /**
7798      * Selects multiple rows.
7799      * @param {Array} rows Array of the indexes of the row to select
7800      * @param {Boolean} keepExisting (optional) True to keep existing selections
7801      */
7802     selectRows : function(rows, keepExisting){
7803         if(!keepExisting){
7804             this.clearSelections();
7805         }
7806         for(var i = 0, len = rows.length; i < len; i++){
7807             this.selectRow(rows[i], true);
7808         }
7809     },
7810
7811     /**
7812      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7813      * @param {Number} startRow The index of the first row in the range
7814      * @param {Number} endRow The index of the last row in the range
7815      * @param {Boolean} keepExisting (optional) True to retain existing selections
7816      */
7817     selectRange : function(startRow, endRow, keepExisting){
7818         if(this.locked) {
7819             return;
7820         }
7821         if(!keepExisting){
7822             this.clearSelections();
7823         }
7824         if(startRow <= endRow){
7825             for(var i = startRow; i <= endRow; i++){
7826                 this.selectRow(i, true);
7827             }
7828         }else{
7829             for(var i = startRow; i >= endRow; i--){
7830                 this.selectRow(i, true);
7831             }
7832         }
7833     },
7834
7835     /**
7836      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7837      * @param {Number} startRow The index of the first row in the range
7838      * @param {Number} endRow The index of the last row in the range
7839      */
7840     deselectRange : function(startRow, endRow, preventViewNotify){
7841         if(this.locked) {
7842             return;
7843         }
7844         for(var i = startRow; i <= endRow; i++){
7845             this.deselectRow(i, preventViewNotify);
7846         }
7847     },
7848
7849     /**
7850      * Selects a row.
7851      * @param {Number} row The index of the row to select
7852      * @param {Boolean} keepExisting (optional) True to keep existing selections
7853      */
7854     selectRow : function(index, keepExisting, preventViewNotify){
7855         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7856             return;
7857         }
7858         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7859             if(!keepExisting || this.singleSelect){
7860                 this.clearSelections();
7861             }
7862             var r = this.grid.ds.getAt(index);
7863             this.selections.add(r);
7864             this.last = this.lastActive = index;
7865             if(!preventViewNotify){
7866                 var view = this.grid.view ? this.grid.view : this.grid;
7867                 view.onRowSelect(index);
7868             }
7869             this.fireEvent("rowselect", this, index, r);
7870             this.fireEvent("selectionchange", this);
7871         }
7872     },
7873
7874     /**
7875      * Deselects a row.
7876      * @param {Number} row The index of the row to deselect
7877      */
7878     deselectRow : function(index, preventViewNotify){
7879         if(this.locked) {
7880             return;
7881         }
7882         if(this.last == index){
7883             this.last = false;
7884         }
7885         if(this.lastActive == index){
7886             this.lastActive = false;
7887         }
7888         var r = this.grid.ds.getAt(index);
7889         this.selections.remove(r);
7890         if(!preventViewNotify){
7891             var view = this.grid.view ? this.grid.view : this.grid;
7892             view.onRowDeselect(index);
7893         }
7894         this.fireEvent("rowdeselect", this, index);
7895         this.fireEvent("selectionchange", this);
7896     },
7897
7898     // private
7899     restoreLast : function(){
7900         if(this._last){
7901             this.last = this._last;
7902         }
7903     },
7904
7905     // private
7906     acceptsNav : function(row, col, cm){
7907         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7908     },
7909
7910     // private
7911     onEditorKey : function(field, e){
7912         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7913         if(k == e.TAB){
7914             e.stopEvent();
7915             ed.completeEdit();
7916             if(e.shiftKey){
7917                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7918             }else{
7919                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7920             }
7921         }else if(k == e.ENTER && !e.ctrlKey){
7922             e.stopEvent();
7923             ed.completeEdit();
7924             if(e.shiftKey){
7925                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7926             }else{
7927                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7928             }
7929         }else if(k == e.ESC){
7930             ed.cancelEdit();
7931         }
7932         if(newCell){
7933             g.startEditing(newCell[0], newCell[1]);
7934         }
7935     }
7936 });/*
7937  * Based on:
7938  * Ext JS Library 1.1.1
7939  * Copyright(c) 2006-2007, Ext JS, LLC.
7940  *
7941  * Originally Released Under LGPL - original licence link has changed is not relivant.
7942  *
7943  * Fork - LGPL
7944  * <script type="text/javascript">
7945  */
7946  
7947
7948 /**
7949  * @class Roo.grid.ColumnModel
7950  * @extends Roo.util.Observable
7951  * This is the default implementation of a ColumnModel used by the Grid. It defines
7952  * the columns in the grid.
7953  * <br>Usage:<br>
7954  <pre><code>
7955  var colModel = new Roo.grid.ColumnModel([
7956         {header: "Ticker", width: 60, sortable: true, locked: true},
7957         {header: "Company Name", width: 150, sortable: true},
7958         {header: "Market Cap.", width: 100, sortable: true},
7959         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7960         {header: "Employees", width: 100, sortable: true, resizable: false}
7961  ]);
7962  </code></pre>
7963  * <p>
7964  
7965  * The config options listed for this class are options which may appear in each
7966  * individual column definition.
7967  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7968  * @constructor
7969  * @param {Object} config An Array of column config objects. See this class's
7970  * config objects for details.
7971 */
7972 Roo.grid.ColumnModel = function(config){
7973         /**
7974      * The config passed into the constructor
7975      */
7976     this.config = []; //config;
7977     this.lookup = {};
7978
7979     // if no id, create one
7980     // if the column does not have a dataIndex mapping,
7981     // map it to the order it is in the config
7982     for(var i = 0, len = config.length; i < len; i++){
7983         this.addColumn(config[i]);
7984         
7985     }
7986
7987     /**
7988      * The width of columns which have no width specified (defaults to 100)
7989      * @type Number
7990      */
7991     this.defaultWidth = 100;
7992
7993     /**
7994      * Default sortable of columns which have no sortable specified (defaults to false)
7995      * @type Boolean
7996      */
7997     this.defaultSortable = false;
7998
7999     this.addEvents({
8000         /**
8001              * @event widthchange
8002              * Fires when the width of a column changes.
8003              * @param {ColumnModel} this
8004              * @param {Number} columnIndex The column index
8005              * @param {Number} newWidth The new width
8006              */
8007             "widthchange": true,
8008         /**
8009              * @event headerchange
8010              * Fires when the text of a header changes.
8011              * @param {ColumnModel} this
8012              * @param {Number} columnIndex The column index
8013              * @param {Number} newText The new header text
8014              */
8015             "headerchange": true,
8016         /**
8017              * @event hiddenchange
8018              * Fires when a column is hidden or "unhidden".
8019              * @param {ColumnModel} this
8020              * @param {Number} columnIndex The column index
8021              * @param {Boolean} hidden true if hidden, false otherwise
8022              */
8023             "hiddenchange": true,
8024             /**
8025          * @event columnmoved
8026          * Fires when a column is moved.
8027          * @param {ColumnModel} this
8028          * @param {Number} oldIndex
8029          * @param {Number} newIndex
8030          */
8031         "columnmoved" : true,
8032         /**
8033          * @event columlockchange
8034          * Fires when a column's locked state is changed
8035          * @param {ColumnModel} this
8036          * @param {Number} colIndex
8037          * @param {Boolean} locked true if locked
8038          */
8039         "columnlockchange" : true
8040     });
8041     Roo.grid.ColumnModel.superclass.constructor.call(this);
8042 };
8043 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8044     /**
8045      * @cfg {String} header The header text to display in the Grid view.
8046      */
8047         /**
8048      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8049      */
8050         /**
8051      * @cfg {String} smHeader Header at Bootsrap Small width
8052      */
8053         /**
8054      * @cfg {String} mdHeader Header at Bootsrap Medium width
8055      */
8056         /**
8057      * @cfg {String} lgHeader Header at Bootsrap Large width
8058      */
8059         /**
8060      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8061      */
8062     /**
8063      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8064      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8065      * specified, the column's index is used as an index into the Record's data Array.
8066      */
8067     /**
8068      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8069      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8070      */
8071     /**
8072      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8073      * Defaults to the value of the {@link #defaultSortable} property.
8074      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8075      */
8076     /**
8077      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8078      */
8079     /**
8080      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8081      */
8082     /**
8083      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8084      */
8085     /**
8086      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8087      */
8088     /**
8089      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8090      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8091      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8092      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8093      */
8094        /**
8095      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8096      */
8097     /**
8098      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8099      */
8100     /**
8101      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8102      */
8103     /**
8104      * @cfg {String} cursor (Optional)
8105      */
8106     /**
8107      * @cfg {String} tooltip (Optional)
8108      */
8109     /**
8110      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8111      */
8112     /**
8113      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115     /**
8116      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118     /**
8119      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8120      */
8121         /**
8122      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8123      */
8124     /**
8125      * Returns the id of the column at the specified index.
8126      * @param {Number} index The column index
8127      * @return {String} the id
8128      */
8129     getColumnId : function(index){
8130         return this.config[index].id;
8131     },
8132
8133     /**
8134      * Returns the column for a specified id.
8135      * @param {String} id The column id
8136      * @return {Object} the column
8137      */
8138     getColumnById : function(id){
8139         return this.lookup[id];
8140     },
8141
8142     
8143     /**
8144      * Returns the column Object for a specified dataIndex.
8145      * @param {String} dataIndex The column dataIndex
8146      * @return {Object|Boolean} the column or false if not found
8147      */
8148     getColumnByDataIndex: function(dataIndex){
8149         var index = this.findColumnIndex(dataIndex);
8150         return index > -1 ? this.config[index] : false;
8151     },
8152     
8153     /**
8154      * Returns the index for a specified column id.
8155      * @param {String} id The column id
8156      * @return {Number} the index, or -1 if not found
8157      */
8158     getIndexById : function(id){
8159         for(var i = 0, len = this.config.length; i < len; i++){
8160             if(this.config[i].id == id){
8161                 return i;
8162             }
8163         }
8164         return -1;
8165     },
8166     
8167     /**
8168      * Returns the index for a specified column dataIndex.
8169      * @param {String} dataIndex The column dataIndex
8170      * @return {Number} the index, or -1 if not found
8171      */
8172     
8173     findColumnIndex : function(dataIndex){
8174         for(var i = 0, len = this.config.length; i < len; i++){
8175             if(this.config[i].dataIndex == dataIndex){
8176                 return i;
8177             }
8178         }
8179         return -1;
8180     },
8181     
8182     
8183     moveColumn : function(oldIndex, newIndex){
8184         var c = this.config[oldIndex];
8185         this.config.splice(oldIndex, 1);
8186         this.config.splice(newIndex, 0, c);
8187         this.dataMap = null;
8188         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8189     },
8190
8191     isLocked : function(colIndex){
8192         return this.config[colIndex].locked === true;
8193     },
8194
8195     setLocked : function(colIndex, value, suppressEvent){
8196         if(this.isLocked(colIndex) == value){
8197             return;
8198         }
8199         this.config[colIndex].locked = value;
8200         if(!suppressEvent){
8201             this.fireEvent("columnlockchange", this, colIndex, value);
8202         }
8203     },
8204
8205     getTotalLockedWidth : function(){
8206         var totalWidth = 0;
8207         for(var i = 0; i < this.config.length; i++){
8208             if(this.isLocked(i) && !this.isHidden(i)){
8209                 this.totalWidth += this.getColumnWidth(i);
8210             }
8211         }
8212         return totalWidth;
8213     },
8214
8215     getLockedCount : function(){
8216         for(var i = 0, len = this.config.length; i < len; i++){
8217             if(!this.isLocked(i)){
8218                 return i;
8219             }
8220         }
8221         
8222         return this.config.length;
8223     },
8224
8225     /**
8226      * Returns the number of columns.
8227      * @return {Number}
8228      */
8229     getColumnCount : function(visibleOnly){
8230         if(visibleOnly === true){
8231             var c = 0;
8232             for(var i = 0, len = this.config.length; i < len; i++){
8233                 if(!this.isHidden(i)){
8234                     c++;
8235                 }
8236             }
8237             return c;
8238         }
8239         return this.config.length;
8240     },
8241
8242     /**
8243      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8244      * @param {Function} fn
8245      * @param {Object} scope (optional)
8246      * @return {Array} result
8247      */
8248     getColumnsBy : function(fn, scope){
8249         var r = [];
8250         for(var i = 0, len = this.config.length; i < len; i++){
8251             var c = this.config[i];
8252             if(fn.call(scope||this, c, i) === true){
8253                 r[r.length] = c;
8254             }
8255         }
8256         return r;
8257     },
8258
8259     /**
8260      * Returns true if the specified column is sortable.
8261      * @param {Number} col The column index
8262      * @return {Boolean}
8263      */
8264     isSortable : function(col){
8265         if(typeof this.config[col].sortable == "undefined"){
8266             return this.defaultSortable;
8267         }
8268         return this.config[col].sortable;
8269     },
8270
8271     /**
8272      * Returns the rendering (formatting) function defined for the column.
8273      * @param {Number} col The column index.
8274      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8275      */
8276     getRenderer : function(col){
8277         if(!this.config[col].renderer){
8278             return Roo.grid.ColumnModel.defaultRenderer;
8279         }
8280         return this.config[col].renderer;
8281     },
8282
8283     /**
8284      * Sets the rendering (formatting) function for a column.
8285      * @param {Number} col The column index
8286      * @param {Function} fn The function to use to process the cell's raw data
8287      * to return HTML markup for the grid view. The render function is called with
8288      * the following parameters:<ul>
8289      * <li>Data value.</li>
8290      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8291      * <li>css A CSS style string to apply to the table cell.</li>
8292      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8293      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8294      * <li>Row index</li>
8295      * <li>Column index</li>
8296      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8297      */
8298     setRenderer : function(col, fn){
8299         this.config[col].renderer = fn;
8300     },
8301
8302     /**
8303      * Returns the width for the specified column.
8304      * @param {Number} col The column index
8305      * @param (optional) {String} gridSize bootstrap width size.
8306      * @return {Number}
8307      */
8308     getColumnWidth : function(col, gridSize)
8309         {
8310                 var cfg = this.config[col];
8311                 
8312                 if (typeof(gridSize) == 'undefined') {
8313                         return cfg.width * 1 || this.defaultWidth;
8314                 }
8315                 if (gridSize === false) { // if we set it..
8316                         return cfg.width || false;
8317                 }
8318                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8319                 
8320                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8321                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8322                                 continue;
8323                         }
8324                         return cfg[ sizes[i] ];
8325                 }
8326                 return 1;
8327                 
8328     },
8329
8330     /**
8331      * Sets the width for a column.
8332      * @param {Number} col The column index
8333      * @param {Number} width The new width
8334      */
8335     setColumnWidth : function(col, width, suppressEvent){
8336         this.config[col].width = width;
8337         this.totalWidth = null;
8338         if(!suppressEvent){
8339              this.fireEvent("widthchange", this, col, width);
8340         }
8341     },
8342
8343     /**
8344      * Returns the total width of all columns.
8345      * @param {Boolean} includeHidden True to include hidden column widths
8346      * @return {Number}
8347      */
8348     getTotalWidth : function(includeHidden){
8349         if(!this.totalWidth){
8350             this.totalWidth = 0;
8351             for(var i = 0, len = this.config.length; i < len; i++){
8352                 if(includeHidden || !this.isHidden(i)){
8353                     this.totalWidth += this.getColumnWidth(i);
8354                 }
8355             }
8356         }
8357         return this.totalWidth;
8358     },
8359
8360     /**
8361      * Returns the header for the specified column.
8362      * @param {Number} col The column index
8363      * @return {String}
8364      */
8365     getColumnHeader : function(col){
8366         return this.config[col].header;
8367     },
8368
8369     /**
8370      * Sets the header for a column.
8371      * @param {Number} col The column index
8372      * @param {String} header The new header
8373      */
8374     setColumnHeader : function(col, header){
8375         this.config[col].header = header;
8376         this.fireEvent("headerchange", this, col, header);
8377     },
8378
8379     /**
8380      * Returns the tooltip for the specified column.
8381      * @param {Number} col The column index
8382      * @return {String}
8383      */
8384     getColumnTooltip : function(col){
8385             return this.config[col].tooltip;
8386     },
8387     /**
8388      * Sets the tooltip for a column.
8389      * @param {Number} col The column index
8390      * @param {String} tooltip The new tooltip
8391      */
8392     setColumnTooltip : function(col, tooltip){
8393             this.config[col].tooltip = tooltip;
8394     },
8395
8396     /**
8397      * Returns the dataIndex for the specified column.
8398      * @param {Number} col The column index
8399      * @return {Number}
8400      */
8401     getDataIndex : function(col){
8402         return this.config[col].dataIndex;
8403     },
8404
8405     /**
8406      * Sets the dataIndex for a column.
8407      * @param {Number} col The column index
8408      * @param {Number} dataIndex The new dataIndex
8409      */
8410     setDataIndex : function(col, dataIndex){
8411         this.config[col].dataIndex = dataIndex;
8412     },
8413
8414     
8415     
8416     /**
8417      * Returns true if the cell is editable.
8418      * @param {Number} colIndex The column index
8419      * @param {Number} rowIndex The row index - this is nto actually used..?
8420      * @return {Boolean}
8421      */
8422     isCellEditable : function(colIndex, rowIndex){
8423         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8424     },
8425
8426     /**
8427      * Returns the editor defined for the cell/column.
8428      * return false or null to disable editing.
8429      * @param {Number} colIndex The column index
8430      * @param {Number} rowIndex The row index
8431      * @return {Object}
8432      */
8433     getCellEditor : function(colIndex, rowIndex){
8434         return this.config[colIndex].editor;
8435     },
8436
8437     /**
8438      * Sets if a column is editable.
8439      * @param {Number} col The column index
8440      * @param {Boolean} editable True if the column is editable
8441      */
8442     setEditable : function(col, editable){
8443         this.config[col].editable = editable;
8444     },
8445
8446
8447     /**
8448      * Returns true if the column is hidden.
8449      * @param {Number} colIndex The column index
8450      * @return {Boolean}
8451      */
8452     isHidden : function(colIndex){
8453         return this.config[colIndex].hidden;
8454     },
8455
8456
8457     /**
8458      * Returns true if the column width cannot be changed
8459      */
8460     isFixed : function(colIndex){
8461         return this.config[colIndex].fixed;
8462     },
8463
8464     /**
8465      * Returns true if the column can be resized
8466      * @return {Boolean}
8467      */
8468     isResizable : function(colIndex){
8469         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8470     },
8471     /**
8472      * Sets if a column is hidden.
8473      * @param {Number} colIndex The column index
8474      * @param {Boolean} hidden True if the column is hidden
8475      */
8476     setHidden : function(colIndex, hidden){
8477         this.config[colIndex].hidden = hidden;
8478         this.totalWidth = null;
8479         this.fireEvent("hiddenchange", this, colIndex, hidden);
8480     },
8481
8482     /**
8483      * Sets the editor for a column.
8484      * @param {Number} col The column index
8485      * @param {Object} editor The editor object
8486      */
8487     setEditor : function(col, editor){
8488         this.config[col].editor = editor;
8489     },
8490     /**
8491      * Add a column (experimental...) - defaults to adding to the end..
8492      * @param {Object} config 
8493     */
8494     addColumn : function(c)
8495     {
8496     
8497         var i = this.config.length;
8498         this.config[i] = c;
8499         
8500         if(typeof c.dataIndex == "undefined"){
8501             c.dataIndex = i;
8502         }
8503         if(typeof c.renderer == "string"){
8504             c.renderer = Roo.util.Format[c.renderer];
8505         }
8506         if(typeof c.id == "undefined"){
8507             c.id = Roo.id();
8508         }
8509         if(c.editor && c.editor.xtype){
8510             c.editor  = Roo.factory(c.editor, Roo.grid);
8511         }
8512         if(c.editor && c.editor.isFormField){
8513             c.editor = new Roo.grid.GridEditor(c.editor);
8514         }
8515         this.lookup[c.id] = c;
8516     }
8517     
8518 });
8519
8520 Roo.grid.ColumnModel.defaultRenderer = function(value)
8521 {
8522     if(typeof value == "object") {
8523         return value;
8524     }
8525         if(typeof value == "string" && value.length < 1){
8526             return "&#160;";
8527         }
8528     
8529         return String.format("{0}", value);
8530 };
8531
8532 // Alias for backwards compatibility
8533 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8534 /*
8535  * Based on:
8536  * Ext JS Library 1.1.1
8537  * Copyright(c) 2006-2007, Ext JS, LLC.
8538  *
8539  * Originally Released Under LGPL - original licence link has changed is not relivant.
8540  *
8541  * Fork - LGPL
8542  * <script type="text/javascript">
8543  */
8544  
8545 /**
8546  * @class Roo.LoadMask
8547  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8548  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8549  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8550  * element's UpdateManager load indicator and will be destroyed after the initial load.
8551  * @constructor
8552  * Create a new LoadMask
8553  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8554  * @param {Object} config The config object
8555  */
8556 Roo.LoadMask = function(el, config){
8557     this.el = Roo.get(el);
8558     Roo.apply(this, config);
8559     if(this.store){
8560         this.store.on('beforeload', this.onBeforeLoad, this);
8561         this.store.on('load', this.onLoad, this);
8562         this.store.on('loadexception', this.onLoadException, this);
8563         this.removeMask = false;
8564     }else{
8565         var um = this.el.getUpdateManager();
8566         um.showLoadIndicator = false; // disable the default indicator
8567         um.on('beforeupdate', this.onBeforeLoad, this);
8568         um.on('update', this.onLoad, this);
8569         um.on('failure', this.onLoad, this);
8570         this.removeMask = true;
8571     }
8572 };
8573
8574 Roo.LoadMask.prototype = {
8575     /**
8576      * @cfg {Boolean} removeMask
8577      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8578      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8579      */
8580     removeMask : false,
8581     /**
8582      * @cfg {String} msg
8583      * The text to display in a centered loading message box (defaults to 'Loading...')
8584      */
8585     msg : 'Loading...',
8586     /**
8587      * @cfg {String} msgCls
8588      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8589      */
8590     msgCls : 'x-mask-loading',
8591
8592     /**
8593      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8594      * @type Boolean
8595      */
8596     disabled: false,
8597
8598     /**
8599      * Disables the mask to prevent it from being displayed
8600      */
8601     disable : function(){
8602        this.disabled = true;
8603     },
8604
8605     /**
8606      * Enables the mask so that it can be displayed
8607      */
8608     enable : function(){
8609         this.disabled = false;
8610     },
8611     
8612     onLoadException : function()
8613     {
8614         Roo.log(arguments);
8615         
8616         if (typeof(arguments[3]) != 'undefined') {
8617             Roo.MessageBox.alert("Error loading",arguments[3]);
8618         } 
8619         /*
8620         try {
8621             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8622                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8623             }   
8624         } catch(e) {
8625             
8626         }
8627         */
8628     
8629         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8630     },
8631     // private
8632     onLoad : function()
8633     {
8634         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8635     },
8636
8637     // private
8638     onBeforeLoad : function(){
8639         if(!this.disabled){
8640             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8641         }
8642     },
8643
8644     // private
8645     destroy : function(){
8646         if(this.store){
8647             this.store.un('beforeload', this.onBeforeLoad, this);
8648             this.store.un('load', this.onLoad, this);
8649             this.store.un('loadexception', this.onLoadException, this);
8650         }else{
8651             var um = this.el.getUpdateManager();
8652             um.un('beforeupdate', this.onBeforeLoad, this);
8653             um.un('update', this.onLoad, this);
8654             um.un('failure', this.onLoad, this);
8655         }
8656     }
8657 };/**
8658  * @class Roo.bootstrap.Table
8659  * @licence LGBL
8660  * @extends Roo.bootstrap.Component
8661  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8662  * Similar to Roo.grid.Grid
8663  * <pre><code>
8664  var table = Roo.factory({
8665     xtype : 'Table',
8666     xns : Roo.bootstrap,
8667     autoSizeColumns: true,
8668     
8669     
8670     store : {
8671         xtype : 'Store',
8672         xns : Roo.data,
8673         remoteSort : true,
8674         sortInfo : { direction : 'ASC', field: 'name' },
8675         proxy : {
8676            xtype : 'HttpProxy',
8677            xns : Roo.data,
8678            method : 'GET',
8679            url : 'https://example.com/some.data.url.json'
8680         },
8681         reader : {
8682            xtype : 'JsonReader',
8683            xns : Roo.data,
8684            fields : [ 'id', 'name', whatever' ],
8685            id : 'id',
8686            root : 'data'
8687         }
8688     },
8689     cm : [
8690         {
8691             xtype : 'ColumnModel',
8692             xns : Roo.grid,
8693             align : 'center',
8694             cursor : 'pointer',
8695             dataIndex : 'is_in_group',
8696             header : "Name",
8697             sortable : true,
8698             renderer : function(v, x , r) {  
8699             
8700                 return String.format("{0}", v)
8701             }
8702             width : 3
8703         } // more columns..
8704     ],
8705     selModel : {
8706         xtype : 'RowSelectionModel',
8707         xns : Roo.bootstrap.Table
8708         // you can add listeners to catch selection change here....
8709     }
8710      
8711
8712  });
8713  // set any options
8714  grid.render(Roo.get("some-div"));
8715 </code></pre>
8716
8717 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8718
8719
8720
8721  *
8722  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8723  * @cfg {Roo.data.Store} store The data store to use
8724  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8725  * 
8726  * @cfg {String} cls table class
8727  *
8728  * 
8729  * @cfg {boolean} striped Should the rows be alternative striped
8730  * @cfg {boolean} bordered Add borders to the table
8731  * @cfg {boolean} hover Add hover highlighting
8732  * @cfg {boolean} condensed Format condensed
8733  * @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,
8734  *                also adds table-responsive (see bootstrap docs for details)
8735  * @cfg {Boolean} loadMask (true|false) default false
8736  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8737  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8738  * @cfg {Boolean} rowSelection (true|false) default false
8739  * @cfg {Boolean} cellSelection (true|false) default false
8740  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8741  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8742  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8743  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8744  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8745  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8746  * 
8747  * @constructor
8748  * Create a new Table
8749  * @param {Object} config The config object
8750  */
8751
8752 Roo.bootstrap.Table = function(config)
8753 {
8754     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8755      
8756     // BC...
8757     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8758     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8759     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8760     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8761     
8762     this.view = this; // compat with grid.
8763     
8764     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8765     if (this.sm) {
8766         this.sm.grid = this;
8767         this.selModel = Roo.factory(this.sm, Roo.grid);
8768         this.sm = this.selModel;
8769         this.sm.xmodule = this.xmodule || false;
8770     }
8771     
8772     if (this.cm && typeof(this.cm.config) == 'undefined') {
8773         this.colModel = new Roo.grid.ColumnModel(this.cm);
8774         this.cm = this.colModel;
8775         this.cm.xmodule = this.xmodule || false;
8776     }
8777     if (this.store) {
8778         this.store= Roo.factory(this.store, Roo.data);
8779         this.ds = this.store;
8780         this.ds.xmodule = this.xmodule || false;
8781          
8782     }
8783     if (this.footer && this.store) {
8784         this.footer.dataSource = this.ds;
8785         this.footer = Roo.factory(this.footer);
8786     }
8787     
8788     /** @private */
8789     this.addEvents({
8790         /**
8791          * @event cellclick
8792          * Fires when a cell is clicked
8793          * @param {Roo.bootstrap.Table} this
8794          * @param {Roo.Element} el
8795          * @param {Number} rowIndex
8796          * @param {Number} columnIndex
8797          * @param {Roo.EventObject} e
8798          */
8799         "cellclick" : true,
8800         /**
8801          * @event celldblclick
8802          * Fires when a cell is double clicked
8803          * @param {Roo.bootstrap.Table} this
8804          * @param {Roo.Element} el
8805          * @param {Number} rowIndex
8806          * @param {Number} columnIndex
8807          * @param {Roo.EventObject} e
8808          */
8809         "celldblclick" : true,
8810         /**
8811          * @event rowclick
8812          * Fires when a row is clicked
8813          * @param {Roo.bootstrap.Table} this
8814          * @param {Roo.Element} el
8815          * @param {Number} rowIndex
8816          * @param {Roo.EventObject} e
8817          */
8818         "rowclick" : true,
8819         /**
8820          * @event rowdblclick
8821          * Fires when a row is double clicked
8822          * @param {Roo.bootstrap.Table} this
8823          * @param {Roo.Element} el
8824          * @param {Number} rowIndex
8825          * @param {Roo.EventObject} e
8826          */
8827         "rowdblclick" : true,
8828         /**
8829          * @event mouseover
8830          * Fires when a mouseover occur
8831          * @param {Roo.bootstrap.Table} this
8832          * @param {Roo.Element} el
8833          * @param {Number} rowIndex
8834          * @param {Number} columnIndex
8835          * @param {Roo.EventObject} e
8836          */
8837         "mouseover" : true,
8838         /**
8839          * @event mouseout
8840          * Fires when a mouseout occur
8841          * @param {Roo.bootstrap.Table} this
8842          * @param {Roo.Element} el
8843          * @param {Number} rowIndex
8844          * @param {Number} columnIndex
8845          * @param {Roo.EventObject} e
8846          */
8847         "mouseout" : true,
8848         /**
8849          * @event rowclass
8850          * Fires when a row is rendered, so you can change add a style to it.
8851          * @param {Roo.bootstrap.Table} this
8852          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8853          */
8854         'rowclass' : true,
8855           /**
8856          * @event rowsrendered
8857          * Fires when all the  rows have been rendered
8858          * @param {Roo.bootstrap.Table} this
8859          */
8860         'rowsrendered' : true,
8861         /**
8862          * @event contextmenu
8863          * The raw contextmenu event for the entire grid.
8864          * @param {Roo.EventObject} e
8865          */
8866         "contextmenu" : true,
8867         /**
8868          * @event rowcontextmenu
8869          * Fires when a row is right clicked
8870          * @param {Roo.bootstrap.Table} this
8871          * @param {Number} rowIndex
8872          * @param {Roo.EventObject} e
8873          */
8874         "rowcontextmenu" : true,
8875         /**
8876          * @event cellcontextmenu
8877          * Fires when a cell is right clicked
8878          * @param {Roo.bootstrap.Table} this
8879          * @param {Number} rowIndex
8880          * @param {Number} cellIndex
8881          * @param {Roo.EventObject} e
8882          */
8883          "cellcontextmenu" : true,
8884          /**
8885          * @event headercontextmenu
8886          * Fires when a header is right clicked
8887          * @param {Roo.bootstrap.Table} this
8888          * @param {Number} columnIndex
8889          * @param {Roo.EventObject} e
8890          */
8891         "headercontextmenu" : true,
8892         /**
8893          * @event mousedown
8894          * The raw mousedown event for the entire grid.
8895          * @param {Roo.EventObject} e
8896          */
8897         "mousedown" : true
8898         
8899     });
8900 };
8901
8902 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8903     
8904     cls: false,
8905     
8906     striped : false,
8907     scrollBody : false,
8908     bordered: false,
8909     hover:  false,
8910     condensed : false,
8911     responsive : false,
8912     sm : false,
8913     cm : false,
8914     store : false,
8915     loadMask : false,
8916     footerShow : true,
8917     headerShow : true,
8918     enableColumnResize: true,
8919   
8920     rowSelection : false,
8921     cellSelection : false,
8922     layout : false,
8923
8924     minColumnWidth : 50,
8925     
8926     // Roo.Element - the tbody
8927     bodyEl: false,  // <tbody> Roo.Element - thead element    
8928     headEl: false,  // <thead> Roo.Element - thead element
8929     resizeProxy : false, // proxy element for dragging?
8930
8931
8932     
8933     container: false, // used by gridpanel...
8934     
8935     lazyLoad : false,
8936     
8937     CSS : Roo.util.CSS,
8938     
8939     auto_hide_footer : false,
8940     
8941     view: false, // actually points to this..
8942     
8943     getAutoCreate : function()
8944     {
8945         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8946         
8947         cfg = {
8948             tag: 'table',
8949             cls : 'table', 
8950             cn : []
8951         };
8952         // this get's auto added by panel.Grid
8953         if (this.scrollBody) {
8954             cfg.cls += ' table-body-fixed';
8955         }    
8956         if (this.striped) {
8957             cfg.cls += ' table-striped';
8958         }
8959         
8960         if (this.hover) {
8961             cfg.cls += ' table-hover';
8962         }
8963         if (this.bordered) {
8964             cfg.cls += ' table-bordered';
8965         }
8966         if (this.condensed) {
8967             cfg.cls += ' table-condensed';
8968         }
8969         
8970         if (this.responsive) {
8971             cfg.cls += ' table-responsive';
8972         }
8973         
8974         if (this.cls) {
8975             cfg.cls+=  ' ' +this.cls;
8976         }
8977         
8978         
8979         
8980         if (this.layout) {
8981             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8982         }
8983         
8984         if(this.store || this.cm){
8985             if(this.headerShow){
8986                 cfg.cn.push(this.renderHeader());
8987             }
8988             
8989             cfg.cn.push(this.renderBody());
8990             
8991             if(this.footerShow){
8992                 cfg.cn.push(this.renderFooter());
8993             }
8994             // where does this come from?
8995             //cfg.cls+=  ' TableGrid';
8996         }
8997         
8998         return { cn : [ cfg ] };
8999     },
9000     
9001     initEvents : function()
9002     {   
9003         if(!this.store || !this.cm){
9004             return;
9005         }
9006         if (this.selModel) {
9007             this.selModel.initEvents();
9008         }
9009         
9010         
9011         //Roo.log('initEvents with ds!!!!');
9012         
9013         this.bodyEl = this.el.select('tbody', true).first();
9014         this.headEl = this.el.select('thead', true).first();
9015         this.mainFoot = this.el.select('tfoot', true).first();
9016         
9017         
9018         
9019         
9020         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9021             e.on('click', this.sort, this);
9022         }, this);
9023         
9024         
9025         // why is this done????? = it breaks dialogs??
9026         //this.parent().el.setStyle('position', 'relative');
9027         
9028         
9029         if (this.footer) {
9030             this.footer.parentId = this.id;
9031             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9032             
9033             if(this.lazyLoad){
9034                 this.el.select('tfoot tr td').first().addClass('hide');
9035             }
9036         } 
9037         
9038         if(this.loadMask) {
9039             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9040         }
9041         
9042         this.store.on('load', this.onLoad, this);
9043         this.store.on('beforeload', this.onBeforeLoad, this);
9044         this.store.on('update', this.onUpdate, this);
9045         this.store.on('add', this.onAdd, this);
9046         this.store.on("clear", this.clear, this);
9047         
9048         this.el.on("contextmenu", this.onContextMenu, this);
9049         
9050         
9051         this.cm.on("headerchange", this.onHeaderChange, this);
9052         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9053
9054  //?? does bodyEl get replaced on render?
9055         this.bodyEl.on("click", this.onClick, this);
9056         this.bodyEl.on("dblclick", this.onDblClick, this);        
9057         this.bodyEl.on('scroll', this.onBodyScroll, this);
9058
9059         // guessing mainbody will work - this relays usually caught by selmodel at present.
9060         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9061   
9062   
9063         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9064         
9065   
9066         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9067             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9068         }
9069         
9070         this.initCSS();
9071     },
9072     // Compatibility with grid - we implement all the view features at present.
9073     getView : function()
9074     {
9075         return this;
9076     },
9077     
9078     initCSS : function()
9079     {
9080         
9081         
9082         var cm = this.cm, styles = [];
9083         this.CSS.removeStyleSheet(this.id + '-cssrules');
9084         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9085         // we can honour xs/sm/md/xl  as widths...
9086         // we first have to decide what widht we are currently at...
9087         var sz = Roo.getGridSize();
9088         
9089         var total = 0;
9090         var last = -1;
9091         var cols = []; // visable cols.
9092         var total_abs = 0;
9093         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9094             var w = cm.getColumnWidth(i, false);
9095             if(cm.isHidden(i)){
9096                 cols.push( { rel : false, abs : 0 });
9097                 continue;
9098             }
9099             if (w !== false) {
9100                 cols.push( { rel : false, abs : w });
9101                 total_abs += w;
9102                 last = i; // not really..
9103                 continue;
9104             }
9105             var w = cm.getColumnWidth(i, sz);
9106             if (w > 0) {
9107                 last = i
9108             }
9109             total += w;
9110             cols.push( { rel : w, abs : false });
9111         }
9112         
9113         var avail = this.bodyEl.dom.clientWidth - total_abs;
9114         
9115         var unitWidth = Math.floor(avail / total);
9116         var rem = avail - (unitWidth * total);
9117         
9118         var hidden, width, pos = 0 , splithide , left;
9119         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9120             
9121             hidden = 'display:none;';
9122             left = '';
9123             width  = 'width:0px;';
9124             splithide = '';
9125             if(!cm.isHidden(i)){
9126                 hidden = '';
9127                 
9128                 
9129                 // we can honour xs/sm/md/xl ?
9130                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9131                 if (w===0) {
9132                     hidden = 'display:none;';
9133                 }
9134                 // width should return a small number...
9135                 if (i == last) {
9136                     w+=rem; // add the remaining with..
9137                 }
9138                 pos += w;
9139                 left = "left:" + (pos -4) + "px;";
9140                 width = "width:" + w+ "px;";
9141                 
9142             }
9143             if (this.responsive) {
9144                 width = '';
9145                 left = '';
9146                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9147                 splithide = 'display: none;';
9148             }
9149             
9150             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9151             if (this.headEl) {
9152                 if (i == last) {
9153                     splithide = 'display:none;';
9154                 }
9155                 
9156                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9157                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9158                 );
9159             }
9160             
9161         }
9162         //Roo.log(styles.join(''));
9163         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9164         
9165     },
9166     
9167     
9168     
9169     onContextMenu : function(e, t)
9170     {
9171         this.processEvent("contextmenu", e);
9172     },
9173     
9174     processEvent : function(name, e)
9175     {
9176         if (name != 'touchstart' ) {
9177             this.fireEvent(name, e);    
9178         }
9179         
9180         var t = e.getTarget();
9181         
9182         var cell = Roo.get(t);
9183         
9184         if(!cell){
9185             return;
9186         }
9187         
9188         if(cell.findParent('tfoot', false, true)){
9189             return;
9190         }
9191         
9192         if(cell.findParent('thead', false, true)){
9193             
9194             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9195                 cell = Roo.get(t).findParent('th', false, true);
9196                 if (!cell) {
9197                     Roo.log("failed to find th in thead?");
9198                     Roo.log(e.getTarget());
9199                     return;
9200                 }
9201             }
9202             
9203             var cellIndex = cell.dom.cellIndex;
9204             
9205             var ename = name == 'touchstart' ? 'click' : name;
9206             this.fireEvent("header" + ename, this, cellIndex, e);
9207             
9208             return;
9209         }
9210         
9211         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9212             cell = Roo.get(t).findParent('td', false, true);
9213             if (!cell) {
9214                 Roo.log("failed to find th in tbody?");
9215                 Roo.log(e.getTarget());
9216                 return;
9217             }
9218         }
9219         
9220         var row = cell.findParent('tr', false, true);
9221         var cellIndex = cell.dom.cellIndex;
9222         var rowIndex = row.dom.rowIndex - 1;
9223         
9224         if(row !== false){
9225             
9226             this.fireEvent("row" + name, this, rowIndex, e);
9227             
9228             if(cell !== false){
9229             
9230                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9231             }
9232         }
9233         
9234     },
9235     
9236     onMouseover : function(e, el)
9237     {
9238         var cell = Roo.get(el);
9239         
9240         if(!cell){
9241             return;
9242         }
9243         
9244         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9245             cell = cell.findParent('td', false, true);
9246         }
9247         
9248         var row = cell.findParent('tr', false, true);
9249         var cellIndex = cell.dom.cellIndex;
9250         var rowIndex = row.dom.rowIndex - 1; // start from 0
9251         
9252         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9253         
9254     },
9255     
9256     onMouseout : function(e, el)
9257     {
9258         var cell = Roo.get(el);
9259         
9260         if(!cell){
9261             return;
9262         }
9263         
9264         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9265             cell = cell.findParent('td', false, true);
9266         }
9267         
9268         var row = cell.findParent('tr', false, true);
9269         var cellIndex = cell.dom.cellIndex;
9270         var rowIndex = row.dom.rowIndex - 1; // start from 0
9271         
9272         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9273         
9274     },
9275     
9276     onClick : function(e, el)
9277     {
9278         var cell = Roo.get(el);
9279         
9280         if(!cell || (!this.cellSelection && !this.rowSelection)){
9281             return;
9282         }
9283         
9284         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9285             cell = cell.findParent('td', false, true);
9286         }
9287         
9288         if(!cell || typeof(cell) == 'undefined'){
9289             return;
9290         }
9291         
9292         var row = cell.findParent('tr', false, true);
9293         
9294         if(!row || typeof(row) == 'undefined'){
9295             return;
9296         }
9297         
9298         var cellIndex = cell.dom.cellIndex;
9299         var rowIndex = this.getRowIndex(row);
9300         
9301         // why??? - should these not be based on SelectionModel?
9302         //if(this.cellSelection){
9303             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9304         //}
9305         
9306         //if(this.rowSelection){
9307             this.fireEvent('rowclick', this, row, rowIndex, e);
9308         //}
9309          
9310     },
9311         
9312     onDblClick : function(e,el)
9313     {
9314         var cell = Roo.get(el);
9315         
9316         if(!cell || (!this.cellSelection && !this.rowSelection)){
9317             return;
9318         }
9319         
9320         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9321             cell = cell.findParent('td', false, true);
9322         }
9323         
9324         if(!cell || typeof(cell) == 'undefined'){
9325             return;
9326         }
9327         
9328         var row = cell.findParent('tr', false, true);
9329         
9330         if(!row || typeof(row) == 'undefined'){
9331             return;
9332         }
9333         
9334         var cellIndex = cell.dom.cellIndex;
9335         var rowIndex = this.getRowIndex(row);
9336         
9337         if(this.cellSelection){
9338             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9339         }
9340         
9341         if(this.rowSelection){
9342             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9343         }
9344     },
9345     findRowIndex : function(el)
9346     {
9347         var cell = Roo.get(el);
9348         if(!cell) {
9349             return false;
9350         }
9351         var row = cell.findParent('tr', false, true);
9352         
9353         if(!row || typeof(row) == 'undefined'){
9354             return false;
9355         }
9356         return this.getRowIndex(row);
9357     },
9358     sort : function(e,el)
9359     {
9360         var col = Roo.get(el);
9361         
9362         if(!col.hasClass('sortable')){
9363             return;
9364         }
9365         
9366         var sort = col.attr('sort');
9367         var dir = 'ASC';
9368         
9369         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9370             dir = 'DESC';
9371         }
9372         
9373         this.store.sortInfo = {field : sort, direction : dir};
9374         
9375         if (this.footer) {
9376             Roo.log("calling footer first");
9377             this.footer.onClick('first');
9378         } else {
9379         
9380             this.store.load({ params : { start : 0 } });
9381         }
9382     },
9383     
9384     renderHeader : function()
9385     {
9386         var header = {
9387             tag: 'thead',
9388             cn : []
9389         };
9390         
9391         var cm = this.cm;
9392         this.totalWidth = 0;
9393         
9394         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9395             
9396             var config = cm.config[i];
9397             
9398             var c = {
9399                 tag: 'th',
9400                 cls : 'x-hcol-' + i,
9401                 style : '',
9402                 
9403                 html: cm.getColumnHeader(i)
9404             };
9405             
9406             var tooltip = cm.getColumnTooltip(i);
9407             if (tooltip) {
9408                 c.tooltip = tooltip;
9409             }
9410             
9411             
9412             var hh = '';
9413             
9414             if(typeof(config.sortable) != 'undefined' && config.sortable){
9415                 c.cls += ' sortable';
9416                 c.html = '<i class="fa"></i>' + c.html;
9417             }
9418             
9419             // could use BS4 hidden-..-down 
9420             
9421             if(typeof(config.lgHeader) != 'undefined'){
9422                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9423             }
9424             
9425             if(typeof(config.mdHeader) != 'undefined'){
9426                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9427             }
9428             
9429             if(typeof(config.smHeader) != 'undefined'){
9430                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9431             }
9432             
9433             if(typeof(config.xsHeader) != 'undefined'){
9434                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9435             }
9436             
9437             if(hh.length){
9438                 c.html = hh;
9439             }
9440             
9441             if(typeof(config.tooltip) != 'undefined'){
9442                 c.tooltip = config.tooltip;
9443             }
9444             
9445             if(typeof(config.colspan) != 'undefined'){
9446                 c.colspan = config.colspan;
9447             }
9448             
9449             // hidden is handled by CSS now
9450             
9451             if(typeof(config.dataIndex) != 'undefined'){
9452                 c.sort = config.dataIndex;
9453             }
9454             
9455            
9456             
9457             if(typeof(config.align) != 'undefined' && config.align.length){
9458                 c.style += ' text-align:' + config.align + ';';
9459             }
9460             
9461             /* width is done in CSS
9462              *if(typeof(config.width) != 'undefined'){
9463                 c.style += ' width:' + config.width + 'px;';
9464                 this.totalWidth += config.width;
9465             } else {
9466                 this.totalWidth += 100; // assume minimum of 100 per column?
9467             }
9468             */
9469             
9470             if(typeof(config.cls) != 'undefined'){
9471                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9472             }
9473             // this is the bit that doesnt reall work at all...
9474             
9475             if (this.responsive) {
9476                  
9477             
9478                 ['xs','sm','md','lg'].map(function(size){
9479                     
9480                     if(typeof(config[size]) == 'undefined'){
9481                         return;
9482                     }
9483                      
9484                     if (!config[size]) { // 0 = hidden
9485                         // BS 4 '0' is treated as hide that column and below.
9486                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9487                         return;
9488                     }
9489                     
9490                     c.cls += ' col-' + size + '-' + config[size] + (
9491                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9492                     );
9493                     
9494                     
9495                 });
9496             }
9497             // at the end?
9498             
9499             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9500             
9501             
9502             
9503             
9504             header.cn.push(c)
9505         }
9506         
9507         return header;
9508     },
9509     
9510     renderBody : function()
9511     {
9512         var body = {
9513             tag: 'tbody',
9514             cn : [
9515                 {
9516                     tag: 'tr',
9517                     cn : [
9518                         {
9519                             tag : 'td',
9520                             colspan :  this.cm.getColumnCount()
9521                         }
9522                     ]
9523                 }
9524             ]
9525         };
9526         
9527         return body;
9528     },
9529     
9530     renderFooter : function()
9531     {
9532         var footer = {
9533             tag: 'tfoot',
9534             cn : [
9535                 {
9536                     tag: 'tr',
9537                     cn : [
9538                         {
9539                             tag : 'td',
9540                             colspan :  this.cm.getColumnCount()
9541                         }
9542                     ]
9543                 }
9544             ]
9545         };
9546         
9547         return footer;
9548     },
9549     
9550     
9551     
9552     onLoad : function()
9553     {
9554 //        Roo.log('ds onload');
9555         this.clear();
9556         
9557         var _this = this;
9558         var cm = this.cm;
9559         var ds = this.store;
9560         
9561         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9562             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9563             if (_this.store.sortInfo) {
9564                     
9565                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9566                     e.select('i', true).addClass(['fa-arrow-up']);
9567                 }
9568                 
9569                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9570                     e.select('i', true).addClass(['fa-arrow-down']);
9571                 }
9572             }
9573         });
9574         
9575         var tbody =  this.bodyEl;
9576               
9577         if(ds.getCount() > 0){
9578             ds.data.each(function(d,rowIndex){
9579                 var row =  this.renderRow(cm, ds, rowIndex);
9580                 
9581                 tbody.createChild(row);
9582                 
9583                 var _this = this;
9584                 
9585                 if(row.cellObjects.length){
9586                     Roo.each(row.cellObjects, function(r){
9587                         _this.renderCellObject(r);
9588                     })
9589                 }
9590                 
9591             }, this);
9592         }
9593         
9594         var tfoot = this.el.select('tfoot', true).first();
9595         
9596         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9597             
9598             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9599             
9600             var total = this.ds.getTotalCount();
9601             
9602             if(this.footer.pageSize < total){
9603                 this.mainFoot.show();
9604             }
9605         }
9606         
9607         Roo.each(this.el.select('tbody td', true).elements, function(e){
9608             e.on('mouseover', _this.onMouseover, _this);
9609         });
9610         
9611         Roo.each(this.el.select('tbody td', true).elements, function(e){
9612             e.on('mouseout', _this.onMouseout, _this);
9613         });
9614         this.fireEvent('rowsrendered', this);
9615         
9616         this.autoSize();
9617         
9618         this.initCSS(); /// resize cols
9619
9620         
9621     },
9622     
9623     
9624     onUpdate : function(ds,record)
9625     {
9626         this.refreshRow(record);
9627         this.autoSize();
9628     },
9629     
9630     onRemove : function(ds, record, index, isUpdate){
9631         if(isUpdate !== true){
9632             this.fireEvent("beforerowremoved", this, index, record);
9633         }
9634         var bt = this.bodyEl.dom;
9635         
9636         var rows = this.el.select('tbody > tr', true).elements;
9637         
9638         if(typeof(rows[index]) != 'undefined'){
9639             bt.removeChild(rows[index].dom);
9640         }
9641         
9642 //        if(bt.rows[index]){
9643 //            bt.removeChild(bt.rows[index]);
9644 //        }
9645         
9646         if(isUpdate !== true){
9647             //this.stripeRows(index);
9648             //this.syncRowHeights(index, index);
9649             //this.layout();
9650             this.fireEvent("rowremoved", this, index, record);
9651         }
9652     },
9653     
9654     onAdd : function(ds, records, rowIndex)
9655     {
9656         //Roo.log('on Add called');
9657         // - note this does not handle multiple adding very well..
9658         var bt = this.bodyEl.dom;
9659         for (var i =0 ; i < records.length;i++) {
9660             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9661             //Roo.log(records[i]);
9662             //Roo.log(this.store.getAt(rowIndex+i));
9663             this.insertRow(this.store, rowIndex + i, false);
9664             return;
9665         }
9666         
9667     },
9668     
9669     
9670     refreshRow : function(record){
9671         var ds = this.store, index;
9672         if(typeof record == 'number'){
9673             index = record;
9674             record = ds.getAt(index);
9675         }else{
9676             index = ds.indexOf(record);
9677             if (index < 0) {
9678                 return; // should not happen - but seems to 
9679             }
9680         }
9681         this.insertRow(ds, index, true);
9682         this.autoSize();
9683         this.onRemove(ds, record, index+1, true);
9684         this.autoSize();
9685         //this.syncRowHeights(index, index);
9686         //this.layout();
9687         this.fireEvent("rowupdated", this, index, record);
9688     },
9689     // private - called by RowSelection
9690     onRowSelect : function(rowIndex){
9691         var row = this.getRowDom(rowIndex);
9692         row.addClass(['bg-info','info']);
9693     },
9694     // private - called by RowSelection
9695     onRowDeselect : function(rowIndex)
9696     {
9697         if (rowIndex < 0) {
9698             return;
9699         }
9700         var row = this.getRowDom(rowIndex);
9701         row.removeClass(['bg-info','info']);
9702     },
9703       /**
9704      * Focuses the specified row.
9705      * @param {Number} row The row index
9706      */
9707     focusRow : function(row)
9708     {
9709         //Roo.log('GridView.focusRow');
9710         var x = this.bodyEl.dom.scrollLeft;
9711         this.focusCell(row, 0, false);
9712         this.bodyEl.dom.scrollLeft = x;
9713
9714     },
9715      /**
9716      * Focuses the specified cell.
9717      * @param {Number} row The row index
9718      * @param {Number} col The column index
9719      * @param {Boolean} hscroll false to disable horizontal scrolling
9720      */
9721     focusCell : function(row, col, hscroll)
9722     {
9723         //Roo.log('GridView.focusCell');
9724         var el = this.ensureVisible(row, col, hscroll);
9725         // not sure what focusEL achives = it's a <a> pos relative 
9726         //this.focusEl.alignTo(el, "tl-tl");
9727         //if(Roo.isGecko){
9728         //    this.focusEl.focus();
9729         //}else{
9730         //    this.focusEl.focus.defer(1, this.focusEl);
9731         //}
9732     },
9733     
9734      /**
9735      * Scrolls the specified cell into view
9736      * @param {Number} row The row index
9737      * @param {Number} col The column index
9738      * @param {Boolean} hscroll false to disable horizontal scrolling
9739      */
9740     ensureVisible : function(row, col, hscroll)
9741     {
9742         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9743         //return null; //disable for testing.
9744         if(typeof row != "number"){
9745             row = row.rowIndex;
9746         }
9747         if(row < 0 && row >= this.ds.getCount()){
9748             return  null;
9749         }
9750         col = (col !== undefined ? col : 0);
9751         var cm = this.cm;
9752         while(cm.isHidden(col)){
9753             col++;
9754         }
9755
9756         var el = this.getCellDom(row, col);
9757         if(!el){
9758             return null;
9759         }
9760         var c = this.bodyEl.dom;
9761
9762         var ctop = parseInt(el.offsetTop, 10);
9763         var cleft = parseInt(el.offsetLeft, 10);
9764         var cbot = ctop + el.offsetHeight;
9765         var cright = cleft + el.offsetWidth;
9766
9767         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9768         var ch = 0; //?? header is not withing the area?
9769         var stop = parseInt(c.scrollTop, 10);
9770         var sleft = parseInt(c.scrollLeft, 10);
9771         var sbot = stop + ch;
9772         var sright = sleft + c.clientWidth;
9773         /*
9774         Roo.log('GridView.ensureVisible:' +
9775                 ' ctop:' + ctop +
9776                 ' c.clientHeight:' + c.clientHeight +
9777                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9778                 ' stop:' + stop +
9779                 ' cbot:' + cbot +
9780                 ' sbot:' + sbot +
9781                 ' ch:' + ch  
9782                 );
9783         */
9784         if(ctop < stop){
9785             c.scrollTop = ctop;
9786             //Roo.log("set scrolltop to ctop DISABLE?");
9787         }else if(cbot > sbot){
9788             //Roo.log("set scrolltop to cbot-ch");
9789             c.scrollTop = cbot-ch;
9790         }
9791
9792         if(hscroll !== false){
9793             if(cleft < sleft){
9794                 c.scrollLeft = cleft;
9795             }else if(cright > sright){
9796                 c.scrollLeft = cright-c.clientWidth;
9797             }
9798         }
9799
9800         return el;
9801     },
9802     
9803     
9804     insertRow : function(dm, rowIndex, isUpdate){
9805         
9806         if(!isUpdate){
9807             this.fireEvent("beforerowsinserted", this, rowIndex);
9808         }
9809             //var s = this.getScrollState();
9810         var row = this.renderRow(this.cm, this.store, rowIndex);
9811         // insert before rowIndex..
9812         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9813         
9814         var _this = this;
9815                 
9816         if(row.cellObjects.length){
9817             Roo.each(row.cellObjects, function(r){
9818                 _this.renderCellObject(r);
9819             })
9820         }
9821             
9822         if(!isUpdate){
9823             this.fireEvent("rowsinserted", this, rowIndex);
9824             //this.syncRowHeights(firstRow, lastRow);
9825             //this.stripeRows(firstRow);
9826             //this.layout();
9827         }
9828         
9829     },
9830     
9831     
9832     getRowDom : function(rowIndex)
9833     {
9834         var rows = this.el.select('tbody > tr', true).elements;
9835         
9836         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9837         
9838     },
9839     getCellDom : function(rowIndex, colIndex)
9840     {
9841         var row = this.getRowDom(rowIndex);
9842         if (row === false) {
9843             return false;
9844         }
9845         var cols = row.select('td', true).elements;
9846         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9847         
9848     },
9849     
9850     // returns the object tree for a tr..
9851   
9852     
9853     renderRow : function(cm, ds, rowIndex) 
9854     {
9855         var d = ds.getAt(rowIndex);
9856         
9857         var row = {
9858             tag : 'tr',
9859             cls : 'x-row-' + rowIndex,
9860             cn : []
9861         };
9862             
9863         var cellObjects = [];
9864         
9865         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9866             var config = cm.config[i];
9867             
9868             var renderer = cm.getRenderer(i);
9869             var value = '';
9870             var id = false;
9871             
9872             if(typeof(renderer) !== 'undefined'){
9873                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9874             }
9875             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9876             // and are rendered into the cells after the row is rendered - using the id for the element.
9877             
9878             if(typeof(value) === 'object'){
9879                 id = Roo.id();
9880                 cellObjects.push({
9881                     container : id,
9882                     cfg : value 
9883                 })
9884             }
9885             
9886             var rowcfg = {
9887                 record: d,
9888                 rowIndex : rowIndex,
9889                 colIndex : i,
9890                 rowClass : ''
9891             };
9892
9893             this.fireEvent('rowclass', this, rowcfg);
9894             
9895             var td = {
9896                 tag: 'td',
9897                 // this might end up displaying HTML?
9898                 // this is too messy... - better to only do it on columsn you know are going to be too long
9899                 //tooltip : (typeof(value) === 'object') ? '' : value,
9900                 cls : rowcfg.rowClass + ' x-col-' + i,
9901                 style: '',
9902                 html: (typeof(value) === 'object') ? '' : value
9903             };
9904             
9905             if (id) {
9906                 td.id = id;
9907             }
9908             
9909             if(typeof(config.colspan) != 'undefined'){
9910                 td.colspan = config.colspan;
9911             }
9912             
9913             
9914             
9915             if(typeof(config.align) != 'undefined' && config.align.length){
9916                 td.style += ' text-align:' + config.align + ';';
9917             }
9918             if(typeof(config.valign) != 'undefined' && config.valign.length){
9919                 td.style += ' vertical-align:' + config.valign + ';';
9920             }
9921             /*
9922             if(typeof(config.width) != 'undefined'){
9923                 td.style += ' width:' +  config.width + 'px;';
9924             }
9925             */
9926             
9927             if(typeof(config.cursor) != 'undefined'){
9928                 td.style += ' cursor:' +  config.cursor + ';';
9929             }
9930             
9931             if(typeof(config.cls) != 'undefined'){
9932                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9933             }
9934             if (this.responsive) {
9935                 ['xs','sm','md','lg'].map(function(size){
9936                     
9937                     if(typeof(config[size]) == 'undefined'){
9938                         return;
9939                     }
9940                     
9941                     
9942                       
9943                     if (!config[size]) { // 0 = hidden
9944                         // BS 4 '0' is treated as hide that column and below.
9945                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9946                         return;
9947                     }
9948                     
9949                     td.cls += ' col-' + size + '-' + config[size] + (
9950                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9951                     );
9952                      
9953     
9954                 });
9955             }
9956             row.cn.push(td);
9957            
9958         }
9959         
9960         row.cellObjects = cellObjects;
9961         
9962         return row;
9963           
9964     },
9965     
9966     
9967     
9968     onBeforeLoad : function()
9969     {
9970         
9971     },
9972      /**
9973      * Remove all rows
9974      */
9975     clear : function()
9976     {
9977         this.el.select('tbody', true).first().dom.innerHTML = '';
9978     },
9979     /**
9980      * Show or hide a row.
9981      * @param {Number} rowIndex to show or hide
9982      * @param {Boolean} state hide
9983      */
9984     setRowVisibility : function(rowIndex, state)
9985     {
9986         var bt = this.bodyEl.dom;
9987         
9988         var rows = this.el.select('tbody > tr', true).elements;
9989         
9990         if(typeof(rows[rowIndex]) == 'undefined'){
9991             return;
9992         }
9993         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9994         
9995     },
9996     
9997     
9998     getSelectionModel : function(){
9999         if(!this.selModel){
10000             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10001         }
10002         return this.selModel;
10003     },
10004     /*
10005      * Render the Roo.bootstrap object from renderder
10006      */
10007     renderCellObject : function(r)
10008     {
10009         var _this = this;
10010         
10011         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10012         
10013         var t = r.cfg.render(r.container);
10014         
10015         if(r.cfg.cn){
10016             Roo.each(r.cfg.cn, function(c){
10017                 var child = {
10018                     container: t.getChildContainer(),
10019                     cfg: c
10020                 };
10021                 _this.renderCellObject(child);
10022             })
10023         }
10024     },
10025     /**
10026      * get the Row Index from a dom element.
10027      * @param {Roo.Element} row The row to look for
10028      * @returns {Number} the row
10029      */
10030     getRowIndex : function(row)
10031     {
10032         var rowIndex = -1;
10033         
10034         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10035             if(el != row){
10036                 return;
10037             }
10038             
10039             rowIndex = index;
10040         });
10041         
10042         return rowIndex;
10043     },
10044     /**
10045      * get the header TH element for columnIndex
10046      * @param {Number} columnIndex
10047      * @returns {Roo.Element}
10048      */
10049     getHeaderIndex: function(colIndex)
10050     {
10051         var cols = this.headEl.select('th', true).elements;
10052         return cols[colIndex]; 
10053     },
10054     /**
10055      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10056      * @param {domElement} cell to look for
10057      * @returns {Number} the column
10058      */
10059     getCellIndex : function(cell)
10060     {
10061         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10062         if(id){
10063             return parseInt(id[1], 10);
10064         }
10065         return 0;
10066     },
10067      /**
10068      * Returns the grid's underlying element = used by panel.Grid
10069      * @return {Element} The element
10070      */
10071     getGridEl : function(){
10072         return this.el;
10073     },
10074      /**
10075      * Forces a resize - used by panel.Grid
10076      * @return {Element} The element
10077      */
10078     autoSize : function()
10079     {
10080         //var ctr = Roo.get(this.container.dom.parentElement);
10081         var ctr = Roo.get(this.el.dom);
10082         
10083         var thd = this.getGridEl().select('thead',true).first();
10084         var tbd = this.getGridEl().select('tbody', true).first();
10085         var tfd = this.getGridEl().select('tfoot', true).first();
10086         
10087         var cw = ctr.getWidth();
10088         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10089         
10090         if (tbd) {
10091             
10092             tbd.setWidth(ctr.getWidth());
10093             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10094             // this needs fixing for various usage - currently only hydra job advers I think..
10095             //tdb.setHeight(
10096             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10097             //); 
10098             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10099             cw -= barsize;
10100         }
10101         cw = Math.max(cw, this.totalWidth);
10102         this.getGridEl().select('tbody tr',true).setWidth(cw);
10103         this.initCSS();
10104         
10105         // resize 'expandable coloumn?
10106         
10107         return; // we doe not have a view in this design..
10108         
10109     },
10110     onBodyScroll: function()
10111     {
10112         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10113         if(this.headEl){
10114             this.headEl.setStyle({
10115                 'position' : 'relative',
10116                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10117             });
10118         }
10119         
10120         if(this.lazyLoad){
10121             
10122             var scrollHeight = this.bodyEl.dom.scrollHeight;
10123             
10124             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10125             
10126             var height = this.bodyEl.getHeight();
10127             
10128             if(scrollHeight - height == scrollTop) {
10129                 
10130                 var total = this.ds.getTotalCount();
10131                 
10132                 if(this.footer.cursor + this.footer.pageSize < total){
10133                     
10134                     this.footer.ds.load({
10135                         params : {
10136                             start : this.footer.cursor + this.footer.pageSize,
10137                             limit : this.footer.pageSize
10138                         },
10139                         add : true
10140                     });
10141                 }
10142             }
10143             
10144         }
10145     },
10146     onColumnSplitterMoved : function(i, diff)
10147     {
10148         this.userResized = true;
10149         
10150         var cm = this.colModel;
10151         
10152         var w = this.getHeaderIndex(i).getWidth() + diff;
10153         
10154         
10155         cm.setColumnWidth(i, w, true);
10156         this.initCSS();
10157         //var cid = cm.getColumnId(i); << not used in this version?
10158        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10159         
10160         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10161         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10162         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10163 */
10164         //this.updateSplitters();
10165         //this.layout(); << ??
10166         this.fireEvent("columnresize", i, w);
10167     },
10168     onHeaderChange : function()
10169     {
10170         var header = this.renderHeader();
10171         var table = this.el.select('table', true).first();
10172         
10173         this.headEl.remove();
10174         this.headEl = table.createChild(header, this.bodyEl, false);
10175         
10176         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10177             e.on('click', this.sort, this);
10178         }, this);
10179         
10180         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10181             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10182         }
10183         
10184     },
10185     
10186     onHiddenChange : function(colModel, colIndex, hidden)
10187     {
10188         /*
10189         this.cm.setHidden()
10190         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10191         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10192         
10193         this.CSS.updateRule(thSelector, "display", "");
10194         this.CSS.updateRule(tdSelector, "display", "");
10195         
10196         if(hidden){
10197             this.CSS.updateRule(thSelector, "display", "none");
10198             this.CSS.updateRule(tdSelector, "display", "none");
10199         }
10200         */
10201         // onload calls initCSS()
10202         this.onHeaderChange();
10203         this.onLoad();
10204     },
10205     
10206     setColumnWidth: function(col_index, width)
10207     {
10208         // width = "md-2 xs-2..."
10209         if(!this.colModel.config[col_index]) {
10210             return;
10211         }
10212         
10213         var w = width.split(" ");
10214         
10215         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10216         
10217         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10218         
10219         
10220         for(var j = 0; j < w.length; j++) {
10221             
10222             if(!w[j]) {
10223                 continue;
10224             }
10225             
10226             var size_cls = w[j].split("-");
10227             
10228             if(!Number.isInteger(size_cls[1] * 1)) {
10229                 continue;
10230             }
10231             
10232             if(!this.colModel.config[col_index][size_cls[0]]) {
10233                 continue;
10234             }
10235             
10236             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10237                 continue;
10238             }
10239             
10240             h_row[0].classList.replace(
10241                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10242                 "col-"+size_cls[0]+"-"+size_cls[1]
10243             );
10244             
10245             for(var i = 0; i < rows.length; i++) {
10246                 
10247                 var size_cls = w[j].split("-");
10248                 
10249                 if(!Number.isInteger(size_cls[1] * 1)) {
10250                     continue;
10251                 }
10252                 
10253                 if(!this.colModel.config[col_index][size_cls[0]]) {
10254                     continue;
10255                 }
10256                 
10257                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10258                     continue;
10259                 }
10260                 
10261                 rows[i].classList.replace(
10262                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10263                     "col-"+size_cls[0]+"-"+size_cls[1]
10264                 );
10265             }
10266             
10267             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10268         }
10269     }
10270 });
10271
10272 // currently only used to find the split on drag.. 
10273 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10274
10275 /**
10276  * @depricated
10277 */
10278 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10279 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10280 /*
10281  * - LGPL
10282  *
10283  * table cell
10284  * 
10285  */
10286
10287 /**
10288  * @class Roo.bootstrap.TableCell
10289  * @extends Roo.bootstrap.Component
10290  * Bootstrap TableCell class
10291  * @cfg {String} html cell contain text
10292  * @cfg {String} cls cell class
10293  * @cfg {String} tag cell tag (td|th) default td
10294  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10295  * @cfg {String} align Aligns the content in a cell
10296  * @cfg {String} axis Categorizes cells
10297  * @cfg {String} bgcolor Specifies the background color of a cell
10298  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10299  * @cfg {Number} colspan Specifies the number of columns a cell should span
10300  * @cfg {String} headers Specifies one or more header cells a cell is related to
10301  * @cfg {Number} height Sets the height of a cell
10302  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10303  * @cfg {Number} rowspan Sets the number of rows a cell should span
10304  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10305  * @cfg {String} valign Vertical aligns the content in a cell
10306  * @cfg {Number} width Specifies the width of a cell
10307  * 
10308  * @constructor
10309  * Create a new TableCell
10310  * @param {Object} config The config object
10311  */
10312
10313 Roo.bootstrap.TableCell = function(config){
10314     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10315 };
10316
10317 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10318     
10319     html: false,
10320     cls: false,
10321     tag: false,
10322     abbr: false,
10323     align: false,
10324     axis: false,
10325     bgcolor: false,
10326     charoff: false,
10327     colspan: false,
10328     headers: false,
10329     height: false,
10330     nowrap: false,
10331     rowspan: false,
10332     scope: false,
10333     valign: false,
10334     width: false,
10335     
10336     
10337     getAutoCreate : function(){
10338         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10339         
10340         cfg = {
10341             tag: 'td'
10342         };
10343         
10344         if(this.tag){
10345             cfg.tag = this.tag;
10346         }
10347         
10348         if (this.html) {
10349             cfg.html=this.html
10350         }
10351         if (this.cls) {
10352             cfg.cls=this.cls
10353         }
10354         if (this.abbr) {
10355             cfg.abbr=this.abbr
10356         }
10357         if (this.align) {
10358             cfg.align=this.align
10359         }
10360         if (this.axis) {
10361             cfg.axis=this.axis
10362         }
10363         if (this.bgcolor) {
10364             cfg.bgcolor=this.bgcolor
10365         }
10366         if (this.charoff) {
10367             cfg.charoff=this.charoff
10368         }
10369         if (this.colspan) {
10370             cfg.colspan=this.colspan
10371         }
10372         if (this.headers) {
10373             cfg.headers=this.headers
10374         }
10375         if (this.height) {
10376             cfg.height=this.height
10377         }
10378         if (this.nowrap) {
10379             cfg.nowrap=this.nowrap
10380         }
10381         if (this.rowspan) {
10382             cfg.rowspan=this.rowspan
10383         }
10384         if (this.scope) {
10385             cfg.scope=this.scope
10386         }
10387         if (this.valign) {
10388             cfg.valign=this.valign
10389         }
10390         if (this.width) {
10391             cfg.width=this.width
10392         }
10393         
10394         
10395         return cfg;
10396     }
10397    
10398 });
10399
10400  
10401
10402  /*
10403  * - LGPL
10404  *
10405  * table row
10406  * 
10407  */
10408
10409 /**
10410  * @class Roo.bootstrap.TableRow
10411  * @extends Roo.bootstrap.Component
10412  * Bootstrap TableRow class
10413  * @cfg {String} cls row class
10414  * @cfg {String} align Aligns the content in a table row
10415  * @cfg {String} bgcolor Specifies a background color for a table row
10416  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10417  * @cfg {String} valign Vertical aligns the content in a table row
10418  * 
10419  * @constructor
10420  * Create a new TableRow
10421  * @param {Object} config The config object
10422  */
10423
10424 Roo.bootstrap.TableRow = function(config){
10425     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10426 };
10427
10428 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10429     
10430     cls: false,
10431     align: false,
10432     bgcolor: false,
10433     charoff: false,
10434     valign: false,
10435     
10436     getAutoCreate : function(){
10437         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10438         
10439         cfg = {
10440             tag: 'tr'
10441         };
10442             
10443         if(this.cls){
10444             cfg.cls = this.cls;
10445         }
10446         if(this.align){
10447             cfg.align = this.align;
10448         }
10449         if(this.bgcolor){
10450             cfg.bgcolor = this.bgcolor;
10451         }
10452         if(this.charoff){
10453             cfg.charoff = this.charoff;
10454         }
10455         if(this.valign){
10456             cfg.valign = this.valign;
10457         }
10458         
10459         return cfg;
10460     }
10461    
10462 });
10463
10464  
10465
10466  /*
10467  * - LGPL
10468  *
10469  * table body
10470  * 
10471  */
10472
10473 /**
10474  * @class Roo.bootstrap.TableBody
10475  * @extends Roo.bootstrap.Component
10476  * Bootstrap TableBody class
10477  * @cfg {String} cls element class
10478  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10479  * @cfg {String} align Aligns the content inside the element
10480  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10481  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10482  * 
10483  * @constructor
10484  * Create a new TableBody
10485  * @param {Object} config The config object
10486  */
10487
10488 Roo.bootstrap.TableBody = function(config){
10489     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10490 };
10491
10492 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10493     
10494     cls: false,
10495     tag: false,
10496     align: false,
10497     charoff: false,
10498     valign: false,
10499     
10500     getAutoCreate : function(){
10501         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10502         
10503         cfg = {
10504             tag: 'tbody'
10505         };
10506             
10507         if (this.cls) {
10508             cfg.cls=this.cls
10509         }
10510         if(this.tag){
10511             cfg.tag = this.tag;
10512         }
10513         
10514         if(this.align){
10515             cfg.align = this.align;
10516         }
10517         if(this.charoff){
10518             cfg.charoff = this.charoff;
10519         }
10520         if(this.valign){
10521             cfg.valign = this.valign;
10522         }
10523         
10524         return cfg;
10525     }
10526     
10527     
10528 //    initEvents : function()
10529 //    {
10530 //        
10531 //        if(!this.store){
10532 //            return;
10533 //        }
10534 //        
10535 //        this.store = Roo.factory(this.store, Roo.data);
10536 //        this.store.on('load', this.onLoad, this);
10537 //        
10538 //        this.store.load();
10539 //        
10540 //    },
10541 //    
10542 //    onLoad: function () 
10543 //    {   
10544 //        this.fireEvent('load', this);
10545 //    }
10546 //    
10547 //   
10548 });
10549
10550  
10551
10552  /*
10553  * Based on:
10554  * Ext JS Library 1.1.1
10555  * Copyright(c) 2006-2007, Ext JS, LLC.
10556  *
10557  * Originally Released Under LGPL - original licence link has changed is not relivant.
10558  *
10559  * Fork - LGPL
10560  * <script type="text/javascript">
10561  */
10562
10563 // as we use this in bootstrap.
10564 Roo.namespace('Roo.form');
10565  /**
10566  * @class Roo.form.Action
10567  * Internal Class used to handle form actions
10568  * @constructor
10569  * @param {Roo.form.BasicForm} el The form element or its id
10570  * @param {Object} config Configuration options
10571  */
10572
10573  
10574  
10575 // define the action interface
10576 Roo.form.Action = function(form, options){
10577     this.form = form;
10578     this.options = options || {};
10579 };
10580 /**
10581  * Client Validation Failed
10582  * @const 
10583  */
10584 Roo.form.Action.CLIENT_INVALID = 'client';
10585 /**
10586  * Server Validation Failed
10587  * @const 
10588  */
10589 Roo.form.Action.SERVER_INVALID = 'server';
10590  /**
10591  * Connect to Server Failed
10592  * @const 
10593  */
10594 Roo.form.Action.CONNECT_FAILURE = 'connect';
10595 /**
10596  * Reading Data from Server Failed
10597  * @const 
10598  */
10599 Roo.form.Action.LOAD_FAILURE = 'load';
10600
10601 Roo.form.Action.prototype = {
10602     type : 'default',
10603     failureType : undefined,
10604     response : undefined,
10605     result : undefined,
10606
10607     // interface method
10608     run : function(options){
10609
10610     },
10611
10612     // interface method
10613     success : function(response){
10614
10615     },
10616
10617     // interface method
10618     handleResponse : function(response){
10619
10620     },
10621
10622     // default connection failure
10623     failure : function(response){
10624         
10625         this.response = response;
10626         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10627         this.form.afterAction(this, false);
10628     },
10629
10630     processResponse : function(response){
10631         this.response = response;
10632         if(!response.responseText){
10633             return true;
10634         }
10635         this.result = this.handleResponse(response);
10636         return this.result;
10637     },
10638
10639     // utility functions used internally
10640     getUrl : function(appendParams){
10641         var url = this.options.url || this.form.url || this.form.el.dom.action;
10642         if(appendParams){
10643             var p = this.getParams();
10644             if(p){
10645                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10646             }
10647         }
10648         return url;
10649     },
10650
10651     getMethod : function(){
10652         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10653     },
10654
10655     getParams : function(){
10656         var bp = this.form.baseParams;
10657         var p = this.options.params;
10658         if(p){
10659             if(typeof p == "object"){
10660                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10661             }else if(typeof p == 'string' && bp){
10662                 p += '&' + Roo.urlEncode(bp);
10663             }
10664         }else if(bp){
10665             p = Roo.urlEncode(bp);
10666         }
10667         return p;
10668     },
10669
10670     createCallback : function(){
10671         return {
10672             success: this.success,
10673             failure: this.failure,
10674             scope: this,
10675             timeout: (this.form.timeout*1000),
10676             upload: this.form.fileUpload ? this.success : undefined
10677         };
10678     }
10679 };
10680
10681 Roo.form.Action.Submit = function(form, options){
10682     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10683 };
10684
10685 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10686     type : 'submit',
10687
10688     haveProgress : false,
10689     uploadComplete : false,
10690     
10691     // uploadProgress indicator.
10692     uploadProgress : function()
10693     {
10694         if (!this.form.progressUrl) {
10695             return;
10696         }
10697         
10698         if (!this.haveProgress) {
10699             Roo.MessageBox.progress("Uploading", "Uploading");
10700         }
10701         if (this.uploadComplete) {
10702            Roo.MessageBox.hide();
10703            return;
10704         }
10705         
10706         this.haveProgress = true;
10707    
10708         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10709         
10710         var c = new Roo.data.Connection();
10711         c.request({
10712             url : this.form.progressUrl,
10713             params: {
10714                 id : uid
10715             },
10716             method: 'GET',
10717             success : function(req){
10718                //console.log(data);
10719                 var rdata = false;
10720                 var edata;
10721                 try  {
10722                    rdata = Roo.decode(req.responseText)
10723                 } catch (e) {
10724                     Roo.log("Invalid data from server..");
10725                     Roo.log(edata);
10726                     return;
10727                 }
10728                 if (!rdata || !rdata.success) {
10729                     Roo.log(rdata);
10730                     Roo.MessageBox.alert(Roo.encode(rdata));
10731                     return;
10732                 }
10733                 var data = rdata.data;
10734                 
10735                 if (this.uploadComplete) {
10736                    Roo.MessageBox.hide();
10737                    return;
10738                 }
10739                    
10740                 if (data){
10741                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10742                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10743                     );
10744                 }
10745                 this.uploadProgress.defer(2000,this);
10746             },
10747        
10748             failure: function(data) {
10749                 Roo.log('progress url failed ');
10750                 Roo.log(data);
10751             },
10752             scope : this
10753         });
10754            
10755     },
10756     
10757     
10758     run : function()
10759     {
10760         // run get Values on the form, so it syncs any secondary forms.
10761         this.form.getValues();
10762         
10763         var o = this.options;
10764         var method = this.getMethod();
10765         var isPost = method == 'POST';
10766         if(o.clientValidation === false || this.form.isValid()){
10767             
10768             if (this.form.progressUrl) {
10769                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10770                     (new Date() * 1) + '' + Math.random());
10771                     
10772             } 
10773             
10774             
10775             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10776                 form:this.form.el.dom,
10777                 url:this.getUrl(!isPost),
10778                 method: method,
10779                 params:isPost ? this.getParams() : null,
10780                 isUpload: this.form.fileUpload,
10781                 formData : this.form.formData
10782             }));
10783             
10784             this.uploadProgress();
10785
10786         }else if (o.clientValidation !== false){ // client validation failed
10787             this.failureType = Roo.form.Action.CLIENT_INVALID;
10788             this.form.afterAction(this, false);
10789         }
10790     },
10791
10792     success : function(response)
10793     {
10794         this.uploadComplete= true;
10795         if (this.haveProgress) {
10796             Roo.MessageBox.hide();
10797         }
10798         
10799         
10800         var result = this.processResponse(response);
10801         if(result === true || result.success){
10802             this.form.afterAction(this, true);
10803             return;
10804         }
10805         if(result.errors){
10806             this.form.markInvalid(result.errors);
10807             this.failureType = Roo.form.Action.SERVER_INVALID;
10808         }
10809         this.form.afterAction(this, false);
10810     },
10811     failure : function(response)
10812     {
10813         this.uploadComplete= true;
10814         if (this.haveProgress) {
10815             Roo.MessageBox.hide();
10816         }
10817         
10818         this.response = response;
10819         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10820         this.form.afterAction(this, false);
10821     },
10822     
10823     handleResponse : function(response){
10824         if(this.form.errorReader){
10825             var rs = this.form.errorReader.read(response);
10826             var errors = [];
10827             if(rs.records){
10828                 for(var i = 0, len = rs.records.length; i < len; i++) {
10829                     var r = rs.records[i];
10830                     errors[i] = r.data;
10831                 }
10832             }
10833             if(errors.length < 1){
10834                 errors = null;
10835             }
10836             return {
10837                 success : rs.success,
10838                 errors : errors
10839             };
10840         }
10841         var ret = false;
10842         try {
10843             ret = Roo.decode(response.responseText);
10844         } catch (e) {
10845             ret = {
10846                 success: false,
10847                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10848                 errors : []
10849             };
10850         }
10851         return ret;
10852         
10853     }
10854 });
10855
10856
10857 Roo.form.Action.Load = function(form, options){
10858     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10859     this.reader = this.form.reader;
10860 };
10861
10862 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10863     type : 'load',
10864
10865     run : function(){
10866         
10867         Roo.Ajax.request(Roo.apply(
10868                 this.createCallback(), {
10869                     method:this.getMethod(),
10870                     url:this.getUrl(false),
10871                     params:this.getParams()
10872         }));
10873     },
10874
10875     success : function(response){
10876         
10877         var result = this.processResponse(response);
10878         if(result === true || !result.success || !result.data){
10879             this.failureType = Roo.form.Action.LOAD_FAILURE;
10880             this.form.afterAction(this, false);
10881             return;
10882         }
10883         this.form.clearInvalid();
10884         this.form.setValues(result.data);
10885         this.form.afterAction(this, true);
10886     },
10887
10888     handleResponse : function(response){
10889         if(this.form.reader){
10890             var rs = this.form.reader.read(response);
10891             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10892             return {
10893                 success : rs.success,
10894                 data : data
10895             };
10896         }
10897         return Roo.decode(response.responseText);
10898     }
10899 });
10900
10901 Roo.form.Action.ACTION_TYPES = {
10902     'load' : Roo.form.Action.Load,
10903     'submit' : Roo.form.Action.Submit
10904 };/*
10905  * - LGPL
10906  *
10907  * form
10908  *
10909  */
10910
10911 /**
10912  * @class Roo.bootstrap.Form
10913  * @extends Roo.bootstrap.Component
10914  * Bootstrap Form class
10915  * @cfg {String} method  GET | POST (default POST)
10916  * @cfg {String} labelAlign top | left (default top)
10917  * @cfg {String} align left  | right - for navbars
10918  * @cfg {Boolean} loadMask load mask when submit (default true)
10919
10920  *
10921  * @constructor
10922  * Create a new Form
10923  * @param {Object} config The config object
10924  */
10925
10926
10927 Roo.bootstrap.Form = function(config){
10928     
10929     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10930     
10931     Roo.bootstrap.Form.popover.apply();
10932     
10933     this.addEvents({
10934         /**
10935          * @event clientvalidation
10936          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10937          * @param {Form} this
10938          * @param {Boolean} valid true if the form has passed client-side validation
10939          */
10940         clientvalidation: true,
10941         /**
10942          * @event beforeaction
10943          * Fires before any action is performed. Return false to cancel the action.
10944          * @param {Form} this
10945          * @param {Action} action The action to be performed
10946          */
10947         beforeaction: true,
10948         /**
10949          * @event actionfailed
10950          * Fires when an action fails.
10951          * @param {Form} this
10952          * @param {Action} action The action that failed
10953          */
10954         actionfailed : true,
10955         /**
10956          * @event actioncomplete
10957          * Fires when an action is completed.
10958          * @param {Form} this
10959          * @param {Action} action The action that completed
10960          */
10961         actioncomplete : true
10962     });
10963 };
10964
10965 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10966
10967      /**
10968      * @cfg {String} method
10969      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10970      */
10971     method : 'POST',
10972     /**
10973      * @cfg {String} url
10974      * The URL to use for form actions if one isn't supplied in the action options.
10975      */
10976     /**
10977      * @cfg {Boolean} fileUpload
10978      * Set to true if this form is a file upload.
10979      */
10980
10981     /**
10982      * @cfg {Object} baseParams
10983      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10984      */
10985
10986     /**
10987      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10988      */
10989     timeout: 30,
10990     /**
10991      * @cfg {Sting} align (left|right) for navbar forms
10992      */
10993     align : 'left',
10994
10995     // private
10996     activeAction : null,
10997
10998     /**
10999      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11000      * element by passing it or its id or mask the form itself by passing in true.
11001      * @type Mixed
11002      */
11003     waitMsgTarget : false,
11004
11005     loadMask : true,
11006     
11007     /**
11008      * @cfg {Boolean} errorMask (true|false) default false
11009      */
11010     errorMask : false,
11011     
11012     /**
11013      * @cfg {Number} maskOffset Default 100
11014      */
11015     maskOffset : 100,
11016     
11017     /**
11018      * @cfg {Boolean} maskBody
11019      */
11020     maskBody : false,
11021
11022     getAutoCreate : function(){
11023
11024         var cfg = {
11025             tag: 'form',
11026             method : this.method || 'POST',
11027             id : this.id || Roo.id(),
11028             cls : ''
11029         };
11030         if (this.parent().xtype.match(/^Nav/)) {
11031             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11032
11033         }
11034
11035         if (this.labelAlign == 'left' ) {
11036             cfg.cls += ' form-horizontal';
11037         }
11038
11039
11040         return cfg;
11041     },
11042     initEvents : function()
11043     {
11044         this.el.on('submit', this.onSubmit, this);
11045         // this was added as random key presses on the form where triggering form submit.
11046         this.el.on('keypress', function(e) {
11047             if (e.getCharCode() != 13) {
11048                 return true;
11049             }
11050             // we might need to allow it for textareas.. and some other items.
11051             // check e.getTarget().
11052
11053             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11054                 return true;
11055             }
11056
11057             Roo.log("keypress blocked");
11058
11059             e.preventDefault();
11060             return false;
11061         });
11062         
11063     },
11064     // private
11065     onSubmit : function(e){
11066         e.stopEvent();
11067     },
11068
11069      /**
11070      * Returns true if client-side validation on the form is successful.
11071      * @return Boolean
11072      */
11073     isValid : function(){
11074         var items = this.getItems();
11075         var valid = true;
11076         var target = false;
11077         
11078         items.each(function(f){
11079             
11080             if(f.validate()){
11081                 return;
11082             }
11083             
11084             Roo.log('invalid field: ' + f.name);
11085             
11086             valid = false;
11087
11088             if(!target && f.el.isVisible(true)){
11089                 target = f;
11090             }
11091            
11092         });
11093         
11094         if(this.errorMask && !valid){
11095             Roo.bootstrap.Form.popover.mask(this, target);
11096         }
11097         
11098         return valid;
11099     },
11100     
11101     /**
11102      * Returns true if any fields in this form have changed since their original load.
11103      * @return Boolean
11104      */
11105     isDirty : function(){
11106         var dirty = false;
11107         var items = this.getItems();
11108         items.each(function(f){
11109            if(f.isDirty()){
11110                dirty = true;
11111                return false;
11112            }
11113            return true;
11114         });
11115         return dirty;
11116     },
11117      /**
11118      * Performs a predefined action (submit or load) or custom actions you define on this form.
11119      * @param {String} actionName The name of the action type
11120      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11121      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11122      * accept other config options):
11123      * <pre>
11124 Property          Type             Description
11125 ----------------  ---------------  ----------------------------------------------------------------------------------
11126 url               String           The url for the action (defaults to the form's url)
11127 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11128 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11129 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11130                                    validate the form on the client (defaults to false)
11131      * </pre>
11132      * @return {BasicForm} this
11133      */
11134     doAction : function(action, options){
11135         if(typeof action == 'string'){
11136             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11137         }
11138         if(this.fireEvent('beforeaction', this, action) !== false){
11139             this.beforeAction(action);
11140             action.run.defer(100, action);
11141         }
11142         return this;
11143     },
11144
11145     // private
11146     beforeAction : function(action){
11147         var o = action.options;
11148         
11149         if(this.loadMask){
11150             
11151             if(this.maskBody){
11152                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11153             } else {
11154                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155             }
11156         }
11157         // not really supported yet.. ??
11158
11159         //if(this.waitMsgTarget === true){
11160         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11161         //}else if(this.waitMsgTarget){
11162         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11163         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11164         //}else {
11165         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11166        // }
11167
11168     },
11169
11170     // private
11171     afterAction : function(action, success){
11172         this.activeAction = null;
11173         var o = action.options;
11174
11175         if(this.loadMask){
11176             
11177             if(this.maskBody){
11178                 Roo.get(document.body).unmask();
11179             } else {
11180                 this.el.unmask();
11181             }
11182         }
11183         
11184         //if(this.waitMsgTarget === true){
11185 //            this.el.unmask();
11186         //}else if(this.waitMsgTarget){
11187         //    this.waitMsgTarget.unmask();
11188         //}else{
11189         //    Roo.MessageBox.updateProgress(1);
11190         //    Roo.MessageBox.hide();
11191        // }
11192         //
11193         if(success){
11194             if(o.reset){
11195                 this.reset();
11196             }
11197             Roo.callback(o.success, o.scope, [this, action]);
11198             this.fireEvent('actioncomplete', this, action);
11199
11200         }else{
11201
11202             // failure condition..
11203             // we have a scenario where updates need confirming.
11204             // eg. if a locking scenario exists..
11205             // we look for { errors : { needs_confirm : true }} in the response.
11206             if (
11207                 (typeof(action.result) != 'undefined')  &&
11208                 (typeof(action.result.errors) != 'undefined')  &&
11209                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11210            ){
11211                 var _t = this;
11212                 Roo.log("not supported yet");
11213                  /*
11214
11215                 Roo.MessageBox.confirm(
11216                     "Change requires confirmation",
11217                     action.result.errorMsg,
11218                     function(r) {
11219                         if (r != 'yes') {
11220                             return;
11221                         }
11222                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11223                     }
11224
11225                 );
11226                 */
11227
11228
11229                 return;
11230             }
11231
11232             Roo.callback(o.failure, o.scope, [this, action]);
11233             // show an error message if no failed handler is set..
11234             if (!this.hasListener('actionfailed')) {
11235                 Roo.log("need to add dialog support");
11236                 /*
11237                 Roo.MessageBox.alert("Error",
11238                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11239                         action.result.errorMsg :
11240                         "Saving Failed, please check your entries or try again"
11241                 );
11242                 */
11243             }
11244
11245             this.fireEvent('actionfailed', this, action);
11246         }
11247
11248     },
11249     /**
11250      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11251      * @param {String} id The value to search for
11252      * @return Field
11253      */
11254     findField : function(id){
11255         var items = this.getItems();
11256         var field = items.get(id);
11257         if(!field){
11258              items.each(function(f){
11259                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11260                     field = f;
11261                     return false;
11262                 }
11263                 return true;
11264             });
11265         }
11266         return field || null;
11267     },
11268      /**
11269      * Mark fields in this form invalid in bulk.
11270      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11271      * @return {BasicForm} this
11272      */
11273     markInvalid : function(errors){
11274         if(errors instanceof Array){
11275             for(var i = 0, len = errors.length; i < len; i++){
11276                 var fieldError = errors[i];
11277                 var f = this.findField(fieldError.id);
11278                 if(f){
11279                     f.markInvalid(fieldError.msg);
11280                 }
11281             }
11282         }else{
11283             var field, id;
11284             for(id in errors){
11285                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11286                     field.markInvalid(errors[id]);
11287                 }
11288             }
11289         }
11290         //Roo.each(this.childForms || [], function (f) {
11291         //    f.markInvalid(errors);
11292         //});
11293
11294         return this;
11295     },
11296
11297     /**
11298      * Set values for fields in this form in bulk.
11299      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11300      * @return {BasicForm} this
11301      */
11302     setValues : function(values){
11303         if(values instanceof Array){ // array of objects
11304             for(var i = 0, len = values.length; i < len; i++){
11305                 var v = values[i];
11306                 var f = this.findField(v.id);
11307                 if(f){
11308                     f.setValue(v.value);
11309                     if(this.trackResetOnLoad){
11310                         f.originalValue = f.getValue();
11311                     }
11312                 }
11313             }
11314         }else{ // object hash
11315             var field, id;
11316             for(id in values){
11317                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11318
11319                     if (field.setFromData &&
11320                         field.valueField &&
11321                         field.displayField &&
11322                         // combos' with local stores can
11323                         // be queried via setValue()
11324                         // to set their value..
11325                         (field.store && !field.store.isLocal)
11326                         ) {
11327                         // it's a combo
11328                         var sd = { };
11329                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11330                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11331                         field.setFromData(sd);
11332
11333                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11334                         
11335                         field.setFromData(values);
11336                         
11337                     } else {
11338                         field.setValue(values[id]);
11339                     }
11340
11341
11342                     if(this.trackResetOnLoad){
11343                         field.originalValue = field.getValue();
11344                     }
11345                 }
11346             }
11347         }
11348
11349         //Roo.each(this.childForms || [], function (f) {
11350         //    f.setValues(values);
11351         //});
11352
11353         return this;
11354     },
11355
11356     /**
11357      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11358      * they are returned as an array.
11359      * @param {Boolean} asString
11360      * @return {Object}
11361      */
11362     getValues : function(asString){
11363         //if (this.childForms) {
11364             // copy values from the child forms
11365         //    Roo.each(this.childForms, function (f) {
11366         //        this.setValues(f.getValues());
11367         //    }, this);
11368         //}
11369
11370
11371
11372         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11373         if(asString === true){
11374             return fs;
11375         }
11376         return Roo.urlDecode(fs);
11377     },
11378
11379     /**
11380      * Returns the fields in this form as an object with key/value pairs.
11381      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11382      * @return {Object}
11383      */
11384     getFieldValues : function(with_hidden)
11385     {
11386         var items = this.getItems();
11387         var ret = {};
11388         items.each(function(f){
11389             
11390             if (!f.getName()) {
11391                 return;
11392             }
11393             
11394             var v = f.getValue();
11395             
11396             if (f.inputType =='radio') {
11397                 if (typeof(ret[f.getName()]) == 'undefined') {
11398                     ret[f.getName()] = ''; // empty..
11399                 }
11400
11401                 if (!f.el.dom.checked) {
11402                     return;
11403
11404                 }
11405                 v = f.el.dom.value;
11406
11407             }
11408             
11409             if(f.xtype == 'MoneyField'){
11410                 ret[f.currencyName] = f.getCurrency();
11411             }
11412
11413             // not sure if this supported any more..
11414             if ((typeof(v) == 'object') && f.getRawValue) {
11415                 v = f.getRawValue() ; // dates..
11416             }
11417             // combo boxes where name != hiddenName...
11418             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11419                 ret[f.name] = f.getRawValue();
11420             }
11421             ret[f.getName()] = v;
11422         });
11423
11424         return ret;
11425     },
11426
11427     /**
11428      * Clears all invalid messages in this form.
11429      * @return {BasicForm} this
11430      */
11431     clearInvalid : function(){
11432         var items = this.getItems();
11433
11434         items.each(function(f){
11435            f.clearInvalid();
11436         });
11437
11438         return this;
11439     },
11440
11441     /**
11442      * Resets this form.
11443      * @return {BasicForm} this
11444      */
11445     reset : function(){
11446         var items = this.getItems();
11447         items.each(function(f){
11448             f.reset();
11449         });
11450
11451         Roo.each(this.childForms || [], function (f) {
11452             f.reset();
11453         });
11454
11455
11456         return this;
11457     },
11458     
11459     getItems : function()
11460     {
11461         var r=new Roo.util.MixedCollection(false, function(o){
11462             return o.id || (o.id = Roo.id());
11463         });
11464         var iter = function(el) {
11465             if (el.inputEl) {
11466                 r.add(el);
11467             }
11468             if (!el.items) {
11469                 return;
11470             }
11471             Roo.each(el.items,function(e) {
11472                 iter(e);
11473             });
11474         };
11475
11476         iter(this);
11477         return r;
11478     },
11479     
11480     hideFields : function(items)
11481     {
11482         Roo.each(items, function(i){
11483             
11484             var f = this.findField(i);
11485             
11486             if(!f){
11487                 return;
11488             }
11489             
11490             f.hide();
11491             
11492         }, this);
11493     },
11494     
11495     showFields : function(items)
11496     {
11497         Roo.each(items, function(i){
11498             
11499             var f = this.findField(i);
11500             
11501             if(!f){
11502                 return;
11503             }
11504             
11505             f.show();
11506             
11507         }, this);
11508     }
11509
11510 });
11511
11512 Roo.apply(Roo.bootstrap.Form, {
11513     
11514     popover : {
11515         
11516         padding : 5,
11517         
11518         isApplied : false,
11519         
11520         isMasked : false,
11521         
11522         form : false,
11523         
11524         target : false,
11525         
11526         toolTip : false,
11527         
11528         intervalID : false,
11529         
11530         maskEl : false,
11531         
11532         apply : function()
11533         {
11534             if(this.isApplied){
11535                 return;
11536             }
11537             
11538             this.maskEl = {
11539                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11540                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11541                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11542                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11543             };
11544             
11545             this.maskEl.top.enableDisplayMode("block");
11546             this.maskEl.left.enableDisplayMode("block");
11547             this.maskEl.bottom.enableDisplayMode("block");
11548             this.maskEl.right.enableDisplayMode("block");
11549             
11550             this.toolTip = new Roo.bootstrap.Tooltip({
11551                 cls : 'roo-form-error-popover',
11552                 alignment : {
11553                     'left' : ['r-l', [-2,0], 'right'],
11554                     'right' : ['l-r', [2,0], 'left'],
11555                     'bottom' : ['tl-bl', [0,2], 'top'],
11556                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11557                 }
11558             });
11559             
11560             this.toolTip.render(Roo.get(document.body));
11561
11562             this.toolTip.el.enableDisplayMode("block");
11563             
11564             Roo.get(document.body).on('click', function(){
11565                 this.unmask();
11566             }, this);
11567             
11568             Roo.get(document.body).on('touchstart', function(){
11569                 this.unmask();
11570             }, this);
11571             
11572             this.isApplied = true
11573         },
11574         
11575         mask : function(form, target)
11576         {
11577             this.form = form;
11578             
11579             this.target = target;
11580             
11581             if(!this.form.errorMask || !target.el){
11582                 return;
11583             }
11584             
11585             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11586             
11587             Roo.log(scrollable);
11588             
11589             var ot = this.target.el.calcOffsetsTo(scrollable);
11590             
11591             var scrollTo = ot[1] - this.form.maskOffset;
11592             
11593             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11594             
11595             scrollable.scrollTo('top', scrollTo);
11596             
11597             var box = this.target.el.getBox();
11598             Roo.log(box);
11599             var zIndex = Roo.bootstrap.Modal.zIndex++;
11600
11601             
11602             this.maskEl.top.setStyle('position', 'absolute');
11603             this.maskEl.top.setStyle('z-index', zIndex);
11604             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11605             this.maskEl.top.setLeft(0);
11606             this.maskEl.top.setTop(0);
11607             this.maskEl.top.show();
11608             
11609             this.maskEl.left.setStyle('position', 'absolute');
11610             this.maskEl.left.setStyle('z-index', zIndex);
11611             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11612             this.maskEl.left.setLeft(0);
11613             this.maskEl.left.setTop(box.y - this.padding);
11614             this.maskEl.left.show();
11615
11616             this.maskEl.bottom.setStyle('position', 'absolute');
11617             this.maskEl.bottom.setStyle('z-index', zIndex);
11618             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11619             this.maskEl.bottom.setLeft(0);
11620             this.maskEl.bottom.setTop(box.bottom + this.padding);
11621             this.maskEl.bottom.show();
11622
11623             this.maskEl.right.setStyle('position', 'absolute');
11624             this.maskEl.right.setStyle('z-index', zIndex);
11625             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11626             this.maskEl.right.setLeft(box.right + this.padding);
11627             this.maskEl.right.setTop(box.y - this.padding);
11628             this.maskEl.right.show();
11629
11630             this.toolTip.bindEl = this.target.el;
11631
11632             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11633
11634             var tip = this.target.blankText;
11635
11636             if(this.target.getValue() !== '' ) {
11637                 
11638                 if (this.target.invalidText.length) {
11639                     tip = this.target.invalidText;
11640                 } else if (this.target.regexText.length){
11641                     tip = this.target.regexText;
11642                 }
11643             }
11644
11645             this.toolTip.show(tip);
11646
11647             this.intervalID = window.setInterval(function() {
11648                 Roo.bootstrap.Form.popover.unmask();
11649             }, 10000);
11650
11651             window.onwheel = function(){ return false;};
11652             
11653             (function(){ this.isMasked = true; }).defer(500, this);
11654             
11655         },
11656         
11657         unmask : function()
11658         {
11659             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11660                 return;
11661             }
11662             
11663             this.maskEl.top.setStyle('position', 'absolute');
11664             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11665             this.maskEl.top.hide();
11666
11667             this.maskEl.left.setStyle('position', 'absolute');
11668             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11669             this.maskEl.left.hide();
11670
11671             this.maskEl.bottom.setStyle('position', 'absolute');
11672             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11673             this.maskEl.bottom.hide();
11674
11675             this.maskEl.right.setStyle('position', 'absolute');
11676             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11677             this.maskEl.right.hide();
11678             
11679             this.toolTip.hide();
11680             
11681             this.toolTip.el.hide();
11682             
11683             window.onwheel = function(){ return true;};
11684             
11685             if(this.intervalID){
11686                 window.clearInterval(this.intervalID);
11687                 this.intervalID = false;
11688             }
11689             
11690             this.isMasked = false;
11691             
11692         }
11693         
11694     }
11695     
11696 });
11697
11698 /*
11699  * Based on:
11700  * Ext JS Library 1.1.1
11701  * Copyright(c) 2006-2007, Ext JS, LLC.
11702  *
11703  * Originally Released Under LGPL - original licence link has changed is not relivant.
11704  *
11705  * Fork - LGPL
11706  * <script type="text/javascript">
11707  */
11708 /**
11709  * @class Roo.form.VTypes
11710  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11711  * @singleton
11712  */
11713 Roo.form.VTypes = function(){
11714     // closure these in so they are only created once.
11715     var alpha = /^[a-zA-Z_]+$/;
11716     var alphanum = /^[a-zA-Z0-9_]+$/;
11717     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11718     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11719
11720     // All these messages and functions are configurable
11721     return {
11722         /**
11723          * The function used to validate email addresses
11724          * @param {String} value The email address
11725          */
11726         'email' : function(v){
11727             return email.test(v);
11728         },
11729         /**
11730          * The error text to display when the email validation function returns false
11731          * @type String
11732          */
11733         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11734         /**
11735          * The keystroke filter mask to be applied on email input
11736          * @type RegExp
11737          */
11738         'emailMask' : /[a-z0-9_\.\-@]/i,
11739
11740         /**
11741          * The function used to validate URLs
11742          * @param {String} value The URL
11743          */
11744         'url' : function(v){
11745             return url.test(v);
11746         },
11747         /**
11748          * The error text to display when the url validation function returns false
11749          * @type String
11750          */
11751         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11752         
11753         /**
11754          * The function used to validate alpha values
11755          * @param {String} value The value
11756          */
11757         'alpha' : function(v){
11758             return alpha.test(v);
11759         },
11760         /**
11761          * The error text to display when the alpha validation function returns false
11762          * @type String
11763          */
11764         'alphaText' : 'This field should only contain letters and _',
11765         /**
11766          * The keystroke filter mask to be applied on alpha input
11767          * @type RegExp
11768          */
11769         'alphaMask' : /[a-z_]/i,
11770
11771         /**
11772          * The function used to validate alphanumeric values
11773          * @param {String} value The value
11774          */
11775         'alphanum' : function(v){
11776             return alphanum.test(v);
11777         },
11778         /**
11779          * The error text to display when the alphanumeric validation function returns false
11780          * @type String
11781          */
11782         'alphanumText' : 'This field should only contain letters, numbers and _',
11783         /**
11784          * The keystroke filter mask to be applied on alphanumeric input
11785          * @type RegExp
11786          */
11787         'alphanumMask' : /[a-z0-9_]/i
11788     };
11789 }();/*
11790  * - LGPL
11791  *
11792  * Input
11793  * 
11794  */
11795
11796 /**
11797  * @class Roo.bootstrap.Input
11798  * @extends Roo.bootstrap.Component
11799  * Bootstrap Input class
11800  * @cfg {Boolean} disabled is it disabled
11801  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11802  * @cfg {String} name name of the input
11803  * @cfg {string} fieldLabel - the label associated
11804  * @cfg {string} placeholder - placeholder to put in text.
11805  * @cfg {string}  before - input group add on before
11806  * @cfg {string} after - input group add on after
11807  * @cfg {string} size - (lg|sm) or leave empty..
11808  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11809  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11810  * @cfg {Number} md colspan out of 12 for computer-sized screens
11811  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11812  * @cfg {string} value default value of the input
11813  * @cfg {Number} labelWidth set the width of label 
11814  * @cfg {Number} labellg set the width of label (1-12)
11815  * @cfg {Number} labelmd set the width of label (1-12)
11816  * @cfg {Number} labelsm set the width of label (1-12)
11817  * @cfg {Number} labelxs set the width of label (1-12)
11818  * @cfg {String} labelAlign (top|left)
11819  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11820  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11821  * @cfg {String} indicatorpos (left|right) default left
11822  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11823  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11824  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11825
11826  * @cfg {String} align (left|center|right) Default left
11827  * @cfg {Boolean} forceFeedback (true|false) Default false
11828  * 
11829  * @constructor
11830  * Create a new Input
11831  * @param {Object} config The config object
11832  */
11833
11834 Roo.bootstrap.Input = function(config){
11835     
11836     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11837     
11838     this.addEvents({
11839         /**
11840          * @event focus
11841          * Fires when this field receives input focus.
11842          * @param {Roo.form.Field} this
11843          */
11844         focus : true,
11845         /**
11846          * @event blur
11847          * Fires when this field loses input focus.
11848          * @param {Roo.form.Field} this
11849          */
11850         blur : true,
11851         /**
11852          * @event specialkey
11853          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11854          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11855          * @param {Roo.form.Field} this
11856          * @param {Roo.EventObject} e The event object
11857          */
11858         specialkey : true,
11859         /**
11860          * @event change
11861          * Fires just before the field blurs if the field value has changed.
11862          * @param {Roo.form.Field} this
11863          * @param {Mixed} newValue The new value
11864          * @param {Mixed} oldValue The original value
11865          */
11866         change : true,
11867         /**
11868          * @event invalid
11869          * Fires after the field has been marked as invalid.
11870          * @param {Roo.form.Field} this
11871          * @param {String} msg The validation message
11872          */
11873         invalid : true,
11874         /**
11875          * @event valid
11876          * Fires after the field has been validated with no errors.
11877          * @param {Roo.form.Field} this
11878          */
11879         valid : true,
11880          /**
11881          * @event keyup
11882          * Fires after the key up
11883          * @param {Roo.form.Field} this
11884          * @param {Roo.EventObject}  e The event Object
11885          */
11886         keyup : true,
11887         /**
11888          * @event paste
11889          * Fires after the user pastes into input
11890          * @param {Roo.form.Field} this
11891          * @param {Roo.EventObject}  e The event Object
11892          */
11893         paste : true
11894     });
11895 };
11896
11897 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11898      /**
11899      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11900       automatic validation (defaults to "keyup").
11901      */
11902     validationEvent : "keyup",
11903      /**
11904      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11905      */
11906     validateOnBlur : true,
11907     /**
11908      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11909      */
11910     validationDelay : 250,
11911      /**
11912      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11913      */
11914     focusClass : "x-form-focus",  // not needed???
11915     
11916        
11917     /**
11918      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11919      */
11920     invalidClass : "has-warning",
11921     
11922     /**
11923      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11924      */
11925     validClass : "has-success",
11926     
11927     /**
11928      * @cfg {Boolean} hasFeedback (true|false) default true
11929      */
11930     hasFeedback : true,
11931     
11932     /**
11933      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11934      */
11935     invalidFeedbackClass : "glyphicon-warning-sign",
11936     
11937     /**
11938      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11939      */
11940     validFeedbackClass : "glyphicon-ok",
11941     
11942     /**
11943      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11944      */
11945     selectOnFocus : false,
11946     
11947      /**
11948      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11949      */
11950     maskRe : null,
11951        /**
11952      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11953      */
11954     vtype : null,
11955     
11956       /**
11957      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11958      */
11959     disableKeyFilter : false,
11960     
11961        /**
11962      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11963      */
11964     disabled : false,
11965      /**
11966      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11967      */
11968     allowBlank : true,
11969     /**
11970      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11971      */
11972     blankText : "Please complete this mandatory field",
11973     
11974      /**
11975      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11976      */
11977     minLength : 0,
11978     /**
11979      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11980      */
11981     maxLength : Number.MAX_VALUE,
11982     /**
11983      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11984      */
11985     minLengthText : "The minimum length for this field is {0}",
11986     /**
11987      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11988      */
11989     maxLengthText : "The maximum length for this field is {0}",
11990   
11991     
11992     /**
11993      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11994      * If available, this function will be called only after the basic validators all return true, and will be passed the
11995      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11996      */
11997     validator : null,
11998     /**
11999      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12000      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12001      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12002      */
12003     regex : null,
12004     /**
12005      * @cfg {String} regexText -- Depricated - use Invalid Text
12006      */
12007     regexText : "",
12008     
12009     /**
12010      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12011      */
12012     invalidText : "",
12013     
12014     
12015     
12016     autocomplete: false,
12017     
12018     
12019     fieldLabel : '',
12020     inputType : 'text',
12021     
12022     name : false,
12023     placeholder: false,
12024     before : false,
12025     after : false,
12026     size : false,
12027     hasFocus : false,
12028     preventMark: false,
12029     isFormField : true,
12030     value : '',
12031     labelWidth : 2,
12032     labelAlign : false,
12033     readOnly : false,
12034     align : false,
12035     formatedValue : false,
12036     forceFeedback : false,
12037     
12038     indicatorpos : 'left',
12039     
12040     labellg : 0,
12041     labelmd : 0,
12042     labelsm : 0,
12043     labelxs : 0,
12044     
12045     capture : '',
12046     accept : '',
12047     
12048     parentLabelAlign : function()
12049     {
12050         var parent = this;
12051         while (parent.parent()) {
12052             parent = parent.parent();
12053             if (typeof(parent.labelAlign) !='undefined') {
12054                 return parent.labelAlign;
12055             }
12056         }
12057         return 'left';
12058         
12059     },
12060     
12061     getAutoCreate : function()
12062     {
12063         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12064         
12065         var id = Roo.id();
12066         
12067         var cfg = {};
12068         
12069         if(this.inputType != 'hidden'){
12070             cfg.cls = 'form-group' //input-group
12071         }
12072         
12073         var input =  {
12074             tag: 'input',
12075             id : id,
12076             type : this.inputType,
12077             value : this.value,
12078             cls : 'form-control',
12079             placeholder : this.placeholder || '',
12080             autocomplete : this.autocomplete || 'new-password'
12081         };
12082         if (this.inputType == 'file') {
12083             input.style = 'overflow:hidden'; // why not in CSS?
12084         }
12085         
12086         if(this.capture.length){
12087             input.capture = this.capture;
12088         }
12089         
12090         if(this.accept.length){
12091             input.accept = this.accept + "/*";
12092         }
12093         
12094         if(this.align){
12095             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12096         }
12097         
12098         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12099             input.maxLength = this.maxLength;
12100         }
12101         
12102         if (this.disabled) {
12103             input.disabled=true;
12104         }
12105         
12106         if (this.readOnly) {
12107             input.readonly=true;
12108         }
12109         
12110         if (this.name) {
12111             input.name = this.name;
12112         }
12113         
12114         if (this.size) {
12115             input.cls += ' input-' + this.size;
12116         }
12117         
12118         var settings=this;
12119         ['xs','sm','md','lg'].map(function(size){
12120             if (settings[size]) {
12121                 cfg.cls += ' col-' + size + '-' + settings[size];
12122             }
12123         });
12124         
12125         var inputblock = input;
12126         
12127         var feedback = {
12128             tag: 'span',
12129             cls: 'glyphicon form-control-feedback'
12130         };
12131             
12132         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12133             
12134             inputblock = {
12135                 cls : 'has-feedback',
12136                 cn :  [
12137                     input,
12138                     feedback
12139                 ] 
12140             };  
12141         }
12142         
12143         if (this.before || this.after) {
12144             
12145             inputblock = {
12146                 cls : 'input-group',
12147                 cn :  [] 
12148             };
12149             
12150             if (this.before && typeof(this.before) == 'string') {
12151                 
12152                 inputblock.cn.push({
12153                     tag :'span',
12154                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12155                     html : this.before
12156                 });
12157             }
12158             if (this.before && typeof(this.before) == 'object') {
12159                 this.before = Roo.factory(this.before);
12160                 
12161                 inputblock.cn.push({
12162                     tag :'span',
12163                     cls : 'roo-input-before input-group-prepend   input-group-' +
12164                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12165                 });
12166             }
12167             
12168             inputblock.cn.push(input);
12169             
12170             if (this.after && typeof(this.after) == 'string') {
12171                 inputblock.cn.push({
12172                     tag :'span',
12173                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12174                     html : this.after
12175                 });
12176             }
12177             if (this.after && typeof(this.after) == 'object') {
12178                 this.after = Roo.factory(this.after);
12179                 
12180                 inputblock.cn.push({
12181                     tag :'span',
12182                     cls : 'roo-input-after input-group-append  input-group-' +
12183                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12184                 });
12185             }
12186             
12187             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12188                 inputblock.cls += ' has-feedback';
12189                 inputblock.cn.push(feedback);
12190             }
12191         };
12192         var indicator = {
12193             tag : 'i',
12194             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12195             tooltip : 'This field is required'
12196         };
12197         if (this.allowBlank ) {
12198             indicator.style = this.allowBlank ? ' display:none' : '';
12199         }
12200         if (align ==='left' && this.fieldLabel.length) {
12201             
12202             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12203             
12204             cfg.cn = [
12205                 indicator,
12206                 {
12207                     tag: 'label',
12208                     'for' :  id,
12209                     cls : 'control-label col-form-label',
12210                     html : this.fieldLabel
12211
12212                 },
12213                 {
12214                     cls : "", 
12215                     cn: [
12216                         inputblock
12217                     ]
12218                 }
12219             ];
12220             
12221             var labelCfg = cfg.cn[1];
12222             var contentCfg = cfg.cn[2];
12223             
12224             if(this.indicatorpos == 'right'){
12225                 cfg.cn = [
12226                     {
12227                         tag: 'label',
12228                         'for' :  id,
12229                         cls : 'control-label col-form-label',
12230                         cn : [
12231                             {
12232                                 tag : 'span',
12233                                 html : this.fieldLabel
12234                             },
12235                             indicator
12236                         ]
12237                     },
12238                     {
12239                         cls : "",
12240                         cn: [
12241                             inputblock
12242                         ]
12243                     }
12244
12245                 ];
12246                 
12247                 labelCfg = cfg.cn[0];
12248                 contentCfg = cfg.cn[1];
12249             
12250             }
12251             
12252             if(this.labelWidth > 12){
12253                 labelCfg.style = "width: " + this.labelWidth + 'px';
12254             }
12255             
12256             if(this.labelWidth < 13 && this.labelmd == 0){
12257                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12258             }
12259             
12260             if(this.labellg > 0){
12261                 labelCfg.cls += ' col-lg-' + this.labellg;
12262                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12263             }
12264             
12265             if(this.labelmd > 0){
12266                 labelCfg.cls += ' col-md-' + this.labelmd;
12267                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12268             }
12269             
12270             if(this.labelsm > 0){
12271                 labelCfg.cls += ' col-sm-' + this.labelsm;
12272                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12273             }
12274             
12275             if(this.labelxs > 0){
12276                 labelCfg.cls += ' col-xs-' + this.labelxs;
12277                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12278             }
12279             
12280             
12281         } else if ( this.fieldLabel.length) {
12282                 
12283             
12284             
12285             cfg.cn = [
12286                 {
12287                     tag : 'i',
12288                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12289                     tooltip : 'This field is required',
12290                     style : this.allowBlank ? ' display:none' : '' 
12291                 },
12292                 {
12293                     tag: 'label',
12294                    //cls : 'input-group-addon',
12295                     html : this.fieldLabel
12296
12297                 },
12298
12299                inputblock
12300
12301            ];
12302            
12303            if(this.indicatorpos == 'right'){
12304        
12305                 cfg.cn = [
12306                     {
12307                         tag: 'label',
12308                        //cls : 'input-group-addon',
12309                         html : this.fieldLabel
12310
12311                     },
12312                     {
12313                         tag : 'i',
12314                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12315                         tooltip : 'This field is required',
12316                         style : this.allowBlank ? ' display:none' : '' 
12317                     },
12318
12319                    inputblock
12320
12321                ];
12322
12323             }
12324
12325         } else {
12326             
12327             cfg.cn = [
12328
12329                     inputblock
12330
12331             ];
12332                 
12333                 
12334         };
12335         
12336         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12337            cfg.cls += ' navbar-form';
12338         }
12339         
12340         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12341             // on BS4 we do this only if not form 
12342             cfg.cls += ' navbar-form';
12343             cfg.tag = 'li';
12344         }
12345         
12346         return cfg;
12347         
12348     },
12349     /**
12350      * return the real input element.
12351      */
12352     inputEl: function ()
12353     {
12354         return this.el.select('input.form-control',true).first();
12355     },
12356     
12357     tooltipEl : function()
12358     {
12359         return this.inputEl();
12360     },
12361     
12362     indicatorEl : function()
12363     {
12364         if (Roo.bootstrap.version == 4) {
12365             return false; // not enabled in v4 yet.
12366         }
12367         
12368         var indicator = this.el.select('i.roo-required-indicator',true).first();
12369         
12370         if(!indicator){
12371             return false;
12372         }
12373         
12374         return indicator;
12375         
12376     },
12377     
12378     setDisabled : function(v)
12379     {
12380         var i  = this.inputEl().dom;
12381         if (!v) {
12382             i.removeAttribute('disabled');
12383             return;
12384             
12385         }
12386         i.setAttribute('disabled','true');
12387     },
12388     initEvents : function()
12389     {
12390           
12391         this.inputEl().on("keydown" , this.fireKey,  this);
12392         this.inputEl().on("focus", this.onFocus,  this);
12393         this.inputEl().on("blur", this.onBlur,  this);
12394         
12395         this.inputEl().relayEvent('keyup', this);
12396         this.inputEl().relayEvent('paste', this);
12397         
12398         this.indicator = this.indicatorEl();
12399         
12400         if(this.indicator){
12401             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12402         }
12403  
12404         // reference to original value for reset
12405         this.originalValue = this.getValue();
12406         //Roo.form.TextField.superclass.initEvents.call(this);
12407         if(this.validationEvent == 'keyup'){
12408             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12409             this.inputEl().on('keyup', this.filterValidation, this);
12410         }
12411         else if(this.validationEvent !== false){
12412             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12413         }
12414         
12415         if(this.selectOnFocus){
12416             this.on("focus", this.preFocus, this);
12417             
12418         }
12419         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12420             this.inputEl().on("keypress", this.filterKeys, this);
12421         } else {
12422             this.inputEl().relayEvent('keypress', this);
12423         }
12424        /* if(this.grow){
12425             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12426             this.el.on("click", this.autoSize,  this);
12427         }
12428         */
12429         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12430             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12431         }
12432         
12433         if (typeof(this.before) == 'object') {
12434             this.before.render(this.el.select('.roo-input-before',true).first());
12435         }
12436         if (typeof(this.after) == 'object') {
12437             this.after.render(this.el.select('.roo-input-after',true).first());
12438         }
12439         
12440         this.inputEl().on('change', this.onChange, this);
12441         
12442     },
12443     filterValidation : function(e){
12444         if(!e.isNavKeyPress()){
12445             this.validationTask.delay(this.validationDelay);
12446         }
12447     },
12448      /**
12449      * Validates the field value
12450      * @return {Boolean} True if the value is valid, else false
12451      */
12452     validate : function(){
12453         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12454         if(this.disabled || this.validateValue(this.getRawValue())){
12455             this.markValid();
12456             return true;
12457         }
12458         
12459         this.markInvalid();
12460         return false;
12461     },
12462     
12463     
12464     /**
12465      * Validates a value according to the field's validation rules and marks the field as invalid
12466      * if the validation fails
12467      * @param {Mixed} value The value to validate
12468      * @return {Boolean} True if the value is valid, else false
12469      */
12470     validateValue : function(value)
12471     {
12472         if(this.getVisibilityEl().hasClass('hidden')){
12473             return true;
12474         }
12475         
12476         if(value.length < 1)  { // if it's blank
12477             if(this.allowBlank){
12478                 return true;
12479             }
12480             return false;
12481         }
12482         
12483         if(value.length < this.minLength){
12484             return false;
12485         }
12486         if(value.length > this.maxLength){
12487             return false;
12488         }
12489         if(this.vtype){
12490             var vt = Roo.form.VTypes;
12491             if(!vt[this.vtype](value, this)){
12492                 return false;
12493             }
12494         }
12495         if(typeof this.validator == "function"){
12496             var msg = this.validator(value);
12497             if(msg !== true){
12498                 return false;
12499             }
12500             if (typeof(msg) == 'string') {
12501                 this.invalidText = msg;
12502             }
12503         }
12504         
12505         if(this.regex && !this.regex.test(value)){
12506             return false;
12507         }
12508         
12509         return true;
12510     },
12511     
12512      // private
12513     fireKey : function(e){
12514         //Roo.log('field ' + e.getKey());
12515         if(e.isNavKeyPress()){
12516             this.fireEvent("specialkey", this, e);
12517         }
12518     },
12519     focus : function (selectText){
12520         if(this.rendered){
12521             this.inputEl().focus();
12522             if(selectText === true){
12523                 this.inputEl().dom.select();
12524             }
12525         }
12526         return this;
12527     } ,
12528     
12529     onFocus : function(){
12530         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12531            // this.el.addClass(this.focusClass);
12532         }
12533         if(!this.hasFocus){
12534             this.hasFocus = true;
12535             this.startValue = this.getValue();
12536             this.fireEvent("focus", this);
12537         }
12538     },
12539     
12540     beforeBlur : Roo.emptyFn,
12541
12542     
12543     // private
12544     onBlur : function(){
12545         this.beforeBlur();
12546         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12547             //this.el.removeClass(this.focusClass);
12548         }
12549         this.hasFocus = false;
12550         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12551             this.validate();
12552         }
12553         var v = this.getValue();
12554         if(String(v) !== String(this.startValue)){
12555             this.fireEvent('change', this, v, this.startValue);
12556         }
12557         this.fireEvent("blur", this);
12558     },
12559     
12560     onChange : function(e)
12561     {
12562         var v = this.getValue();
12563         if(String(v) !== String(this.startValue)){
12564             this.fireEvent('change', this, v, this.startValue);
12565         }
12566         
12567     },
12568     
12569     /**
12570      * Resets the current field value to the originally loaded value and clears any validation messages
12571      */
12572     reset : function(){
12573         this.setValue(this.originalValue);
12574         this.validate();
12575     },
12576      /**
12577      * Returns the name of the field
12578      * @return {Mixed} name The name field
12579      */
12580     getName: function(){
12581         return this.name;
12582     },
12583      /**
12584      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12585      * @return {Mixed} value The field value
12586      */
12587     getValue : function(){
12588         
12589         var v = this.inputEl().getValue();
12590         
12591         return v;
12592     },
12593     /**
12594      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12595      * @return {Mixed} value The field value
12596      */
12597     getRawValue : function(){
12598         var v = this.inputEl().getValue();
12599         
12600         return v;
12601     },
12602     
12603     /**
12604      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12605      * @param {Mixed} value The value to set
12606      */
12607     setRawValue : function(v){
12608         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12609     },
12610     
12611     selectText : function(start, end){
12612         var v = this.getRawValue();
12613         if(v.length > 0){
12614             start = start === undefined ? 0 : start;
12615             end = end === undefined ? v.length : end;
12616             var d = this.inputEl().dom;
12617             if(d.setSelectionRange){
12618                 d.setSelectionRange(start, end);
12619             }else if(d.createTextRange){
12620                 var range = d.createTextRange();
12621                 range.moveStart("character", start);
12622                 range.moveEnd("character", v.length-end);
12623                 range.select();
12624             }
12625         }
12626     },
12627     
12628     /**
12629      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12630      * @param {Mixed} value The value to set
12631      */
12632     setValue : function(v){
12633         this.value = v;
12634         if(this.rendered){
12635             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12636             this.validate();
12637         }
12638     },
12639     
12640     /*
12641     processValue : function(value){
12642         if(this.stripCharsRe){
12643             var newValue = value.replace(this.stripCharsRe, '');
12644             if(newValue !== value){
12645                 this.setRawValue(newValue);
12646                 return newValue;
12647             }
12648         }
12649         return value;
12650     },
12651   */
12652     preFocus : function(){
12653         
12654         if(this.selectOnFocus){
12655             this.inputEl().dom.select();
12656         }
12657     },
12658     filterKeys : function(e){
12659         var k = e.getKey();
12660         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12661             return;
12662         }
12663         var c = e.getCharCode(), cc = String.fromCharCode(c);
12664         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12665             return;
12666         }
12667         if(!this.maskRe.test(cc)){
12668             e.stopEvent();
12669         }
12670     },
12671      /**
12672      * Clear any invalid styles/messages for this field
12673      */
12674     clearInvalid : function(){
12675         
12676         if(!this.el || this.preventMark){ // not rendered
12677             return;
12678         }
12679         
12680         
12681         this.el.removeClass([this.invalidClass, 'is-invalid']);
12682         
12683         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12684             
12685             var feedback = this.el.select('.form-control-feedback', true).first();
12686             
12687             if(feedback){
12688                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12689             }
12690             
12691         }
12692         
12693         if(this.indicator){
12694             this.indicator.removeClass('visible');
12695             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12696         }
12697         
12698         this.fireEvent('valid', this);
12699     },
12700     
12701      /**
12702      * Mark this field as valid
12703      */
12704     markValid : function()
12705     {
12706         if(!this.el  || this.preventMark){ // not rendered...
12707             return;
12708         }
12709         
12710         this.el.removeClass([this.invalidClass, this.validClass]);
12711         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12712
12713         var feedback = this.el.select('.form-control-feedback', true).first();
12714             
12715         if(feedback){
12716             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12717         }
12718         
12719         if(this.indicator){
12720             this.indicator.removeClass('visible');
12721             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12722         }
12723         
12724         if(this.disabled){
12725             return;
12726         }
12727         
12728            
12729         if(this.allowBlank && !this.getRawValue().length){
12730             return;
12731         }
12732         if (Roo.bootstrap.version == 3) {
12733             this.el.addClass(this.validClass);
12734         } else {
12735             this.inputEl().addClass('is-valid');
12736         }
12737
12738         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12739             
12740             var feedback = this.el.select('.form-control-feedback', true).first();
12741             
12742             if(feedback){
12743                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12744                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12745             }
12746             
12747         }
12748         
12749         this.fireEvent('valid', this);
12750     },
12751     
12752      /**
12753      * Mark this field as invalid
12754      * @param {String} msg The validation message
12755      */
12756     markInvalid : function(msg)
12757     {
12758         if(!this.el  || this.preventMark){ // not rendered
12759             return;
12760         }
12761         
12762         this.el.removeClass([this.invalidClass, this.validClass]);
12763         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12764         
12765         var feedback = this.el.select('.form-control-feedback', true).first();
12766             
12767         if(feedback){
12768             this.el.select('.form-control-feedback', true).first().removeClass(
12769                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12770         }
12771
12772         if(this.disabled){
12773             return;
12774         }
12775         
12776         if(this.allowBlank && !this.getRawValue().length){
12777             return;
12778         }
12779         
12780         if(this.indicator){
12781             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12782             this.indicator.addClass('visible');
12783         }
12784         if (Roo.bootstrap.version == 3) {
12785             this.el.addClass(this.invalidClass);
12786         } else {
12787             this.inputEl().addClass('is-invalid');
12788         }
12789         
12790         
12791         
12792         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12793             
12794             var feedback = this.el.select('.form-control-feedback', true).first();
12795             
12796             if(feedback){
12797                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12798                 
12799                 if(this.getValue().length || this.forceFeedback){
12800                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12801                 }
12802                 
12803             }
12804             
12805         }
12806         
12807         this.fireEvent('invalid', this, msg);
12808     },
12809     // private
12810     SafariOnKeyDown : function(event)
12811     {
12812         // this is a workaround for a password hang bug on chrome/ webkit.
12813         if (this.inputEl().dom.type != 'password') {
12814             return;
12815         }
12816         
12817         var isSelectAll = false;
12818         
12819         if(this.inputEl().dom.selectionEnd > 0){
12820             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12821         }
12822         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12823             event.preventDefault();
12824             this.setValue('');
12825             return;
12826         }
12827         
12828         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12829             
12830             event.preventDefault();
12831             // this is very hacky as keydown always get's upper case.
12832             //
12833             var cc = String.fromCharCode(event.getCharCode());
12834             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12835             
12836         }
12837     },
12838     adjustWidth : function(tag, w){
12839         tag = tag.toLowerCase();
12840         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12841             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12842                 if(tag == 'input'){
12843                     return w + 2;
12844                 }
12845                 if(tag == 'textarea'){
12846                     return w-2;
12847                 }
12848             }else if(Roo.isOpera){
12849                 if(tag == 'input'){
12850                     return w + 2;
12851                 }
12852                 if(tag == 'textarea'){
12853                     return w-2;
12854                 }
12855             }
12856         }
12857         return w;
12858     },
12859     
12860     setFieldLabel : function(v)
12861     {
12862         if(!this.rendered){
12863             return;
12864         }
12865         
12866         if(this.indicatorEl()){
12867             var ar = this.el.select('label > span',true);
12868             
12869             if (ar.elements.length) {
12870                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12871                 this.fieldLabel = v;
12872                 return;
12873             }
12874             
12875             var br = this.el.select('label',true);
12876             
12877             if(br.elements.length) {
12878                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12879                 this.fieldLabel = v;
12880                 return;
12881             }
12882             
12883             Roo.log('Cannot Found any of label > span || label in input');
12884             return;
12885         }
12886         
12887         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12888         this.fieldLabel = v;
12889         
12890         
12891     }
12892 });
12893
12894  
12895 /*
12896  * - LGPL
12897  *
12898  * Input
12899  * 
12900  */
12901
12902 /**
12903  * @class Roo.bootstrap.TextArea
12904  * @extends Roo.bootstrap.Input
12905  * Bootstrap TextArea class
12906  * @cfg {Number} cols Specifies the visible width of a text area
12907  * @cfg {Number} rows Specifies the visible number of lines in a text area
12908  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12909  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12910  * @cfg {string} html text
12911  * 
12912  * @constructor
12913  * Create a new TextArea
12914  * @param {Object} config The config object
12915  */
12916
12917 Roo.bootstrap.TextArea = function(config){
12918     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12919    
12920 };
12921
12922 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12923      
12924     cols : false,
12925     rows : 5,
12926     readOnly : false,
12927     warp : 'soft',
12928     resize : false,
12929     value: false,
12930     html: false,
12931     
12932     getAutoCreate : function(){
12933         
12934         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12935         
12936         var id = Roo.id();
12937         
12938         var cfg = {};
12939         
12940         if(this.inputType != 'hidden'){
12941             cfg.cls = 'form-group' //input-group
12942         }
12943         
12944         var input =  {
12945             tag: 'textarea',
12946             id : id,
12947             warp : this.warp,
12948             rows : this.rows,
12949             value : this.value || '',
12950             html: this.html || '',
12951             cls : 'form-control',
12952             placeholder : this.placeholder || '' 
12953             
12954         };
12955         
12956         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12957             input.maxLength = this.maxLength;
12958         }
12959         
12960         if(this.resize){
12961             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12962         }
12963         
12964         if(this.cols){
12965             input.cols = this.cols;
12966         }
12967         
12968         if (this.readOnly) {
12969             input.readonly = true;
12970         }
12971         
12972         if (this.name) {
12973             input.name = this.name;
12974         }
12975         
12976         if (this.size) {
12977             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12978         }
12979         
12980         var settings=this;
12981         ['xs','sm','md','lg'].map(function(size){
12982             if (settings[size]) {
12983                 cfg.cls += ' col-' + size + '-' + settings[size];
12984             }
12985         });
12986         
12987         var inputblock = input;
12988         
12989         if(this.hasFeedback && !this.allowBlank){
12990             
12991             var feedback = {
12992                 tag: 'span',
12993                 cls: 'glyphicon form-control-feedback'
12994             };
12995
12996             inputblock = {
12997                 cls : 'has-feedback',
12998                 cn :  [
12999                     input,
13000                     feedback
13001                 ] 
13002             };  
13003         }
13004         
13005         
13006         if (this.before || this.after) {
13007             
13008             inputblock = {
13009                 cls : 'input-group',
13010                 cn :  [] 
13011             };
13012             if (this.before) {
13013                 inputblock.cn.push({
13014                     tag :'span',
13015                     cls : 'input-group-addon',
13016                     html : this.before
13017                 });
13018             }
13019             
13020             inputblock.cn.push(input);
13021             
13022             if(this.hasFeedback && !this.allowBlank){
13023                 inputblock.cls += ' has-feedback';
13024                 inputblock.cn.push(feedback);
13025             }
13026             
13027             if (this.after) {
13028                 inputblock.cn.push({
13029                     tag :'span',
13030                     cls : 'input-group-addon',
13031                     html : this.after
13032                 });
13033             }
13034             
13035         }
13036         
13037         if (align ==='left' && this.fieldLabel.length) {
13038             cfg.cn = [
13039                 {
13040                     tag: 'label',
13041                     'for' :  id,
13042                     cls : 'control-label',
13043                     html : this.fieldLabel
13044                 },
13045                 {
13046                     cls : "",
13047                     cn: [
13048                         inputblock
13049                     ]
13050                 }
13051
13052             ];
13053             
13054             if(this.labelWidth > 12){
13055                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13056             }
13057
13058             if(this.labelWidth < 13 && this.labelmd == 0){
13059                 this.labelmd = this.labelWidth;
13060             }
13061
13062             if(this.labellg > 0){
13063                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13064                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13065             }
13066
13067             if(this.labelmd > 0){
13068                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13069                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13070             }
13071
13072             if(this.labelsm > 0){
13073                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13074                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13075             }
13076
13077             if(this.labelxs > 0){
13078                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13079                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13080             }
13081             
13082         } else if ( this.fieldLabel.length) {
13083             cfg.cn = [
13084
13085                {
13086                    tag: 'label',
13087                    //cls : 'input-group-addon',
13088                    html : this.fieldLabel
13089
13090                },
13091
13092                inputblock
13093
13094            ];
13095
13096         } else {
13097
13098             cfg.cn = [
13099
13100                 inputblock
13101
13102             ];
13103                 
13104         }
13105         
13106         if (this.disabled) {
13107             input.disabled=true;
13108         }
13109         
13110         return cfg;
13111         
13112     },
13113     /**
13114      * return the real textarea element.
13115      */
13116     inputEl: function ()
13117     {
13118         return this.el.select('textarea.form-control',true).first();
13119     },
13120     
13121     /**
13122      * Clear any invalid styles/messages for this field
13123      */
13124     clearInvalid : function()
13125     {
13126         
13127         if(!this.el || this.preventMark){ // not rendered
13128             return;
13129         }
13130         
13131         var label = this.el.select('label', true).first();
13132         var icon = this.el.select('i.fa-star', true).first();
13133         
13134         if(label && icon){
13135             icon.remove();
13136         }
13137         this.el.removeClass( this.validClass);
13138         this.inputEl().removeClass('is-invalid');
13139          
13140         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13141             
13142             var feedback = this.el.select('.form-control-feedback', true).first();
13143             
13144             if(feedback){
13145                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13146             }
13147             
13148         }
13149         
13150         this.fireEvent('valid', this);
13151     },
13152     
13153      /**
13154      * Mark this field as valid
13155      */
13156     markValid : function()
13157     {
13158         if(!this.el  || this.preventMark){ // not rendered
13159             return;
13160         }
13161         
13162         this.el.removeClass([this.invalidClass, this.validClass]);
13163         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13164         
13165         var feedback = this.el.select('.form-control-feedback', true).first();
13166             
13167         if(feedback){
13168             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13169         }
13170
13171         if(this.disabled || this.allowBlank){
13172             return;
13173         }
13174         
13175         var label = this.el.select('label', true).first();
13176         var icon = this.el.select('i.fa-star', true).first();
13177         
13178         if(label && icon){
13179             icon.remove();
13180         }
13181         if (Roo.bootstrap.version == 3) {
13182             this.el.addClass(this.validClass);
13183         } else {
13184             this.inputEl().addClass('is-valid');
13185         }
13186         
13187         
13188         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13189             
13190             var feedback = this.el.select('.form-control-feedback', true).first();
13191             
13192             if(feedback){
13193                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13194                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13195             }
13196             
13197         }
13198         
13199         this.fireEvent('valid', this);
13200     },
13201     
13202      /**
13203      * Mark this field as invalid
13204      * @param {String} msg The validation message
13205      */
13206     markInvalid : function(msg)
13207     {
13208         if(!this.el  || this.preventMark){ // not rendered
13209             return;
13210         }
13211         
13212         this.el.removeClass([this.invalidClass, this.validClass]);
13213         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13214         
13215         var feedback = this.el.select('.form-control-feedback', true).first();
13216             
13217         if(feedback){
13218             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13219         }
13220
13221         if(this.disabled || this.allowBlank){
13222             return;
13223         }
13224         
13225         var label = this.el.select('label', true).first();
13226         var icon = this.el.select('i.fa-star', true).first();
13227         
13228         if(!this.getValue().length && label && !icon){
13229             this.el.createChild({
13230                 tag : 'i',
13231                 cls : 'text-danger fa fa-lg fa-star',
13232                 tooltip : 'This field is required',
13233                 style : 'margin-right:5px;'
13234             }, label, true);
13235         }
13236         
13237         if (Roo.bootstrap.version == 3) {
13238             this.el.addClass(this.invalidClass);
13239         } else {
13240             this.inputEl().addClass('is-invalid');
13241         }
13242         
13243         // fixme ... this may be depricated need to test..
13244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13245             
13246             var feedback = this.el.select('.form-control-feedback', true).first();
13247             
13248             if(feedback){
13249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13250                 
13251                 if(this.getValue().length || this.forceFeedback){
13252                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13253                 }
13254                 
13255             }
13256             
13257         }
13258         
13259         this.fireEvent('invalid', this, msg);
13260     }
13261 });
13262
13263  
13264 /*
13265  * - LGPL
13266  *
13267  * trigger field - base class for combo..
13268  * 
13269  */
13270  
13271 /**
13272  * @class Roo.bootstrap.TriggerField
13273  * @extends Roo.bootstrap.Input
13274  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13275  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13276  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13277  * for which you can provide a custom implementation.  For example:
13278  * <pre><code>
13279 var trigger = new Roo.bootstrap.TriggerField();
13280 trigger.onTriggerClick = myTriggerFn;
13281 trigger.applyTo('my-field');
13282 </code></pre>
13283  *
13284  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13285  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13286  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13287  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13288  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13289
13290  * @constructor
13291  * Create a new TriggerField.
13292  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13293  * to the base TextField)
13294  */
13295 Roo.bootstrap.TriggerField = function(config){
13296     this.mimicing = false;
13297     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13298 };
13299
13300 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13301     /**
13302      * @cfg {String} triggerClass A CSS class to apply to the trigger
13303      */
13304      /**
13305      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13306      */
13307     hideTrigger:false,
13308
13309     /**
13310      * @cfg {Boolean} removable (true|false) special filter default false
13311      */
13312     removable : false,
13313     
13314     /** @cfg {Boolean} grow @hide */
13315     /** @cfg {Number} growMin @hide */
13316     /** @cfg {Number} growMax @hide */
13317
13318     /**
13319      * @hide 
13320      * @method
13321      */
13322     autoSize: Roo.emptyFn,
13323     // private
13324     monitorTab : true,
13325     // private
13326     deferHeight : true,
13327
13328     
13329     actionMode : 'wrap',
13330     
13331     caret : false,
13332     
13333     
13334     getAutoCreate : function(){
13335        
13336         var align = this.labelAlign || this.parentLabelAlign();
13337         
13338         var id = Roo.id();
13339         
13340         var cfg = {
13341             cls: 'form-group' //input-group
13342         };
13343         
13344         
13345         var input =  {
13346             tag: 'input',
13347             id : id,
13348             type : this.inputType,
13349             cls : 'form-control',
13350             autocomplete: 'new-password',
13351             placeholder : this.placeholder || '' 
13352             
13353         };
13354         if (this.name) {
13355             input.name = this.name;
13356         }
13357         if (this.size) {
13358             input.cls += ' input-' + this.size;
13359         }
13360         
13361         if (this.disabled) {
13362             input.disabled=true;
13363         }
13364         
13365         var inputblock = input;
13366         
13367         if(this.hasFeedback && !this.allowBlank){
13368             
13369             var feedback = {
13370                 tag: 'span',
13371                 cls: 'glyphicon form-control-feedback'
13372             };
13373             
13374             if(this.removable && !this.editable  ){
13375                 inputblock = {
13376                     cls : 'has-feedback',
13377                     cn :  [
13378                         inputblock,
13379                         {
13380                             tag: 'button',
13381                             html : 'x',
13382                             cls : 'roo-combo-removable-btn close'
13383                         },
13384                         feedback
13385                     ] 
13386                 };
13387             } else {
13388                 inputblock = {
13389                     cls : 'has-feedback',
13390                     cn :  [
13391                         inputblock,
13392                         feedback
13393                     ] 
13394                 };
13395             }
13396
13397         } else {
13398             if(this.removable && !this.editable ){
13399                 inputblock = {
13400                     cls : 'roo-removable',
13401                     cn :  [
13402                         inputblock,
13403                         {
13404                             tag: 'button',
13405                             html : 'x',
13406                             cls : 'roo-combo-removable-btn close'
13407                         }
13408                     ] 
13409                 };
13410             }
13411         }
13412         
13413         if (this.before || this.after) {
13414             
13415             inputblock = {
13416                 cls : 'input-group',
13417                 cn :  [] 
13418             };
13419             if (this.before) {
13420                 inputblock.cn.push({
13421                     tag :'span',
13422                     cls : 'input-group-addon input-group-prepend input-group-text',
13423                     html : this.before
13424                 });
13425             }
13426             
13427             inputblock.cn.push(input);
13428             
13429             if(this.hasFeedback && !this.allowBlank){
13430                 inputblock.cls += ' has-feedback';
13431                 inputblock.cn.push(feedback);
13432             }
13433             
13434             if (this.after) {
13435                 inputblock.cn.push({
13436                     tag :'span',
13437                     cls : 'input-group-addon input-group-append input-group-text',
13438                     html : this.after
13439                 });
13440             }
13441             
13442         };
13443         
13444       
13445         
13446         var ibwrap = inputblock;
13447         
13448         if(this.multiple){
13449             ibwrap = {
13450                 tag: 'ul',
13451                 cls: 'roo-select2-choices',
13452                 cn:[
13453                     {
13454                         tag: 'li',
13455                         cls: 'roo-select2-search-field',
13456                         cn: [
13457
13458                             inputblock
13459                         ]
13460                     }
13461                 ]
13462             };
13463                 
13464         }
13465         
13466         var combobox = {
13467             cls: 'roo-select2-container input-group',
13468             cn: [
13469                  {
13470                     tag: 'input',
13471                     type : 'hidden',
13472                     cls: 'form-hidden-field'
13473                 },
13474                 ibwrap
13475             ]
13476         };
13477         
13478         if(!this.multiple && this.showToggleBtn){
13479             
13480             var caret = {
13481                         tag: 'span',
13482                         cls: 'caret'
13483              };
13484             if (this.caret != false) {
13485                 caret = {
13486                      tag: 'i',
13487                      cls: 'fa fa-' + this.caret
13488                 };
13489                 
13490             }
13491             
13492             combobox.cn.push({
13493                 tag :'span',
13494                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13495                 cn : [
13496                     Roo.bootstrap.version == 3 ? caret : '',
13497                     {
13498                         tag: 'span',
13499                         cls: 'combobox-clear',
13500                         cn  : [
13501                             {
13502                                 tag : 'i',
13503                                 cls: 'icon-remove'
13504                             }
13505                         ]
13506                     }
13507                 ]
13508
13509             })
13510         }
13511         
13512         if(this.multiple){
13513             combobox.cls += ' roo-select2-container-multi';
13514         }
13515          var indicator = {
13516             tag : 'i',
13517             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13518             tooltip : 'This field is required'
13519         };
13520         if (Roo.bootstrap.version == 4) {
13521             indicator = {
13522                 tag : 'i',
13523                 style : 'display:none'
13524             };
13525         }
13526         
13527         
13528         if (align ==='left' && this.fieldLabel.length) {
13529             
13530             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13531
13532             cfg.cn = [
13533                 indicator,
13534                 {
13535                     tag: 'label',
13536                     'for' :  id,
13537                     cls : 'control-label',
13538                     html : this.fieldLabel
13539
13540                 },
13541                 {
13542                     cls : "", 
13543                     cn: [
13544                         combobox
13545                     ]
13546                 }
13547
13548             ];
13549             
13550             var labelCfg = cfg.cn[1];
13551             var contentCfg = cfg.cn[2];
13552             
13553             if(this.indicatorpos == 'right'){
13554                 cfg.cn = [
13555                     {
13556                         tag: 'label',
13557                         'for' :  id,
13558                         cls : 'control-label',
13559                         cn : [
13560                             {
13561                                 tag : 'span',
13562                                 html : this.fieldLabel
13563                             },
13564                             indicator
13565                         ]
13566                     },
13567                     {
13568                         cls : "", 
13569                         cn: [
13570                             combobox
13571                         ]
13572                     }
13573
13574                 ];
13575                 
13576                 labelCfg = cfg.cn[0];
13577                 contentCfg = cfg.cn[1];
13578             }
13579             
13580             if(this.labelWidth > 12){
13581                 labelCfg.style = "width: " + this.labelWidth + 'px';
13582             }
13583             
13584             if(this.labelWidth < 13 && this.labelmd == 0){
13585                 this.labelmd = this.labelWidth;
13586             }
13587             
13588             if(this.labellg > 0){
13589                 labelCfg.cls += ' col-lg-' + this.labellg;
13590                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13591             }
13592             
13593             if(this.labelmd > 0){
13594                 labelCfg.cls += ' col-md-' + this.labelmd;
13595                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13596             }
13597             
13598             if(this.labelsm > 0){
13599                 labelCfg.cls += ' col-sm-' + this.labelsm;
13600                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13601             }
13602             
13603             if(this.labelxs > 0){
13604                 labelCfg.cls += ' col-xs-' + this.labelxs;
13605                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13606             }
13607             
13608         } else if ( this.fieldLabel.length) {
13609 //                Roo.log(" label");
13610             cfg.cn = [
13611                 indicator,
13612                {
13613                    tag: 'label',
13614                    //cls : 'input-group-addon',
13615                    html : this.fieldLabel
13616
13617                },
13618
13619                combobox
13620
13621             ];
13622             
13623             if(this.indicatorpos == 'right'){
13624                 
13625                 cfg.cn = [
13626                     {
13627                        tag: 'label',
13628                        cn : [
13629                            {
13630                                tag : 'span',
13631                                html : this.fieldLabel
13632                            },
13633                            indicator
13634                        ]
13635
13636                     },
13637                     combobox
13638
13639                 ];
13640
13641             }
13642
13643         } else {
13644             
13645 //                Roo.log(" no label && no align");
13646                 cfg = combobox
13647                      
13648                 
13649         }
13650         
13651         var settings=this;
13652         ['xs','sm','md','lg'].map(function(size){
13653             if (settings[size]) {
13654                 cfg.cls += ' col-' + size + '-' + settings[size];
13655             }
13656         });
13657         
13658         return cfg;
13659         
13660     },
13661     
13662     
13663     
13664     // private
13665     onResize : function(w, h){
13666 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13667 //        if(typeof w == 'number'){
13668 //            var x = w - this.trigger.getWidth();
13669 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13670 //            this.trigger.setStyle('left', x+'px');
13671 //        }
13672     },
13673
13674     // private
13675     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13676
13677     // private
13678     getResizeEl : function(){
13679         return this.inputEl();
13680     },
13681
13682     // private
13683     getPositionEl : function(){
13684         return this.inputEl();
13685     },
13686
13687     // private
13688     alignErrorIcon : function(){
13689         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13690     },
13691
13692     // private
13693     initEvents : function(){
13694         
13695         this.createList();
13696         
13697         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13698         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13699         if(!this.multiple && this.showToggleBtn){
13700             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13701             if(this.hideTrigger){
13702                 this.trigger.setDisplayed(false);
13703             }
13704             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13705         }
13706         
13707         if(this.multiple){
13708             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13709         }
13710         
13711         if(this.removable && !this.editable && !this.tickable){
13712             var close = this.closeTriggerEl();
13713             
13714             if(close){
13715                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13716                 close.on('click', this.removeBtnClick, this, close);
13717             }
13718         }
13719         
13720         //this.trigger.addClassOnOver('x-form-trigger-over');
13721         //this.trigger.addClassOnClick('x-form-trigger-click');
13722         
13723         //if(!this.width){
13724         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13725         //}
13726     },
13727     
13728     closeTriggerEl : function()
13729     {
13730         var close = this.el.select('.roo-combo-removable-btn', true).first();
13731         return close ? close : false;
13732     },
13733     
13734     removeBtnClick : function(e, h, el)
13735     {
13736         e.preventDefault();
13737         
13738         if(this.fireEvent("remove", this) !== false){
13739             this.reset();
13740             this.fireEvent("afterremove", this)
13741         }
13742     },
13743     
13744     createList : function()
13745     {
13746         this.list = Roo.get(document.body).createChild({
13747             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13748             cls: 'typeahead typeahead-long dropdown-menu shadow',
13749             style: 'display:none'
13750         });
13751         
13752         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13753         
13754     },
13755
13756     // private
13757     initTrigger : function(){
13758        
13759     },
13760
13761     // private
13762     onDestroy : function(){
13763         if(this.trigger){
13764             this.trigger.removeAllListeners();
13765           //  this.trigger.remove();
13766         }
13767         //if(this.wrap){
13768         //    this.wrap.remove();
13769         //}
13770         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13771     },
13772
13773     // private
13774     onFocus : function(){
13775         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13776         /*
13777         if(!this.mimicing){
13778             this.wrap.addClass('x-trigger-wrap-focus');
13779             this.mimicing = true;
13780             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13781             if(this.monitorTab){
13782                 this.el.on("keydown", this.checkTab, this);
13783             }
13784         }
13785         */
13786     },
13787
13788     // private
13789     checkTab : function(e){
13790         if(e.getKey() == e.TAB){
13791             this.triggerBlur();
13792         }
13793     },
13794
13795     // private
13796     onBlur : function(){
13797         // do nothing
13798     },
13799
13800     // private
13801     mimicBlur : function(e, t){
13802         /*
13803         if(!this.wrap.contains(t) && this.validateBlur()){
13804             this.triggerBlur();
13805         }
13806         */
13807     },
13808
13809     // private
13810     triggerBlur : function(){
13811         this.mimicing = false;
13812         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13813         if(this.monitorTab){
13814             this.el.un("keydown", this.checkTab, this);
13815         }
13816         //this.wrap.removeClass('x-trigger-wrap-focus');
13817         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13818     },
13819
13820     // private
13821     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13822     validateBlur : function(e, t){
13823         return true;
13824     },
13825
13826     // private
13827     onDisable : function(){
13828         this.inputEl().dom.disabled = true;
13829         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13830         //if(this.wrap){
13831         //    this.wrap.addClass('x-item-disabled');
13832         //}
13833     },
13834
13835     // private
13836     onEnable : function(){
13837         this.inputEl().dom.disabled = false;
13838         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13839         //if(this.wrap){
13840         //    this.el.removeClass('x-item-disabled');
13841         //}
13842     },
13843
13844     // private
13845     onShow : function(){
13846         var ae = this.getActionEl();
13847         
13848         if(ae){
13849             ae.dom.style.display = '';
13850             ae.dom.style.visibility = 'visible';
13851         }
13852     },
13853
13854     // private
13855     
13856     onHide : function(){
13857         var ae = this.getActionEl();
13858         ae.dom.style.display = 'none';
13859     },
13860
13861     /**
13862      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13863      * by an implementing function.
13864      * @method
13865      * @param {EventObject} e
13866      */
13867     onTriggerClick : Roo.emptyFn
13868 });
13869  
13870 /*
13871 * Licence: LGPL
13872 */
13873
13874 /**
13875  * @class Roo.bootstrap.CardUploader
13876  * @extends Roo.bootstrap.Button
13877  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13878  * @cfg {Number} errorTimeout default 3000
13879  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13880  * @cfg {Array}  html The button text.
13881
13882  *
13883  * @constructor
13884  * Create a new CardUploader
13885  * @param {Object} config The config object
13886  */
13887
13888 Roo.bootstrap.CardUploader = function(config){
13889     
13890  
13891     
13892     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13893     
13894     
13895     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13896         return r.data.id
13897      });
13898     
13899      this.addEvents({
13900          // raw events
13901         /**
13902          * @event preview
13903          * When a image is clicked on - and needs to display a slideshow or similar..
13904          * @param {Roo.bootstrap.Card} this
13905          * @param {Object} The image information data 
13906          *
13907          */
13908         'preview' : true,
13909          /**
13910          * @event download
13911          * When a the download link is clicked
13912          * @param {Roo.bootstrap.Card} this
13913          * @param {Object} The image information data  contains 
13914          */
13915         'download' : true
13916         
13917     });
13918 };
13919  
13920 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13921     
13922      
13923     errorTimeout : 3000,
13924      
13925     images : false,
13926    
13927     fileCollection : false,
13928     allowBlank : true,
13929     
13930     getAutoCreate : function()
13931     {
13932         
13933         var cfg =  {
13934             cls :'form-group' ,
13935             cn : [
13936                
13937                 {
13938                     tag: 'label',
13939                    //cls : 'input-group-addon',
13940                     html : this.fieldLabel
13941
13942                 },
13943
13944                 {
13945                     tag: 'input',
13946                     type : 'hidden',
13947                     name : this.name,
13948                     value : this.value,
13949                     cls : 'd-none  form-control'
13950                 },
13951                 
13952                 {
13953                     tag: 'input',
13954                     multiple : 'multiple',
13955                     type : 'file',
13956                     cls : 'd-none  roo-card-upload-selector'
13957                 },
13958                 
13959                 {
13960                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13961                 },
13962                 {
13963                     cls : 'card-columns roo-card-uploader-container'
13964                 }
13965
13966             ]
13967         };
13968            
13969          
13970         return cfg;
13971     },
13972     
13973     getChildContainer : function() /// what children are added to.
13974     {
13975         return this.containerEl;
13976     },
13977    
13978     getButtonContainer : function() /// what children are added to.
13979     {
13980         return this.el.select(".roo-card-uploader-button-container").first();
13981     },
13982    
13983     initEvents : function()
13984     {
13985         
13986         Roo.bootstrap.Input.prototype.initEvents.call(this);
13987         
13988         var t = this;
13989         this.addxtype({
13990             xns: Roo.bootstrap,
13991
13992             xtype : 'Button',
13993             container_method : 'getButtonContainer' ,            
13994             html :  this.html, // fix changable?
13995             cls : 'w-100 ',
13996             listeners : {
13997                 'click' : function(btn, e) {
13998                     t.onClick(e);
13999                 }
14000             }
14001         });
14002         
14003         
14004         
14005         
14006         this.urlAPI = (window.createObjectURL && window) || 
14007                                 (window.URL && URL.revokeObjectURL && URL) || 
14008                                 (window.webkitURL && webkitURL);
14009                         
14010          
14011          
14012          
14013         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14014         
14015         this.selectorEl.on('change', this.onFileSelected, this);
14016         if (this.images) {
14017             var t = this;
14018             this.images.forEach(function(img) {
14019                 t.addCard(img)
14020             });
14021             this.images = false;
14022         }
14023         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14024          
14025        
14026     },
14027     
14028    
14029     onClick : function(e)
14030     {
14031         e.preventDefault();
14032          
14033         this.selectorEl.dom.click();
14034          
14035     },
14036     
14037     onFileSelected : function(e)
14038     {
14039         e.preventDefault();
14040         
14041         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14042             return;
14043         }
14044         
14045         Roo.each(this.selectorEl.dom.files, function(file){    
14046             this.addFile(file);
14047         }, this);
14048          
14049     },
14050     
14051       
14052     
14053       
14054     
14055     addFile : function(file)
14056     {
14057            
14058         if(typeof(file) === 'string'){
14059             throw "Add file by name?"; // should not happen
14060             return;
14061         }
14062         
14063         if(!file || !this.urlAPI){
14064             return;
14065         }
14066         
14067         // file;
14068         // file.type;
14069         
14070         var _this = this;
14071         
14072         
14073         var url = _this.urlAPI.createObjectURL( file);
14074            
14075         this.addCard({
14076             id : Roo.bootstrap.CardUploader.ID--,
14077             is_uploaded : false,
14078             src : url,
14079             srcfile : file,
14080             title : file.name,
14081             mimetype : file.type,
14082             preview : false,
14083             is_deleted : 0
14084         });
14085         
14086     },
14087     
14088     /**
14089      * addCard - add an Attachment to the uploader
14090      * @param data - the data about the image to upload
14091      *
14092      * {
14093           id : 123
14094           title : "Title of file",
14095           is_uploaded : false,
14096           src : "http://.....",
14097           srcfile : { the File upload object },
14098           mimetype : file.type,
14099           preview : false,
14100           is_deleted : 0
14101           .. any other data...
14102         }
14103      *
14104      * 
14105     */
14106     
14107     addCard : function (data)
14108     {
14109         // hidden input element?
14110         // if the file is not an image...
14111         //then we need to use something other that and header_image
14112         var t = this;
14113         //   remove.....
14114         var footer = [
14115             {
14116                 xns : Roo.bootstrap,
14117                 xtype : 'CardFooter',
14118                  items: [
14119                     {
14120                         xns : Roo.bootstrap,
14121                         xtype : 'Element',
14122                         cls : 'd-flex',
14123                         items : [
14124                             
14125                             {
14126                                 xns : Roo.bootstrap,
14127                                 xtype : 'Button',
14128                                 html : String.format("<small>{0}</small>", data.title),
14129                                 cls : 'col-10 text-left',
14130                                 size: 'sm',
14131                                 weight: 'link',
14132                                 fa : 'download',
14133                                 listeners : {
14134                                     click : function() {
14135                                      
14136                                         t.fireEvent( "download", t, data );
14137                                     }
14138                                 }
14139                             },
14140                           
14141                             {
14142                                 xns : Roo.bootstrap,
14143                                 xtype : 'Button',
14144                                 style: 'max-height: 28px; ',
14145                                 size : 'sm',
14146                                 weight: 'danger',
14147                                 cls : 'col-2',
14148                                 fa : 'times',
14149                                 listeners : {
14150                                     click : function() {
14151                                         t.removeCard(data.id)
14152                                     }
14153                                 }
14154                             }
14155                         ]
14156                     }
14157                     
14158                 ] 
14159             }
14160             
14161         ];
14162         
14163         var cn = this.addxtype(
14164             {
14165                  
14166                 xns : Roo.bootstrap,
14167                 xtype : 'Card',
14168                 closeable : true,
14169                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14170                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14171                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14172                 data : data,
14173                 html : false,
14174                  
14175                 items : footer,
14176                 initEvents : function() {
14177                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14178                     var card = this;
14179                     this.imgEl = this.el.select('.card-img-top').first();
14180                     if (this.imgEl) {
14181                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14182                         this.imgEl.set({ 'pointer' : 'cursor' });
14183                                   
14184                     }
14185                     this.getCardFooter().addClass('p-1');
14186                     
14187                   
14188                 }
14189                 
14190             }
14191         );
14192         // dont' really need ot update items.
14193         // this.items.push(cn);
14194         this.fileCollection.add(cn);
14195         
14196         if (!data.srcfile) {
14197             this.updateInput();
14198             return;
14199         }
14200             
14201         var _t = this;
14202         var reader = new FileReader();
14203         reader.addEventListener("load", function() {  
14204             data.srcdata =  reader.result;
14205             _t.updateInput();
14206         });
14207         reader.readAsDataURL(data.srcfile);
14208         
14209         
14210         
14211     },
14212     removeCard : function(id)
14213     {
14214         
14215         var card  = this.fileCollection.get(id);
14216         card.data.is_deleted = 1;
14217         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14218         //this.fileCollection.remove(card);
14219         //this.items = this.items.filter(function(e) { return e != card });
14220         // dont' really need ot update items.
14221         card.el.dom.parentNode.removeChild(card.el.dom);
14222         this.updateInput();
14223
14224         
14225     },
14226     reset: function()
14227     {
14228         this.fileCollection.each(function(card) {
14229             if (card.el.dom && card.el.dom.parentNode) {
14230                 card.el.dom.parentNode.removeChild(card.el.dom);
14231             }
14232         });
14233         this.fileCollection.clear();
14234         this.updateInput();
14235     },
14236     
14237     updateInput : function()
14238     {
14239          var data = [];
14240         this.fileCollection.each(function(e) {
14241             data.push(e.data);
14242             
14243         });
14244         this.inputEl().dom.value = JSON.stringify(data);
14245         
14246         
14247         
14248     }
14249     
14250     
14251 });
14252
14253
14254 Roo.bootstrap.CardUploader.ID = -1;/*
14255  * Based on:
14256  * Ext JS Library 1.1.1
14257  * Copyright(c) 2006-2007, Ext JS, LLC.
14258  *
14259  * Originally Released Under LGPL - original licence link has changed is not relivant.
14260  *
14261  * Fork - LGPL
14262  * <script type="text/javascript">
14263  */
14264
14265
14266 /**
14267  * @class Roo.data.SortTypes
14268  * @singleton
14269  * Defines the default sorting (casting?) comparison functions used when sorting data.
14270  */
14271 Roo.data.SortTypes = {
14272     /**
14273      * Default sort that does nothing
14274      * @param {Mixed} s The value being converted
14275      * @return {Mixed} The comparison value
14276      */
14277     none : function(s){
14278         return s;
14279     },
14280     
14281     /**
14282      * The regular expression used to strip tags
14283      * @type {RegExp}
14284      * @property
14285      */
14286     stripTagsRE : /<\/?[^>]+>/gi,
14287     
14288     /**
14289      * Strips all HTML tags to sort on text only
14290      * @param {Mixed} s The value being converted
14291      * @return {String} The comparison value
14292      */
14293     asText : function(s){
14294         return String(s).replace(this.stripTagsRE, "");
14295     },
14296     
14297     /**
14298      * Strips all HTML tags to sort on text only - Case insensitive
14299      * @param {Mixed} s The value being converted
14300      * @return {String} The comparison value
14301      */
14302     asUCText : function(s){
14303         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14304     },
14305     
14306     /**
14307      * Case insensitive string
14308      * @param {Mixed} s The value being converted
14309      * @return {String} The comparison value
14310      */
14311     asUCString : function(s) {
14312         return String(s).toUpperCase();
14313     },
14314     
14315     /**
14316      * Date sorting
14317      * @param {Mixed} s The value being converted
14318      * @return {Number} The comparison value
14319      */
14320     asDate : function(s) {
14321         if(!s){
14322             return 0;
14323         }
14324         if(s instanceof Date){
14325             return s.getTime();
14326         }
14327         return Date.parse(String(s));
14328     },
14329     
14330     /**
14331      * Float sorting
14332      * @param {Mixed} s The value being converted
14333      * @return {Float} The comparison value
14334      */
14335     asFloat : function(s) {
14336         var val = parseFloat(String(s).replace(/,/g, ""));
14337         if(isNaN(val)) {
14338             val = 0;
14339         }
14340         return val;
14341     },
14342     
14343     /**
14344      * Integer sorting
14345      * @param {Mixed} s The value being converted
14346      * @return {Number} The comparison value
14347      */
14348     asInt : function(s) {
14349         var val = parseInt(String(s).replace(/,/g, ""));
14350         if(isNaN(val)) {
14351             val = 0;
14352         }
14353         return val;
14354     }
14355 };/*
14356  * Based on:
14357  * Ext JS Library 1.1.1
14358  * Copyright(c) 2006-2007, Ext JS, LLC.
14359  *
14360  * Originally Released Under LGPL - original licence link has changed is not relivant.
14361  *
14362  * Fork - LGPL
14363  * <script type="text/javascript">
14364  */
14365
14366 /**
14367 * @class Roo.data.Record
14368  * Instances of this class encapsulate both record <em>definition</em> information, and record
14369  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14370  * to access Records cached in an {@link Roo.data.Store} object.<br>
14371  * <p>
14372  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14373  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14374  * objects.<br>
14375  * <p>
14376  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14377  * @constructor
14378  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14379  * {@link #create}. The parameters are the same.
14380  * @param {Array} data An associative Array of data values keyed by the field name.
14381  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14382  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14383  * not specified an integer id is generated.
14384  */
14385 Roo.data.Record = function(data, id){
14386     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14387     this.data = data;
14388 };
14389
14390 /**
14391  * Generate a constructor for a specific record layout.
14392  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14393  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14394  * Each field definition object may contain the following properties: <ul>
14395  * <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,
14396  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14397  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14398  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14399  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14400  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14401  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14402  * this may be omitted.</p></li>
14403  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14404  * <ul><li>auto (Default, implies no conversion)</li>
14405  * <li>string</li>
14406  * <li>int</li>
14407  * <li>float</li>
14408  * <li>boolean</li>
14409  * <li>date</li></ul></p></li>
14410  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14411  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14412  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14413  * by the Reader into an object that will be stored in the Record. It is passed the
14414  * following parameters:<ul>
14415  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14416  * </ul></p></li>
14417  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14418  * </ul>
14419  * <br>usage:<br><pre><code>
14420 var TopicRecord = Roo.data.Record.create(
14421     {name: 'title', mapping: 'topic_title'},
14422     {name: 'author', mapping: 'username'},
14423     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14424     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14425     {name: 'lastPoster', mapping: 'user2'},
14426     {name: 'excerpt', mapping: 'post_text'}
14427 );
14428
14429 var myNewRecord = new TopicRecord({
14430     title: 'Do my job please',
14431     author: 'noobie',
14432     totalPosts: 1,
14433     lastPost: new Date(),
14434     lastPoster: 'Animal',
14435     excerpt: 'No way dude!'
14436 });
14437 myStore.add(myNewRecord);
14438 </code></pre>
14439  * @method create
14440  * @static
14441  */
14442 Roo.data.Record.create = function(o){
14443     var f = function(){
14444         f.superclass.constructor.apply(this, arguments);
14445     };
14446     Roo.extend(f, Roo.data.Record);
14447     var p = f.prototype;
14448     p.fields = new Roo.util.MixedCollection(false, function(field){
14449         return field.name;
14450     });
14451     for(var i = 0, len = o.length; i < len; i++){
14452         p.fields.add(new Roo.data.Field(o[i]));
14453     }
14454     f.getField = function(name){
14455         return p.fields.get(name);  
14456     };
14457     return f;
14458 };
14459
14460 Roo.data.Record.AUTO_ID = 1000;
14461 Roo.data.Record.EDIT = 'edit';
14462 Roo.data.Record.REJECT = 'reject';
14463 Roo.data.Record.COMMIT = 'commit';
14464
14465 Roo.data.Record.prototype = {
14466     /**
14467      * Readonly flag - true if this record has been modified.
14468      * @type Boolean
14469      */
14470     dirty : false,
14471     editing : false,
14472     error: null,
14473     modified: null,
14474
14475     // private
14476     join : function(store){
14477         this.store = store;
14478     },
14479
14480     /**
14481      * Set the named field to the specified value.
14482      * @param {String} name The name of the field to set.
14483      * @param {Object} value The value to set the field to.
14484      */
14485     set : function(name, value){
14486         if(this.data[name] == value){
14487             return;
14488         }
14489         this.dirty = true;
14490         if(!this.modified){
14491             this.modified = {};
14492         }
14493         if(typeof this.modified[name] == 'undefined'){
14494             this.modified[name] = this.data[name];
14495         }
14496         this.data[name] = value;
14497         if(!this.editing && this.store){
14498             this.store.afterEdit(this);
14499         }       
14500     },
14501
14502     /**
14503      * Get the value of the named field.
14504      * @param {String} name The name of the field to get the value of.
14505      * @return {Object} The value of the field.
14506      */
14507     get : function(name){
14508         return this.data[name]; 
14509     },
14510
14511     // private
14512     beginEdit : function(){
14513         this.editing = true;
14514         this.modified = {}; 
14515     },
14516
14517     // private
14518     cancelEdit : function(){
14519         this.editing = false;
14520         delete this.modified;
14521     },
14522
14523     // private
14524     endEdit : function(){
14525         this.editing = false;
14526         if(this.dirty && this.store){
14527             this.store.afterEdit(this);
14528         }
14529     },
14530
14531     /**
14532      * Usually called by the {@link Roo.data.Store} which owns the Record.
14533      * Rejects all changes made to the Record since either creation, or the last commit operation.
14534      * Modified fields are reverted to their original values.
14535      * <p>
14536      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14537      * of reject operations.
14538      */
14539     reject : function(){
14540         var m = this.modified;
14541         for(var n in m){
14542             if(typeof m[n] != "function"){
14543                 this.data[n] = m[n];
14544             }
14545         }
14546         this.dirty = false;
14547         delete this.modified;
14548         this.editing = false;
14549         if(this.store){
14550             this.store.afterReject(this);
14551         }
14552     },
14553
14554     /**
14555      * Usually called by the {@link Roo.data.Store} which owns the Record.
14556      * Commits all changes made to the Record since either creation, or the last commit operation.
14557      * <p>
14558      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14559      * of commit operations.
14560      */
14561     commit : function(){
14562         this.dirty = false;
14563         delete this.modified;
14564         this.editing = false;
14565         if(this.store){
14566             this.store.afterCommit(this);
14567         }
14568     },
14569
14570     // private
14571     hasError : function(){
14572         return this.error != null;
14573     },
14574
14575     // private
14576     clearError : function(){
14577         this.error = null;
14578     },
14579
14580     /**
14581      * Creates a copy of this record.
14582      * @param {String} id (optional) A new record id if you don't want to use this record's id
14583      * @return {Record}
14584      */
14585     copy : function(newId) {
14586         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14587     }
14588 };/*
14589  * Based on:
14590  * Ext JS Library 1.1.1
14591  * Copyright(c) 2006-2007, Ext JS, LLC.
14592  *
14593  * Originally Released Under LGPL - original licence link has changed is not relivant.
14594  *
14595  * Fork - LGPL
14596  * <script type="text/javascript">
14597  */
14598
14599
14600
14601 /**
14602  * @class Roo.data.Store
14603  * @extends Roo.util.Observable
14604  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14605  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14606  * <p>
14607  * 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
14608  * has no knowledge of the format of the data returned by the Proxy.<br>
14609  * <p>
14610  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14611  * instances from the data object. These records are cached and made available through accessor functions.
14612  * @constructor
14613  * Creates a new Store.
14614  * @param {Object} config A config object containing the objects needed for the Store to access data,
14615  * and read the data into Records.
14616  */
14617 Roo.data.Store = function(config){
14618     this.data = new Roo.util.MixedCollection(false);
14619     this.data.getKey = function(o){
14620         return o.id;
14621     };
14622     this.baseParams = {};
14623     // private
14624     this.paramNames = {
14625         "start" : "start",
14626         "limit" : "limit",
14627         "sort" : "sort",
14628         "dir" : "dir",
14629         "multisort" : "_multisort"
14630     };
14631
14632     if(config && config.data){
14633         this.inlineData = config.data;
14634         delete config.data;
14635     }
14636
14637     Roo.apply(this, config);
14638     
14639     if(this.reader){ // reader passed
14640         this.reader = Roo.factory(this.reader, Roo.data);
14641         this.reader.xmodule = this.xmodule || false;
14642         if(!this.recordType){
14643             this.recordType = this.reader.recordType;
14644         }
14645         if(this.reader.onMetaChange){
14646             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14647         }
14648     }
14649
14650     if(this.recordType){
14651         this.fields = this.recordType.prototype.fields;
14652     }
14653     this.modified = [];
14654
14655     this.addEvents({
14656         /**
14657          * @event datachanged
14658          * Fires when the data cache has changed, and a widget which is using this Store
14659          * as a Record cache should refresh its view.
14660          * @param {Store} this
14661          */
14662         datachanged : true,
14663         /**
14664          * @event metachange
14665          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14666          * @param {Store} this
14667          * @param {Object} meta The JSON metadata
14668          */
14669         metachange : true,
14670         /**
14671          * @event add
14672          * Fires when Records have been added to the Store
14673          * @param {Store} this
14674          * @param {Roo.data.Record[]} records The array of Records added
14675          * @param {Number} index The index at which the record(s) were added
14676          */
14677         add : true,
14678         /**
14679          * @event remove
14680          * Fires when a Record has been removed from the Store
14681          * @param {Store} this
14682          * @param {Roo.data.Record} record The Record that was removed
14683          * @param {Number} index The index at which the record was removed
14684          */
14685         remove : true,
14686         /**
14687          * @event update
14688          * Fires when a Record has been updated
14689          * @param {Store} this
14690          * @param {Roo.data.Record} record The Record that was updated
14691          * @param {String} operation The update operation being performed.  Value may be one of:
14692          * <pre><code>
14693  Roo.data.Record.EDIT
14694  Roo.data.Record.REJECT
14695  Roo.data.Record.COMMIT
14696          * </code></pre>
14697          */
14698         update : true,
14699         /**
14700          * @event clear
14701          * Fires when the data cache has been cleared.
14702          * @param {Store} this
14703          */
14704         clear : true,
14705         /**
14706          * @event beforeload
14707          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14708          * the load action will be canceled.
14709          * @param {Store} this
14710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14711          */
14712         beforeload : true,
14713         /**
14714          * @event beforeloadadd
14715          * Fires after a new set of Records has been loaded.
14716          * @param {Store} this
14717          * @param {Roo.data.Record[]} records The Records that were loaded
14718          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14719          */
14720         beforeloadadd : true,
14721         /**
14722          * @event load
14723          * Fires after a new set of Records has been loaded, before they are added to the store.
14724          * @param {Store} this
14725          * @param {Roo.data.Record[]} records The Records that were loaded
14726          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14727          * @params {Object} return from reader
14728          */
14729         load : true,
14730         /**
14731          * @event loadexception
14732          * Fires if an exception occurs in the Proxy during loading.
14733          * Called with the signature of the Proxy's "loadexception" event.
14734          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14735          * 
14736          * @param {Proxy} 
14737          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14738          * @param {Object} load options 
14739          * @param {Object} jsonData from your request (normally this contains the Exception)
14740          */
14741         loadexception : true
14742     });
14743     
14744     if(this.proxy){
14745         this.proxy = Roo.factory(this.proxy, Roo.data);
14746         this.proxy.xmodule = this.xmodule || false;
14747         this.relayEvents(this.proxy,  ["loadexception"]);
14748     }
14749     this.sortToggle = {};
14750     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14751
14752     Roo.data.Store.superclass.constructor.call(this);
14753
14754     if(this.inlineData){
14755         this.loadData(this.inlineData);
14756         delete this.inlineData;
14757     }
14758 };
14759
14760 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14761      /**
14762     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14763     * without a remote query - used by combo/forms at present.
14764     */
14765     
14766     /**
14767     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14768     */
14769     /**
14770     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14771     */
14772     /**
14773     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14774     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14775     */
14776     /**
14777     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14778     * on any HTTP request
14779     */
14780     /**
14781     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14782     */
14783     /**
14784     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14785     */
14786     multiSort: false,
14787     /**
14788     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14789     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14790     */
14791     remoteSort : false,
14792
14793     /**
14794     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14795      * loaded or when a record is removed. (defaults to false).
14796     */
14797     pruneModifiedRecords : false,
14798
14799     // private
14800     lastOptions : null,
14801
14802     /**
14803      * Add Records to the Store and fires the add event.
14804      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14805      */
14806     add : function(records){
14807         records = [].concat(records);
14808         for(var i = 0, len = records.length; i < len; i++){
14809             records[i].join(this);
14810         }
14811         var index = this.data.length;
14812         this.data.addAll(records);
14813         this.fireEvent("add", this, records, index);
14814     },
14815
14816     /**
14817      * Remove a Record from the Store and fires the remove event.
14818      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14819      */
14820     remove : function(record){
14821         var index = this.data.indexOf(record);
14822         this.data.removeAt(index);
14823  
14824         if(this.pruneModifiedRecords){
14825             this.modified.remove(record);
14826         }
14827         this.fireEvent("remove", this, record, index);
14828     },
14829
14830     /**
14831      * Remove all Records from the Store and fires the clear event.
14832      */
14833     removeAll : function(){
14834         this.data.clear();
14835         if(this.pruneModifiedRecords){
14836             this.modified = [];
14837         }
14838         this.fireEvent("clear", this);
14839     },
14840
14841     /**
14842      * Inserts Records to the Store at the given index and fires the add event.
14843      * @param {Number} index The start index at which to insert the passed Records.
14844      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14845      */
14846     insert : function(index, records){
14847         records = [].concat(records);
14848         for(var i = 0, len = records.length; i < len; i++){
14849             this.data.insert(index, records[i]);
14850             records[i].join(this);
14851         }
14852         this.fireEvent("add", this, records, index);
14853     },
14854
14855     /**
14856      * Get the index within the cache of the passed Record.
14857      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14858      * @return {Number} The index of the passed Record. Returns -1 if not found.
14859      */
14860     indexOf : function(record){
14861         return this.data.indexOf(record);
14862     },
14863
14864     /**
14865      * Get the index within the cache of the Record with the passed id.
14866      * @param {String} id The id of the Record to find.
14867      * @return {Number} The index of the Record. Returns -1 if not found.
14868      */
14869     indexOfId : function(id){
14870         return this.data.indexOfKey(id);
14871     },
14872
14873     /**
14874      * Get the Record with the specified id.
14875      * @param {String} id The id of the Record to find.
14876      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14877      */
14878     getById : function(id){
14879         return this.data.key(id);
14880     },
14881
14882     /**
14883      * Get the Record at the specified index.
14884      * @param {Number} index The index of the Record to find.
14885      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14886      */
14887     getAt : function(index){
14888         return this.data.itemAt(index);
14889     },
14890
14891     /**
14892      * Returns a range of Records between specified indices.
14893      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14894      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14895      * @return {Roo.data.Record[]} An array of Records
14896      */
14897     getRange : function(start, end){
14898         return this.data.getRange(start, end);
14899     },
14900
14901     // private
14902     storeOptions : function(o){
14903         o = Roo.apply({}, o);
14904         delete o.callback;
14905         delete o.scope;
14906         this.lastOptions = o;
14907     },
14908
14909     /**
14910      * Loads the Record cache from the configured Proxy using the configured Reader.
14911      * <p>
14912      * If using remote paging, then the first load call must specify the <em>start</em>
14913      * and <em>limit</em> properties in the options.params property to establish the initial
14914      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14915      * <p>
14916      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14917      * and this call will return before the new data has been loaded. Perform any post-processing
14918      * in a callback function, or in a "load" event handler.</strong>
14919      * <p>
14920      * @param {Object} options An object containing properties which control loading options:<ul>
14921      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14922      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14923      * passed the following arguments:<ul>
14924      * <li>r : Roo.data.Record[]</li>
14925      * <li>options: Options object from the load call</li>
14926      * <li>success: Boolean success indicator</li></ul></li>
14927      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14928      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14929      * </ul>
14930      */
14931     load : function(options){
14932         options = options || {};
14933         if(this.fireEvent("beforeload", this, options) !== false){
14934             this.storeOptions(options);
14935             var p = Roo.apply(options.params || {}, this.baseParams);
14936             // if meta was not loaded from remote source.. try requesting it.
14937             if (!this.reader.metaFromRemote) {
14938                 p._requestMeta = 1;
14939             }
14940             if(this.sortInfo && this.remoteSort){
14941                 var pn = this.paramNames;
14942                 p[pn["sort"]] = this.sortInfo.field;
14943                 p[pn["dir"]] = this.sortInfo.direction;
14944             }
14945             if (this.multiSort) {
14946                 var pn = this.paramNames;
14947                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14948             }
14949             
14950             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14951         }
14952     },
14953
14954     /**
14955      * Reloads the Record cache from the configured Proxy using the configured Reader and
14956      * the options from the last load operation performed.
14957      * @param {Object} options (optional) An object containing properties which may override the options
14958      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14959      * the most recently used options are reused).
14960      */
14961     reload : function(options){
14962         this.load(Roo.applyIf(options||{}, this.lastOptions));
14963     },
14964
14965     // private
14966     // Called as a callback by the Reader during a load operation.
14967     loadRecords : function(o, options, success){
14968         if(!o || success === false){
14969             if(success !== false){
14970                 this.fireEvent("load", this, [], options, o);
14971             }
14972             if(options.callback){
14973                 options.callback.call(options.scope || this, [], options, false);
14974             }
14975             return;
14976         }
14977         // if data returned failure - throw an exception.
14978         if (o.success === false) {
14979             // show a message if no listener is registered.
14980             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14981                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14982             }
14983             // loadmask wil be hooked into this..
14984             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14985             return;
14986         }
14987         var r = o.records, t = o.totalRecords || r.length;
14988         
14989         this.fireEvent("beforeloadadd", this, r, options, o);
14990         
14991         if(!options || options.add !== true){
14992             if(this.pruneModifiedRecords){
14993                 this.modified = [];
14994             }
14995             for(var i = 0, len = r.length; i < len; i++){
14996                 r[i].join(this);
14997             }
14998             if(this.snapshot){
14999                 this.data = this.snapshot;
15000                 delete this.snapshot;
15001             }
15002             this.data.clear();
15003             this.data.addAll(r);
15004             this.totalLength = t;
15005             this.applySort();
15006             this.fireEvent("datachanged", this);
15007         }else{
15008             this.totalLength = Math.max(t, this.data.length+r.length);
15009             this.add(r);
15010         }
15011         
15012         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15013                 
15014             var e = new Roo.data.Record({});
15015
15016             e.set(this.parent.displayField, this.parent.emptyTitle);
15017             e.set(this.parent.valueField, '');
15018
15019             this.insert(0, e);
15020         }
15021             
15022         this.fireEvent("load", this, r, options, o);
15023         if(options.callback){
15024             options.callback.call(options.scope || this, r, options, true);
15025         }
15026     },
15027
15028
15029     /**
15030      * Loads data from a passed data block. A Reader which understands the format of the data
15031      * must have been configured in the constructor.
15032      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15033      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15034      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15035      */
15036     loadData : function(o, append){
15037         var r = this.reader.readRecords(o);
15038         this.loadRecords(r, {add: append}, true);
15039     },
15040     
15041      /**
15042      * using 'cn' the nested child reader read the child array into it's child stores.
15043      * @param {Object} rec The record with a 'children array
15044      */
15045     loadDataFromChildren : function(rec)
15046     {
15047         this.loadData(this.reader.toLoadData(rec));
15048     },
15049     
15050
15051     /**
15052      * Gets the number of cached records.
15053      * <p>
15054      * <em>If using paging, this may not be the total size of the dataset. If the data object
15055      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15056      * the data set size</em>
15057      */
15058     getCount : function(){
15059         return this.data.length || 0;
15060     },
15061
15062     /**
15063      * Gets the total number of records in the dataset as returned by the server.
15064      * <p>
15065      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15066      * the dataset size</em>
15067      */
15068     getTotalCount : function(){
15069         return this.totalLength || 0;
15070     },
15071
15072     /**
15073      * Returns the sort state of the Store as an object with two properties:
15074      * <pre><code>
15075  field {String} The name of the field by which the Records are sorted
15076  direction {String} The sort order, "ASC" or "DESC"
15077      * </code></pre>
15078      */
15079     getSortState : function(){
15080         return this.sortInfo;
15081     },
15082
15083     // private
15084     applySort : function(){
15085         if(this.sortInfo && !this.remoteSort){
15086             var s = this.sortInfo, f = s.field;
15087             var st = this.fields.get(f).sortType;
15088             var fn = function(r1, r2){
15089                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15090                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15091             };
15092             this.data.sort(s.direction, fn);
15093             if(this.snapshot && this.snapshot != this.data){
15094                 this.snapshot.sort(s.direction, fn);
15095             }
15096         }
15097     },
15098
15099     /**
15100      * Sets the default sort column and order to be used by the next load operation.
15101      * @param {String} fieldName The name of the field to sort by.
15102      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15103      */
15104     setDefaultSort : function(field, dir){
15105         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15106     },
15107
15108     /**
15109      * Sort the Records.
15110      * If remote sorting is used, the sort is performed on the server, and the cache is
15111      * reloaded. If local sorting is used, the cache is sorted internally.
15112      * @param {String} fieldName The name of the field to sort by.
15113      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15114      */
15115     sort : function(fieldName, dir){
15116         var f = this.fields.get(fieldName);
15117         if(!dir){
15118             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15119             
15120             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15121                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15122             }else{
15123                 dir = f.sortDir;
15124             }
15125         }
15126         this.sortToggle[f.name] = dir;
15127         this.sortInfo = {field: f.name, direction: dir};
15128         if(!this.remoteSort){
15129             this.applySort();
15130             this.fireEvent("datachanged", this);
15131         }else{
15132             this.load(this.lastOptions);
15133         }
15134     },
15135
15136     /**
15137      * Calls the specified function for each of the Records in the cache.
15138      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15139      * Returning <em>false</em> aborts and exits the iteration.
15140      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15141      */
15142     each : function(fn, scope){
15143         this.data.each(fn, scope);
15144     },
15145
15146     /**
15147      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15148      * (e.g., during paging).
15149      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15150      */
15151     getModifiedRecords : function(){
15152         return this.modified;
15153     },
15154
15155     // private
15156     createFilterFn : function(property, value, anyMatch){
15157         if(!value.exec){ // not a regex
15158             value = String(value);
15159             if(value.length == 0){
15160                 return false;
15161             }
15162             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15163         }
15164         return function(r){
15165             return value.test(r.data[property]);
15166         };
15167     },
15168
15169     /**
15170      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15171      * @param {String} property A field on your records
15172      * @param {Number} start The record index to start at (defaults to 0)
15173      * @param {Number} end The last record index to include (defaults to length - 1)
15174      * @return {Number} The sum
15175      */
15176     sum : function(property, start, end){
15177         var rs = this.data.items, v = 0;
15178         start = start || 0;
15179         end = (end || end === 0) ? end : rs.length-1;
15180
15181         for(var i = start; i <= end; i++){
15182             v += (rs[i].data[property] || 0);
15183         }
15184         return v;
15185     },
15186
15187     /**
15188      * Filter the records by a specified property.
15189      * @param {String} field A field on your records
15190      * @param {String/RegExp} value Either a string that the field
15191      * should start with or a RegExp to test against the field
15192      * @param {Boolean} anyMatch True to match any part not just the beginning
15193      */
15194     filter : function(property, value, anyMatch){
15195         var fn = this.createFilterFn(property, value, anyMatch);
15196         return fn ? this.filterBy(fn) : this.clearFilter();
15197     },
15198
15199     /**
15200      * Filter by a function. The specified function will be called with each
15201      * record in this data source. If the function returns true the record is included,
15202      * otherwise it is filtered.
15203      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15204      * @param {Object} scope (optional) The scope of the function (defaults to this)
15205      */
15206     filterBy : function(fn, scope){
15207         this.snapshot = this.snapshot || this.data;
15208         this.data = this.queryBy(fn, scope||this);
15209         this.fireEvent("datachanged", this);
15210     },
15211
15212     /**
15213      * Query the records by a specified property.
15214      * @param {String} field A field on your records
15215      * @param {String/RegExp} value Either a string that the field
15216      * should start with or a RegExp to test against the field
15217      * @param {Boolean} anyMatch True to match any part not just the beginning
15218      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15219      */
15220     query : function(property, value, anyMatch){
15221         var fn = this.createFilterFn(property, value, anyMatch);
15222         return fn ? this.queryBy(fn) : this.data.clone();
15223     },
15224
15225     /**
15226      * Query by a function. The specified function will be called with each
15227      * record in this data source. If the function returns true the record is included
15228      * in the results.
15229      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15230      * @param {Object} scope (optional) The scope of the function (defaults to this)
15231       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15232      **/
15233     queryBy : function(fn, scope){
15234         var data = this.snapshot || this.data;
15235         return data.filterBy(fn, scope||this);
15236     },
15237
15238     /**
15239      * Collects unique values for a particular dataIndex from this store.
15240      * @param {String} dataIndex The property to collect
15241      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15242      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15243      * @return {Array} An array of the unique values
15244      **/
15245     collect : function(dataIndex, allowNull, bypassFilter){
15246         var d = (bypassFilter === true && this.snapshot) ?
15247                 this.snapshot.items : this.data.items;
15248         var v, sv, r = [], l = {};
15249         for(var i = 0, len = d.length; i < len; i++){
15250             v = d[i].data[dataIndex];
15251             sv = String(v);
15252             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15253                 l[sv] = true;
15254                 r[r.length] = v;
15255             }
15256         }
15257         return r;
15258     },
15259
15260     /**
15261      * Revert to a view of the Record cache with no filtering applied.
15262      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15263      */
15264     clearFilter : function(suppressEvent){
15265         if(this.snapshot && this.snapshot != this.data){
15266             this.data = this.snapshot;
15267             delete this.snapshot;
15268             if(suppressEvent !== true){
15269                 this.fireEvent("datachanged", this);
15270             }
15271         }
15272     },
15273
15274     // private
15275     afterEdit : function(record){
15276         if(this.modified.indexOf(record) == -1){
15277             this.modified.push(record);
15278         }
15279         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15280     },
15281     
15282     // private
15283     afterReject : function(record){
15284         this.modified.remove(record);
15285         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15286     },
15287
15288     // private
15289     afterCommit : function(record){
15290         this.modified.remove(record);
15291         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15292     },
15293
15294     /**
15295      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15296      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15297      */
15298     commitChanges : function(){
15299         var m = this.modified.slice(0);
15300         this.modified = [];
15301         for(var i = 0, len = m.length; i < len; i++){
15302             m[i].commit();
15303         }
15304     },
15305
15306     /**
15307      * Cancel outstanding changes on all changed records.
15308      */
15309     rejectChanges : function(){
15310         var m = this.modified.slice(0);
15311         this.modified = [];
15312         for(var i = 0, len = m.length; i < len; i++){
15313             m[i].reject();
15314         }
15315     },
15316
15317     onMetaChange : function(meta, rtype, o){
15318         this.recordType = rtype;
15319         this.fields = rtype.prototype.fields;
15320         delete this.snapshot;
15321         this.sortInfo = meta.sortInfo || this.sortInfo;
15322         this.modified = [];
15323         this.fireEvent('metachange', this, this.reader.meta);
15324     },
15325     
15326     moveIndex : function(data, type)
15327     {
15328         var index = this.indexOf(data);
15329         
15330         var newIndex = index + type;
15331         
15332         this.remove(data);
15333         
15334         this.insert(newIndex, data);
15335         
15336     }
15337 });/*
15338  * Based on:
15339  * Ext JS Library 1.1.1
15340  * Copyright(c) 2006-2007, Ext JS, LLC.
15341  *
15342  * Originally Released Under LGPL - original licence link has changed is not relivant.
15343  *
15344  * Fork - LGPL
15345  * <script type="text/javascript">
15346  */
15347
15348 /**
15349  * @class Roo.data.SimpleStore
15350  * @extends Roo.data.Store
15351  * Small helper class to make creating Stores from Array data easier.
15352  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15353  * @cfg {Array} fields An array of field definition objects, or field name strings.
15354  * @cfg {Object} an existing reader (eg. copied from another store)
15355  * @cfg {Array} data The multi-dimensional array of data
15356  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15357  * @cfg {Roo.data.Reader} reader  [not-required] 
15358  * @constructor
15359  * @param {Object} config
15360  */
15361 Roo.data.SimpleStore = function(config)
15362 {
15363     Roo.data.SimpleStore.superclass.constructor.call(this, {
15364         isLocal : true,
15365         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15366                 id: config.id
15367             },
15368             Roo.data.Record.create(config.fields)
15369         ),
15370         proxy : new Roo.data.MemoryProxy(config.data)
15371     });
15372     this.load();
15373 };
15374 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15375  * Based on:
15376  * Ext JS Library 1.1.1
15377  * Copyright(c) 2006-2007, Ext JS, LLC.
15378  *
15379  * Originally Released Under LGPL - original licence link has changed is not relivant.
15380  *
15381  * Fork - LGPL
15382  * <script type="text/javascript">
15383  */
15384
15385 /**
15386 /**
15387  * @extends Roo.data.Store
15388  * @class Roo.data.JsonStore
15389  * Small helper class to make creating Stores for JSON data easier. <br/>
15390 <pre><code>
15391 var store = new Roo.data.JsonStore({
15392     url: 'get-images.php',
15393     root: 'images',
15394     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15395 });
15396 </code></pre>
15397  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15398  * JsonReader and HttpProxy (unless inline data is provided).</b>
15399  * @cfg {Array} fields An array of field definition objects, or field name strings.
15400  * @constructor
15401  * @param {Object} config
15402  */
15403 Roo.data.JsonStore = function(c){
15404     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15405         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15406         reader: new Roo.data.JsonReader(c, c.fields)
15407     }));
15408 };
15409 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15410  * Based on:
15411  * Ext JS Library 1.1.1
15412  * Copyright(c) 2006-2007, Ext JS, LLC.
15413  *
15414  * Originally Released Under LGPL - original licence link has changed is not relivant.
15415  *
15416  * Fork - LGPL
15417  * <script type="text/javascript">
15418  */
15419
15420  
15421 Roo.data.Field = function(config){
15422     if(typeof config == "string"){
15423         config = {name: config};
15424     }
15425     Roo.apply(this, config);
15426     
15427     if(!this.type){
15428         this.type = "auto";
15429     }
15430     
15431     var st = Roo.data.SortTypes;
15432     // named sortTypes are supported, here we look them up
15433     if(typeof this.sortType == "string"){
15434         this.sortType = st[this.sortType];
15435     }
15436     
15437     // set default sortType for strings and dates
15438     if(!this.sortType){
15439         switch(this.type){
15440             case "string":
15441                 this.sortType = st.asUCString;
15442                 break;
15443             case "date":
15444                 this.sortType = st.asDate;
15445                 break;
15446             default:
15447                 this.sortType = st.none;
15448         }
15449     }
15450
15451     // define once
15452     var stripRe = /[\$,%]/g;
15453
15454     // prebuilt conversion function for this field, instead of
15455     // switching every time we're reading a value
15456     if(!this.convert){
15457         var cv, dateFormat = this.dateFormat;
15458         switch(this.type){
15459             case "":
15460             case "auto":
15461             case undefined:
15462                 cv = function(v){ return v; };
15463                 break;
15464             case "string":
15465                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15466                 break;
15467             case "int":
15468                 cv = function(v){
15469                     return v !== undefined && v !== null && v !== '' ?
15470                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15471                     };
15472                 break;
15473             case "float":
15474                 cv = function(v){
15475                     return v !== undefined && v !== null && v !== '' ?
15476                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15477                     };
15478                 break;
15479             case "bool":
15480             case "boolean":
15481                 cv = function(v){ return v === true || v === "true" || v == 1; };
15482                 break;
15483             case "date":
15484                 cv = function(v){
15485                     if(!v){
15486                         return '';
15487                     }
15488                     if(v instanceof Date){
15489                         return v;
15490                     }
15491                     if(dateFormat){
15492                         if(dateFormat == "timestamp"){
15493                             return new Date(v*1000);
15494                         }
15495                         return Date.parseDate(v, dateFormat);
15496                     }
15497                     var parsed = Date.parse(v);
15498                     return parsed ? new Date(parsed) : null;
15499                 };
15500              break;
15501             
15502         }
15503         this.convert = cv;
15504     }
15505 };
15506
15507 Roo.data.Field.prototype = {
15508     dateFormat: null,
15509     defaultValue: "",
15510     mapping: null,
15511     sortType : null,
15512     sortDir : "ASC"
15513 };/*
15514  * Based on:
15515  * Ext JS Library 1.1.1
15516  * Copyright(c) 2006-2007, Ext JS, LLC.
15517  *
15518  * Originally Released Under LGPL - original licence link has changed is not relivant.
15519  *
15520  * Fork - LGPL
15521  * <script type="text/javascript">
15522  */
15523  
15524 // Base class for reading structured data from a data source.  This class is intended to be
15525 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15526
15527 /**
15528  * @class Roo.data.DataReader
15529  * @abstract
15530  * Base class for reading structured data from a data source.  This class is intended to be
15531  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15532  */
15533
15534 Roo.data.DataReader = function(meta, recordType){
15535     
15536     this.meta = meta;
15537     
15538     this.recordType = recordType instanceof Array ? 
15539         Roo.data.Record.create(recordType) : recordType;
15540 };
15541
15542 Roo.data.DataReader.prototype = {
15543     
15544     
15545     readerType : 'Data',
15546      /**
15547      * Create an empty record
15548      * @param {Object} data (optional) - overlay some values
15549      * @return {Roo.data.Record} record created.
15550      */
15551     newRow :  function(d) {
15552         var da =  {};
15553         this.recordType.prototype.fields.each(function(c) {
15554             switch( c.type) {
15555                 case 'int' : da[c.name] = 0; break;
15556                 case 'date' : da[c.name] = new Date(); break;
15557                 case 'float' : da[c.name] = 0.0; break;
15558                 case 'boolean' : da[c.name] = false; break;
15559                 default : da[c.name] = ""; break;
15560             }
15561             
15562         });
15563         return new this.recordType(Roo.apply(da, d));
15564     }
15565     
15566     
15567 };/*
15568  * Based on:
15569  * Ext JS Library 1.1.1
15570  * Copyright(c) 2006-2007, Ext JS, LLC.
15571  *
15572  * Originally Released Under LGPL - original licence link has changed is not relivant.
15573  *
15574  * Fork - LGPL
15575  * <script type="text/javascript">
15576  */
15577
15578 /**
15579  * @class Roo.data.DataProxy
15580  * @extends Roo.data.Observable
15581  * @abstract
15582  * This class is an abstract base class for implementations which provide retrieval of
15583  * unformatted data objects.<br>
15584  * <p>
15585  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15586  * (of the appropriate type which knows how to parse the data object) to provide a block of
15587  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15588  * <p>
15589  * Custom implementations must implement the load method as described in
15590  * {@link Roo.data.HttpProxy#load}.
15591  */
15592 Roo.data.DataProxy = function(){
15593     this.addEvents({
15594         /**
15595          * @event beforeload
15596          * Fires before a network request is made to retrieve a data object.
15597          * @param {Object} This DataProxy object.
15598          * @param {Object} params The params parameter to the load function.
15599          */
15600         beforeload : true,
15601         /**
15602          * @event load
15603          * Fires before the load method's callback is called.
15604          * @param {Object} This DataProxy object.
15605          * @param {Object} o The data object.
15606          * @param {Object} arg The callback argument object passed to the load function.
15607          */
15608         load : true,
15609         /**
15610          * @event loadexception
15611          * Fires if an Exception occurs during data retrieval.
15612          * @param {Object} This DataProxy object.
15613          * @param {Object} o The data object.
15614          * @param {Object} arg The callback argument object passed to the load function.
15615          * @param {Object} e The Exception.
15616          */
15617         loadexception : true
15618     });
15619     Roo.data.DataProxy.superclass.constructor.call(this);
15620 };
15621
15622 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15623
15624     /**
15625      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15626      */
15627 /*
15628  * Based on:
15629  * Ext JS Library 1.1.1
15630  * Copyright(c) 2006-2007, Ext JS, LLC.
15631  *
15632  * Originally Released Under LGPL - original licence link has changed is not relivant.
15633  *
15634  * Fork - LGPL
15635  * <script type="text/javascript">
15636  */
15637 /**
15638  * @class Roo.data.MemoryProxy
15639  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15640  * to the Reader when its load method is called.
15641  * @constructor
15642  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15643  */
15644 Roo.data.MemoryProxy = function(data){
15645     if (data.data) {
15646         data = data.data;
15647     }
15648     Roo.data.MemoryProxy.superclass.constructor.call(this);
15649     this.data = data;
15650 };
15651
15652 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15653     
15654     /**
15655      * Load data from the requested source (in this case an in-memory
15656      * data object passed to the constructor), read the data object into
15657      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15658      * process that block using the passed callback.
15659      * @param {Object} params This parameter is not used by the MemoryProxy class.
15660      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15661      * object into a block of Roo.data.Records.
15662      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15663      * The function must be passed <ul>
15664      * <li>The Record block object</li>
15665      * <li>The "arg" argument from the load function</li>
15666      * <li>A boolean success indicator</li>
15667      * </ul>
15668      * @param {Object} scope The scope in which to call the callback
15669      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15670      */
15671     load : function(params, reader, callback, scope, arg){
15672         params = params || {};
15673         var result;
15674         try {
15675             result = reader.readRecords(params.data ? params.data :this.data);
15676         }catch(e){
15677             this.fireEvent("loadexception", this, arg, null, e);
15678             callback.call(scope, null, arg, false);
15679             return;
15680         }
15681         callback.call(scope, result, arg, true);
15682     },
15683     
15684     // private
15685     update : function(params, records){
15686         
15687     }
15688 });/*
15689  * Based on:
15690  * Ext JS Library 1.1.1
15691  * Copyright(c) 2006-2007, Ext JS, LLC.
15692  *
15693  * Originally Released Under LGPL - original licence link has changed is not relivant.
15694  *
15695  * Fork - LGPL
15696  * <script type="text/javascript">
15697  */
15698 /**
15699  * @class Roo.data.HttpProxy
15700  * @extends Roo.data.DataProxy
15701  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15702  * configured to reference a certain URL.<br><br>
15703  * <p>
15704  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15705  * from which the running page was served.<br><br>
15706  * <p>
15707  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15708  * <p>
15709  * Be aware that to enable the browser to parse an XML document, the server must set
15710  * the Content-Type header in the HTTP response to "text/xml".
15711  * @constructor
15712  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15713  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15714  * will be used to make the request.
15715  */
15716 Roo.data.HttpProxy = function(conn){
15717     Roo.data.HttpProxy.superclass.constructor.call(this);
15718     // is conn a conn config or a real conn?
15719     this.conn = conn;
15720     this.useAjax = !conn || !conn.events;
15721   
15722 };
15723
15724 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15725     // thse are take from connection...
15726     
15727     /**
15728      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15729      */
15730     /**
15731      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15732      * extra parameters to each request made by this object. (defaults to undefined)
15733      */
15734     /**
15735      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15736      *  to each request made by this object. (defaults to undefined)
15737      */
15738     /**
15739      * @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)
15740      */
15741     /**
15742      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15743      */
15744      /**
15745      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15746      * @type Boolean
15747      */
15748   
15749
15750     /**
15751      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15752      * @type Boolean
15753      */
15754     /**
15755      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15756      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15757      * a finer-grained basis than the DataProxy events.
15758      */
15759     getConnection : function(){
15760         return this.useAjax ? Roo.Ajax : this.conn;
15761     },
15762
15763     /**
15764      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15765      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15766      * process that block using the passed callback.
15767      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15768      * for the request to the remote server.
15769      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15770      * object into a block of Roo.data.Records.
15771      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15772      * The function must be passed <ul>
15773      * <li>The Record block object</li>
15774      * <li>The "arg" argument from the load function</li>
15775      * <li>A boolean success indicator</li>
15776      * </ul>
15777      * @param {Object} scope The scope in which to call the callback
15778      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15779      */
15780     load : function(params, reader, callback, scope, arg){
15781         if(this.fireEvent("beforeload", this, params) !== false){
15782             var  o = {
15783                 params : params || {},
15784                 request: {
15785                     callback : callback,
15786                     scope : scope,
15787                     arg : arg
15788                 },
15789                 reader: reader,
15790                 callback : this.loadResponse,
15791                 scope: this
15792             };
15793             if(this.useAjax){
15794                 Roo.applyIf(o, this.conn);
15795                 if(this.activeRequest){
15796                     Roo.Ajax.abort(this.activeRequest);
15797                 }
15798                 this.activeRequest = Roo.Ajax.request(o);
15799             }else{
15800                 this.conn.request(o);
15801             }
15802         }else{
15803             callback.call(scope||this, null, arg, false);
15804         }
15805     },
15806
15807     // private
15808     loadResponse : function(o, success, response){
15809         delete this.activeRequest;
15810         if(!success){
15811             this.fireEvent("loadexception", this, o, response);
15812             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15813             return;
15814         }
15815         var result;
15816         try {
15817             result = o.reader.read(response);
15818         }catch(e){
15819             this.fireEvent("loadexception", this, o, response, e);
15820             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15821             return;
15822         }
15823         
15824         this.fireEvent("load", this, o, o.request.arg);
15825         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15826     },
15827
15828     // private
15829     update : function(dataSet){
15830
15831     },
15832
15833     // private
15834     updateResponse : function(dataSet){
15835
15836     }
15837 });/*
15838  * Based on:
15839  * Ext JS Library 1.1.1
15840  * Copyright(c) 2006-2007, Ext JS, LLC.
15841  *
15842  * Originally Released Under LGPL - original licence link has changed is not relivant.
15843  *
15844  * Fork - LGPL
15845  * <script type="text/javascript">
15846  */
15847
15848 /**
15849  * @class Roo.data.ScriptTagProxy
15850  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15851  * other than the originating domain of the running page.<br><br>
15852  * <p>
15853  * <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
15854  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15855  * <p>
15856  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15857  * source code that is used as the source inside a &lt;script> tag.<br><br>
15858  * <p>
15859  * In order for the browser to process the returned data, the server must wrap the data object
15860  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15861  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15862  * depending on whether the callback name was passed:
15863  * <p>
15864  * <pre><code>
15865 boolean scriptTag = false;
15866 String cb = request.getParameter("callback");
15867 if (cb != null) {
15868     scriptTag = true;
15869     response.setContentType("text/javascript");
15870 } else {
15871     response.setContentType("application/x-json");
15872 }
15873 Writer out = response.getWriter();
15874 if (scriptTag) {
15875     out.write(cb + "(");
15876 }
15877 out.print(dataBlock.toJsonString());
15878 if (scriptTag) {
15879     out.write(");");
15880 }
15881 </pre></code>
15882  *
15883  * @constructor
15884  * @param {Object} config A configuration object.
15885  */
15886 Roo.data.ScriptTagProxy = function(config){
15887     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15888     Roo.apply(this, config);
15889     this.head = document.getElementsByTagName("head")[0];
15890 };
15891
15892 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15893
15894 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15895     /**
15896      * @cfg {String} url The URL from which to request the data object.
15897      */
15898     /**
15899      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15900      */
15901     timeout : 30000,
15902     /**
15903      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15904      * the server the name of the callback function set up by the load call to process the returned data object.
15905      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15906      * javascript output which calls this named function passing the data object as its only parameter.
15907      */
15908     callbackParam : "callback",
15909     /**
15910      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15911      * name to the request.
15912      */
15913     nocache : true,
15914
15915     /**
15916      * Load data from the configured URL, read the data object into
15917      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15918      * process that block using the passed callback.
15919      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15920      * for the request to the remote server.
15921      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15922      * object into a block of Roo.data.Records.
15923      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15924      * The function must be passed <ul>
15925      * <li>The Record block object</li>
15926      * <li>The "arg" argument from the load function</li>
15927      * <li>A boolean success indicator</li>
15928      * </ul>
15929      * @param {Object} scope The scope in which to call the callback
15930      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15931      */
15932     load : function(params, reader, callback, scope, arg){
15933         if(this.fireEvent("beforeload", this, params) !== false){
15934
15935             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15936
15937             var url = this.url;
15938             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15939             if(this.nocache){
15940                 url += "&_dc=" + (new Date().getTime());
15941             }
15942             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15943             var trans = {
15944                 id : transId,
15945                 cb : "stcCallback"+transId,
15946                 scriptId : "stcScript"+transId,
15947                 params : params,
15948                 arg : arg,
15949                 url : url,
15950                 callback : callback,
15951                 scope : scope,
15952                 reader : reader
15953             };
15954             var conn = this;
15955
15956             window[trans.cb] = function(o){
15957                 conn.handleResponse(o, trans);
15958             };
15959
15960             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15961
15962             if(this.autoAbort !== false){
15963                 this.abort();
15964             }
15965
15966             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15967
15968             var script = document.createElement("script");
15969             script.setAttribute("src", url);
15970             script.setAttribute("type", "text/javascript");
15971             script.setAttribute("id", trans.scriptId);
15972             this.head.appendChild(script);
15973
15974             this.trans = trans;
15975         }else{
15976             callback.call(scope||this, null, arg, false);
15977         }
15978     },
15979
15980     // private
15981     isLoading : function(){
15982         return this.trans ? true : false;
15983     },
15984
15985     /**
15986      * Abort the current server request.
15987      */
15988     abort : function(){
15989         if(this.isLoading()){
15990             this.destroyTrans(this.trans);
15991         }
15992     },
15993
15994     // private
15995     destroyTrans : function(trans, isLoaded){
15996         this.head.removeChild(document.getElementById(trans.scriptId));
15997         clearTimeout(trans.timeoutId);
15998         if(isLoaded){
15999             window[trans.cb] = undefined;
16000             try{
16001                 delete window[trans.cb];
16002             }catch(e){}
16003         }else{
16004             // if hasn't been loaded, wait for load to remove it to prevent script error
16005             window[trans.cb] = function(){
16006                 window[trans.cb] = undefined;
16007                 try{
16008                     delete window[trans.cb];
16009                 }catch(e){}
16010             };
16011         }
16012     },
16013
16014     // private
16015     handleResponse : function(o, trans){
16016         this.trans = false;
16017         this.destroyTrans(trans, true);
16018         var result;
16019         try {
16020             result = trans.reader.readRecords(o);
16021         }catch(e){
16022             this.fireEvent("loadexception", this, o, trans.arg, e);
16023             trans.callback.call(trans.scope||window, null, trans.arg, false);
16024             return;
16025         }
16026         this.fireEvent("load", this, o, trans.arg);
16027         trans.callback.call(trans.scope||window, result, trans.arg, true);
16028     },
16029
16030     // private
16031     handleFailure : function(trans){
16032         this.trans = false;
16033         this.destroyTrans(trans, false);
16034         this.fireEvent("loadexception", this, null, trans.arg);
16035         trans.callback.call(trans.scope||window, null, trans.arg, false);
16036     }
16037 });/*
16038  * Based on:
16039  * Ext JS Library 1.1.1
16040  * Copyright(c) 2006-2007, Ext JS, LLC.
16041  *
16042  * Originally Released Under LGPL - original licence link has changed is not relivant.
16043  *
16044  * Fork - LGPL
16045  * <script type="text/javascript">
16046  */
16047
16048 /**
16049  * @class Roo.data.JsonReader
16050  * @extends Roo.data.DataReader
16051  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16052  * based on mappings in a provided Roo.data.Record constructor.
16053  * 
16054  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16055  * in the reply previously. 
16056  * 
16057  * <p>
16058  * Example code:
16059  * <pre><code>
16060 var RecordDef = Roo.data.Record.create([
16061     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16062     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16063 ]);
16064 var myReader = new Roo.data.JsonReader({
16065     totalProperty: "results",    // The property which contains the total dataset size (optional)
16066     root: "rows",                // The property which contains an Array of row objects
16067     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16068 }, RecordDef);
16069 </code></pre>
16070  * <p>
16071  * This would consume a JSON file like this:
16072  * <pre><code>
16073 { 'results': 2, 'rows': [
16074     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16075     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16076 }
16077 </code></pre>
16078  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16079  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16080  * paged from the remote server.
16081  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16082  * @cfg {String} root name of the property which contains the Array of row objects.
16083  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16084  * @cfg {Array} fields Array of field definition objects
16085  * @constructor
16086  * Create a new JsonReader
16087  * @param {Object} meta Metadata configuration options
16088  * @param {Object} recordType Either an Array of field definition objects,
16089  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16090  */
16091 Roo.data.JsonReader = function(meta, recordType){
16092     
16093     meta = meta || {};
16094     // set some defaults:
16095     Roo.applyIf(meta, {
16096         totalProperty: 'total',
16097         successProperty : 'success',
16098         root : 'data',
16099         id : 'id'
16100     });
16101     
16102     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16103 };
16104 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16105     
16106     readerType : 'Json',
16107     
16108     /**
16109      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16110      * Used by Store query builder to append _requestMeta to params.
16111      * 
16112      */
16113     metaFromRemote : false,
16114     /**
16115      * This method is only used by a DataProxy which has retrieved data from a remote server.
16116      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16117      * @return {Object} data A data block which is used by an Roo.data.Store object as
16118      * a cache of Roo.data.Records.
16119      */
16120     read : function(response){
16121         var json = response.responseText;
16122        
16123         var o = /* eval:var:o */ eval("("+json+")");
16124         if(!o) {
16125             throw {message: "JsonReader.read: Json object not found"};
16126         }
16127         
16128         if(o.metaData){
16129             
16130             delete this.ef;
16131             this.metaFromRemote = true;
16132             this.meta = o.metaData;
16133             this.recordType = Roo.data.Record.create(o.metaData.fields);
16134             this.onMetaChange(this.meta, this.recordType, o);
16135         }
16136         return this.readRecords(o);
16137     },
16138
16139     // private function a store will implement
16140     onMetaChange : function(meta, recordType, o){
16141
16142     },
16143
16144     /**
16145          * @ignore
16146          */
16147     simpleAccess: function(obj, subsc) {
16148         return obj[subsc];
16149     },
16150
16151         /**
16152          * @ignore
16153          */
16154     getJsonAccessor: function(){
16155         var re = /[\[\.]/;
16156         return function(expr) {
16157             try {
16158                 return(re.test(expr))
16159                     ? new Function("obj", "return obj." + expr)
16160                     : function(obj){
16161                         return obj[expr];
16162                     };
16163             } catch(e){}
16164             return Roo.emptyFn;
16165         };
16166     }(),
16167
16168     /**
16169      * Create a data block containing Roo.data.Records from an XML document.
16170      * @param {Object} o An object which contains an Array of row objects in the property specified
16171      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16172      * which contains the total size of the dataset.
16173      * @return {Object} data A data block which is used by an Roo.data.Store object as
16174      * a cache of Roo.data.Records.
16175      */
16176     readRecords : function(o){
16177         /**
16178          * After any data loads, the raw JSON data is available for further custom processing.
16179          * @type Object
16180          */
16181         this.o = o;
16182         var s = this.meta, Record = this.recordType,
16183             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16184
16185 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16186         if (!this.ef) {
16187             if(s.totalProperty) {
16188                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16189                 }
16190                 if(s.successProperty) {
16191                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16192                 }
16193                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16194                 if (s.id) {
16195                         var g = this.getJsonAccessor(s.id);
16196                         this.getId = function(rec) {
16197                                 var r = g(rec);  
16198                                 return (r === undefined || r === "") ? null : r;
16199                         };
16200                 } else {
16201                         this.getId = function(){return null;};
16202                 }
16203             this.ef = [];
16204             for(var jj = 0; jj < fl; jj++){
16205                 f = fi[jj];
16206                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16207                 this.ef[jj] = this.getJsonAccessor(map);
16208             }
16209         }
16210
16211         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16212         if(s.totalProperty){
16213             var vt = parseInt(this.getTotal(o), 10);
16214             if(!isNaN(vt)){
16215                 totalRecords = vt;
16216             }
16217         }
16218         if(s.successProperty){
16219             var vs = this.getSuccess(o);
16220             if(vs === false || vs === 'false'){
16221                 success = false;
16222             }
16223         }
16224         var records = [];
16225         for(var i = 0; i < c; i++){
16226                 var n = root[i];
16227             var values = {};
16228             var id = this.getId(n);
16229             for(var j = 0; j < fl; j++){
16230                 f = fi[j];
16231             var v = this.ef[j](n);
16232             if (!f.convert) {
16233                 Roo.log('missing convert for ' + f.name);
16234                 Roo.log(f);
16235                 continue;
16236             }
16237             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16238             }
16239             var record = new Record(values, id);
16240             record.json = n;
16241             records[i] = record;
16242         }
16243         return {
16244             raw : o,
16245             success : success,
16246             records : records,
16247             totalRecords : totalRecords
16248         };
16249     },
16250     // used when loading children.. @see loadDataFromChildren
16251     toLoadData: function(rec)
16252     {
16253         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16254         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16255         return { data : data, total : data.length };
16256         
16257     }
16258 });/*
16259  * Based on:
16260  * Ext JS Library 1.1.1
16261  * Copyright(c) 2006-2007, Ext JS, LLC.
16262  *
16263  * Originally Released Under LGPL - original licence link has changed is not relivant.
16264  *
16265  * Fork - LGPL
16266  * <script type="text/javascript">
16267  */
16268
16269 /**
16270  * @class Roo.data.ArrayReader
16271  * @extends Roo.data.DataReader
16272  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16273  * Each element of that Array represents a row of data fields. The
16274  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16275  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16276  * <p>
16277  * Example code:.
16278  * <pre><code>
16279 var RecordDef = Roo.data.Record.create([
16280     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16281     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16282 ]);
16283 var myReader = new Roo.data.ArrayReader({
16284     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16285 }, RecordDef);
16286 </code></pre>
16287  * <p>
16288  * This would consume an Array like this:
16289  * <pre><code>
16290 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16291   </code></pre>
16292  
16293  * @constructor
16294  * Create a new JsonReader
16295  * @param {Object} meta Metadata configuration options.
16296  * @param {Object|Array} recordType Either an Array of field definition objects
16297  * 
16298  * @cfg {Array} fields Array of field definition objects
16299  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16300  * as specified to {@link Roo.data.Record#create},
16301  * or an {@link Roo.data.Record} object
16302  *
16303  * 
16304  * created using {@link Roo.data.Record#create}.
16305  */
16306 Roo.data.ArrayReader = function(meta, recordType)
16307 {    
16308     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16309 };
16310
16311 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16312     
16313       /**
16314      * Create a data block containing Roo.data.Records from an XML document.
16315      * @param {Object} o An Array of row objects which represents the dataset.
16316      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16317      * a cache of Roo.data.Records.
16318      */
16319     readRecords : function(o)
16320     {
16321         var sid = this.meta ? this.meta.id : null;
16322         var recordType = this.recordType, fields = recordType.prototype.fields;
16323         var records = [];
16324         var root = o;
16325         for(var i = 0; i < root.length; i++){
16326             var n = root[i];
16327             var values = {};
16328             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16329             for(var j = 0, jlen = fields.length; j < jlen; j++){
16330                 var f = fields.items[j];
16331                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16332                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16333                 v = f.convert(v);
16334                 values[f.name] = v;
16335             }
16336             var record = new recordType(values, id);
16337             record.json = n;
16338             records[records.length] = record;
16339         }
16340         return {
16341             records : records,
16342             totalRecords : records.length
16343         };
16344     },
16345     // used when loading children.. @see loadDataFromChildren
16346     toLoadData: function(rec)
16347     {
16348         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16349         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16350         
16351     }
16352     
16353     
16354 });/*
16355  * - LGPL
16356  * * 
16357  */
16358
16359 /**
16360  * @class Roo.bootstrap.ComboBox
16361  * @extends Roo.bootstrap.TriggerField
16362  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16363  * @cfg {Boolean} append (true|false) default false
16364  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16365  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16366  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16367  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16368  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16369  * @cfg {Boolean} animate default true
16370  * @cfg {Boolean} emptyResultText only for touch device
16371  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16372  * @cfg {String} emptyTitle default ''
16373  * @cfg {Number} width fixed with? experimental
16374  * @constructor
16375  * Create a new ComboBox.
16376  * @param {Object} config Configuration options
16377  */
16378 Roo.bootstrap.ComboBox = function(config){
16379     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16380     this.addEvents({
16381         /**
16382          * @event expand
16383          * Fires when the dropdown list is expanded
16384         * @param {Roo.bootstrap.ComboBox} combo This combo box
16385         */
16386         'expand' : true,
16387         /**
16388          * @event collapse
16389          * Fires when the dropdown list is collapsed
16390         * @param {Roo.bootstrap.ComboBox} combo This combo box
16391         */
16392         'collapse' : true,
16393         /**
16394          * @event beforeselect
16395          * Fires before a list item is selected. Return false to cancel the selection.
16396         * @param {Roo.bootstrap.ComboBox} combo This combo box
16397         * @param {Roo.data.Record} record The data record returned from the underlying store
16398         * @param {Number} index The index of the selected item in the dropdown list
16399         */
16400         'beforeselect' : true,
16401         /**
16402          * @event select
16403          * Fires when a list item is selected
16404         * @param {Roo.bootstrap.ComboBox} combo This combo box
16405         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16406         * @param {Number} index The index of the selected item in the dropdown list
16407         */
16408         'select' : true,
16409         /**
16410          * @event beforequery
16411          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16412          * The event object passed has these properties:
16413         * @param {Roo.bootstrap.ComboBox} combo This combo box
16414         * @param {String} query The query
16415         * @param {Boolean} forceAll true to force "all" query
16416         * @param {Boolean} cancel true to cancel the query
16417         * @param {Object} e The query event object
16418         */
16419         'beforequery': true,
16420          /**
16421          * @event add
16422          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16423         * @param {Roo.bootstrap.ComboBox} combo This combo box
16424         */
16425         'add' : true,
16426         /**
16427          * @event edit
16428          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16429         * @param {Roo.bootstrap.ComboBox} combo This combo box
16430         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16431         */
16432         'edit' : true,
16433         /**
16434          * @event remove
16435          * Fires when the remove value from the combobox array
16436         * @param {Roo.bootstrap.ComboBox} combo This combo box
16437         */
16438         'remove' : true,
16439         /**
16440          * @event afterremove
16441          * Fires when the remove value from the combobox array
16442         * @param {Roo.bootstrap.ComboBox} combo This combo box
16443         */
16444         'afterremove' : true,
16445         /**
16446          * @event specialfilter
16447          * Fires when specialfilter
16448             * @param {Roo.bootstrap.ComboBox} combo This combo box
16449             */
16450         'specialfilter' : true,
16451         /**
16452          * @event tick
16453          * Fires when tick the element
16454             * @param {Roo.bootstrap.ComboBox} combo This combo box
16455             */
16456         'tick' : true,
16457         /**
16458          * @event touchviewdisplay
16459          * Fires when touch view require special display (default is using displayField)
16460             * @param {Roo.bootstrap.ComboBox} combo This combo box
16461             * @param {Object} cfg set html .
16462             */
16463         'touchviewdisplay' : true
16464         
16465     });
16466     
16467     this.item = [];
16468     this.tickItems = [];
16469     
16470     this.selectedIndex = -1;
16471     if(this.mode == 'local'){
16472         if(config.queryDelay === undefined){
16473             this.queryDelay = 10;
16474         }
16475         if(config.minChars === undefined){
16476             this.minChars = 0;
16477         }
16478     }
16479 };
16480
16481 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16482      
16483     /**
16484      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16485      * rendering into an Roo.Editor, defaults to false)
16486      */
16487     /**
16488      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16489      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16490      */
16491     /**
16492      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16493      */
16494     /**
16495      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16496      * the dropdown list (defaults to undefined, with no header element)
16497      */
16498
16499      /**
16500      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16501      */
16502      
16503      /**
16504      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16505      */
16506     listWidth: undefined,
16507     /**
16508      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16509      * mode = 'remote' or 'text' if mode = 'local')
16510      */
16511     displayField: undefined,
16512     
16513     /**
16514      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16515      * mode = 'remote' or 'value' if mode = 'local'). 
16516      * Note: use of a valueField requires the user make a selection
16517      * in order for a value to be mapped.
16518      */
16519     valueField: undefined,
16520     /**
16521      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16522      */
16523     modalTitle : '',
16524     
16525     /**
16526      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16527      * field's data value (defaults to the underlying DOM element's name)
16528      */
16529     hiddenName: undefined,
16530     /**
16531      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16532      */
16533     listClass: '',
16534     /**
16535      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16536      */
16537     selectedClass: 'active',
16538     
16539     /**
16540      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16541      */
16542     shadow:'sides',
16543     /**
16544      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16545      * anchor positions (defaults to 'tl-bl')
16546      */
16547     listAlign: 'tl-bl?',
16548     /**
16549      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16550      */
16551     maxHeight: 300,
16552     /**
16553      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16554      * query specified by the allQuery config option (defaults to 'query')
16555      */
16556     triggerAction: 'query',
16557     /**
16558      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16559      * (defaults to 4, does not apply if editable = false)
16560      */
16561     minChars : 4,
16562     /**
16563      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16564      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16565      */
16566     typeAhead: false,
16567     /**
16568      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16569      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16570      */
16571     queryDelay: 500,
16572     /**
16573      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16574      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16575      */
16576     pageSize: 0,
16577     /**
16578      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16579      * when editable = true (defaults to false)
16580      */
16581     selectOnFocus:false,
16582     /**
16583      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16584      */
16585     queryParam: 'query',
16586     /**
16587      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16588      * when mode = 'remote' (defaults to 'Loading...')
16589      */
16590     loadingText: 'Loading...',
16591     /**
16592      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16593      */
16594     resizable: false,
16595     /**
16596      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16597      */
16598     handleHeight : 8,
16599     /**
16600      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16601      * traditional select (defaults to true)
16602      */
16603     editable: true,
16604     /**
16605      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16606      */
16607     allQuery: '',
16608     /**
16609      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16610      */
16611     mode: 'remote',
16612     /**
16613      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16614      * listWidth has a higher value)
16615      */
16616     minListWidth : 70,
16617     /**
16618      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16619      * allow the user to set arbitrary text into the field (defaults to false)
16620      */
16621     forceSelection:false,
16622     /**
16623      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16624      * if typeAhead = true (defaults to 250)
16625      */
16626     typeAheadDelay : 250,
16627     /**
16628      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16629      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16630      */
16631     valueNotFoundText : undefined,
16632     /**
16633      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16634      */
16635     blockFocus : false,
16636     
16637     /**
16638      * @cfg {Boolean} disableClear Disable showing of clear button.
16639      */
16640     disableClear : false,
16641     /**
16642      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16643      */
16644     alwaysQuery : false,
16645     
16646     /**
16647      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16648      */
16649     multiple : false,
16650     
16651     /**
16652      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16653      */
16654     invalidClass : "has-warning",
16655     
16656     /**
16657      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16658      */
16659     validClass : "has-success",
16660     
16661     /**
16662      * @cfg {Boolean} specialFilter (true|false) special filter default false
16663      */
16664     specialFilter : false,
16665     
16666     /**
16667      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16668      */
16669     mobileTouchView : true,
16670     
16671     /**
16672      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16673      */
16674     useNativeIOS : false,
16675     
16676     /**
16677      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16678      */
16679     mobile_restrict_height : false,
16680     
16681     ios_options : false,
16682     
16683     //private
16684     addicon : false,
16685     editicon: false,
16686     
16687     page: 0,
16688     hasQuery: false,
16689     append: false,
16690     loadNext: false,
16691     autoFocus : true,
16692     tickable : false,
16693     btnPosition : 'right',
16694     triggerList : true,
16695     showToggleBtn : true,
16696     animate : true,
16697     emptyResultText: 'Empty',
16698     triggerText : 'Select',
16699     emptyTitle : '',
16700     width : false,
16701     
16702     // element that contains real text value.. (when hidden is used..)
16703     
16704     getAutoCreate : function()
16705     {   
16706         var cfg = false;
16707         //render
16708         /*
16709          * Render classic select for iso
16710          */
16711         
16712         if(Roo.isIOS && this.useNativeIOS){
16713             cfg = this.getAutoCreateNativeIOS();
16714             return cfg;
16715         }
16716         
16717         /*
16718          * Touch Devices
16719          */
16720         
16721         if(Roo.isTouch && this.mobileTouchView){
16722             cfg = this.getAutoCreateTouchView();
16723             return cfg;;
16724         }
16725         
16726         /*
16727          *  Normal ComboBox
16728          */
16729         if(!this.tickable){
16730             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16731             return cfg;
16732         }
16733         
16734         /*
16735          *  ComboBox with tickable selections
16736          */
16737              
16738         var align = this.labelAlign || this.parentLabelAlign();
16739         
16740         cfg = {
16741             cls : 'form-group roo-combobox-tickable' //input-group
16742         };
16743         
16744         var btn_text_select = '';
16745         var btn_text_done = '';
16746         var btn_text_cancel = '';
16747         
16748         if (this.btn_text_show) {
16749             btn_text_select = 'Select';
16750             btn_text_done = 'Done';
16751             btn_text_cancel = 'Cancel'; 
16752         }
16753         
16754         var buttons = {
16755             tag : 'div',
16756             cls : 'tickable-buttons',
16757             cn : [
16758                 {
16759                     tag : 'button',
16760                     type : 'button',
16761                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16762                     //html : this.triggerText
16763                     html: btn_text_select
16764                 },
16765                 {
16766                     tag : 'button',
16767                     type : 'button',
16768                     name : 'ok',
16769                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16770                     //html : 'Done'
16771                     html: btn_text_done
16772                 },
16773                 {
16774                     tag : 'button',
16775                     type : 'button',
16776                     name : 'cancel',
16777                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16778                     //html : 'Cancel'
16779                     html: btn_text_cancel
16780                 }
16781             ]
16782         };
16783         
16784         if(this.editable){
16785             buttons.cn.unshift({
16786                 tag: 'input',
16787                 cls: 'roo-select2-search-field-input'
16788             });
16789         }
16790         
16791         var _this = this;
16792         
16793         Roo.each(buttons.cn, function(c){
16794             if (_this.size) {
16795                 c.cls += ' btn-' + _this.size;
16796             }
16797
16798             if (_this.disabled) {
16799                 c.disabled = true;
16800             }
16801         });
16802         
16803         var box = {
16804             tag: 'div',
16805             style : 'display: contents',
16806             cn: [
16807                 {
16808                     tag: 'input',
16809                     type : 'hidden',
16810                     cls: 'form-hidden-field'
16811                 },
16812                 {
16813                     tag: 'ul',
16814                     cls: 'roo-select2-choices',
16815                     cn:[
16816                         {
16817                             tag: 'li',
16818                             cls: 'roo-select2-search-field',
16819                             cn: [
16820                                 buttons
16821                             ]
16822                         }
16823                     ]
16824                 }
16825             ]
16826         };
16827         
16828         var combobox = {
16829             cls: 'roo-select2-container input-group roo-select2-container-multi',
16830             cn: [
16831                 
16832                 box
16833 //                {
16834 //                    tag: 'ul',
16835 //                    cls: 'typeahead typeahead-long dropdown-menu',
16836 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16837 //                }
16838             ]
16839         };
16840         
16841         if(this.hasFeedback && !this.allowBlank){
16842             
16843             var feedback = {
16844                 tag: 'span',
16845                 cls: 'glyphicon form-control-feedback'
16846             };
16847
16848             combobox.cn.push(feedback);
16849         }
16850         
16851         
16852         
16853         var indicator = {
16854             tag : 'i',
16855             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16856             tooltip : 'This field is required'
16857         };
16858         if (Roo.bootstrap.version == 4) {
16859             indicator = {
16860                 tag : 'i',
16861                 style : 'display:none'
16862             };
16863         }
16864         if (align ==='left' && this.fieldLabel.length) {
16865             
16866             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16867             
16868             cfg.cn = [
16869                 indicator,
16870                 {
16871                     tag: 'label',
16872                     'for' :  id,
16873                     cls : 'control-label col-form-label',
16874                     html : this.fieldLabel
16875
16876                 },
16877                 {
16878                     cls : "", 
16879                     cn: [
16880                         combobox
16881                     ]
16882                 }
16883
16884             ];
16885             
16886             var labelCfg = cfg.cn[1];
16887             var contentCfg = cfg.cn[2];
16888             
16889
16890             if(this.indicatorpos == 'right'){
16891                 
16892                 cfg.cn = [
16893                     {
16894                         tag: 'label',
16895                         'for' :  id,
16896                         cls : 'control-label col-form-label',
16897                         cn : [
16898                             {
16899                                 tag : 'span',
16900                                 html : this.fieldLabel
16901                             },
16902                             indicator
16903                         ]
16904                     },
16905                     {
16906                         cls : "",
16907                         cn: [
16908                             combobox
16909                         ]
16910                     }
16911
16912                 ];
16913                 
16914                 
16915                 
16916                 labelCfg = cfg.cn[0];
16917                 contentCfg = cfg.cn[1];
16918             
16919             }
16920             
16921             if(this.labelWidth > 12){
16922                 labelCfg.style = "width: " + this.labelWidth + 'px';
16923             }
16924             if(this.width * 1 > 0){
16925                 contentCfg.style = "width: " + this.width + 'px';
16926             }
16927             if(this.labelWidth < 13 && this.labelmd == 0){
16928                 this.labelmd = this.labelWidth;
16929             }
16930             
16931             if(this.labellg > 0){
16932                 labelCfg.cls += ' col-lg-' + this.labellg;
16933                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16934             }
16935             
16936             if(this.labelmd > 0){
16937                 labelCfg.cls += ' col-md-' + this.labelmd;
16938                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16939             }
16940             
16941             if(this.labelsm > 0){
16942                 labelCfg.cls += ' col-sm-' + this.labelsm;
16943                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16944             }
16945             
16946             if(this.labelxs > 0){
16947                 labelCfg.cls += ' col-xs-' + this.labelxs;
16948                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16949             }
16950                 
16951                 
16952         } else if ( this.fieldLabel.length) {
16953 //                Roo.log(" label");
16954                  cfg.cn = [
16955                    indicator,
16956                     {
16957                         tag: 'label',
16958                         //cls : 'input-group-addon',
16959                         html : this.fieldLabel
16960                     },
16961                     combobox
16962                 ];
16963                 
16964                 if(this.indicatorpos == 'right'){
16965                     cfg.cn = [
16966                         {
16967                             tag: 'label',
16968                             //cls : 'input-group-addon',
16969                             html : this.fieldLabel
16970                         },
16971                         indicator,
16972                         combobox
16973                     ];
16974                     
16975                 }
16976
16977         } else {
16978             
16979 //                Roo.log(" no label && no align");
16980                 cfg = combobox
16981                      
16982                 
16983         }
16984          
16985         var settings=this;
16986         ['xs','sm','md','lg'].map(function(size){
16987             if (settings[size]) {
16988                 cfg.cls += ' col-' + size + '-' + settings[size];
16989             }
16990         });
16991         
16992         return cfg;
16993         
16994     },
16995     
16996     _initEventsCalled : false,
16997     
16998     // private
16999     initEvents: function()
17000     {   
17001         if (this._initEventsCalled) { // as we call render... prevent looping...
17002             return;
17003         }
17004         this._initEventsCalled = true;
17005         
17006         if (!this.store) {
17007             throw "can not find store for combo";
17008         }
17009         
17010         this.indicator = this.indicatorEl();
17011         
17012         this.store = Roo.factory(this.store, Roo.data);
17013         this.store.parent = this;
17014         
17015         // if we are building from html. then this element is so complex, that we can not really
17016         // use the rendered HTML.
17017         // so we have to trash and replace the previous code.
17018         if (Roo.XComponent.build_from_html) {
17019             // remove this element....
17020             var e = this.el.dom, k=0;
17021             while (e ) { e = e.previousSibling;  ++k;}
17022
17023             this.el.remove();
17024             
17025             this.el=false;
17026             this.rendered = false;
17027             
17028             this.render(this.parent().getChildContainer(true), k);
17029         }
17030         
17031         if(Roo.isIOS && this.useNativeIOS){
17032             this.initIOSView();
17033             return;
17034         }
17035         
17036         /*
17037          * Touch Devices
17038          */
17039         
17040         if(Roo.isTouch && this.mobileTouchView){
17041             this.initTouchView();
17042             return;
17043         }
17044         
17045         if(this.tickable){
17046             this.initTickableEvents();
17047             return;
17048         }
17049         
17050         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17051         
17052         if(this.hiddenName){
17053             
17054             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17055             
17056             this.hiddenField.dom.value =
17057                 this.hiddenValue !== undefined ? this.hiddenValue :
17058                 this.value !== undefined ? this.value : '';
17059
17060             // prevent input submission
17061             this.el.dom.removeAttribute('name');
17062             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17063              
17064              
17065         }
17066         //if(Roo.isGecko){
17067         //    this.el.dom.setAttribute('autocomplete', 'off');
17068         //}
17069         
17070         var cls = 'x-combo-list';
17071         
17072         //this.list = new Roo.Layer({
17073         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17074         //});
17075         
17076         var _this = this;
17077         
17078         (function(){
17079             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17080             _this.list.setWidth(lw);
17081         }).defer(100);
17082         
17083         this.list.on('mouseover', this.onViewOver, this);
17084         this.list.on('mousemove', this.onViewMove, this);
17085         this.list.on('scroll', this.onViewScroll, this);
17086         
17087         /*
17088         this.list.swallowEvent('mousewheel');
17089         this.assetHeight = 0;
17090
17091         if(this.title){
17092             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17093             this.assetHeight += this.header.getHeight();
17094         }
17095
17096         this.innerList = this.list.createChild({cls:cls+'-inner'});
17097         this.innerList.on('mouseover', this.onViewOver, this);
17098         this.innerList.on('mousemove', this.onViewMove, this);
17099         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17100         
17101         if(this.allowBlank && !this.pageSize && !this.disableClear){
17102             this.footer = this.list.createChild({cls:cls+'-ft'});
17103             this.pageTb = new Roo.Toolbar(this.footer);
17104            
17105         }
17106         if(this.pageSize){
17107             this.footer = this.list.createChild({cls:cls+'-ft'});
17108             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17109                     {pageSize: this.pageSize});
17110             
17111         }
17112         
17113         if (this.pageTb && this.allowBlank && !this.disableClear) {
17114             var _this = this;
17115             this.pageTb.add(new Roo.Toolbar.Fill(), {
17116                 cls: 'x-btn-icon x-btn-clear',
17117                 text: '&#160;',
17118                 handler: function()
17119                 {
17120                     _this.collapse();
17121                     _this.clearValue();
17122                     _this.onSelect(false, -1);
17123                 }
17124             });
17125         }
17126         if (this.footer) {
17127             this.assetHeight += this.footer.getHeight();
17128         }
17129         */
17130             
17131         if(!this.tpl){
17132             this.tpl = Roo.bootstrap.version == 4 ?
17133                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17134                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17135         }
17136
17137         this.view = new Roo.View(this.list, this.tpl, {
17138             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17139         });
17140         //this.view.wrapEl.setDisplayed(false);
17141         this.view.on('click', this.onViewClick, this);
17142         
17143         
17144         this.store.on('beforeload', this.onBeforeLoad, this);
17145         this.store.on('load', this.onLoad, this);
17146         this.store.on('loadexception', this.onLoadException, this);
17147         /*
17148         if(this.resizable){
17149             this.resizer = new Roo.Resizable(this.list,  {
17150                pinned:true, handles:'se'
17151             });
17152             this.resizer.on('resize', function(r, w, h){
17153                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17154                 this.listWidth = w;
17155                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17156                 this.restrictHeight();
17157             }, this);
17158             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17159         }
17160         */
17161         if(!this.editable){
17162             this.editable = true;
17163             this.setEditable(false);
17164         }
17165         
17166         /*
17167         
17168         if (typeof(this.events.add.listeners) != 'undefined') {
17169             
17170             this.addicon = this.wrap.createChild(
17171                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17172        
17173             this.addicon.on('click', function(e) {
17174                 this.fireEvent('add', this);
17175             }, this);
17176         }
17177         if (typeof(this.events.edit.listeners) != 'undefined') {
17178             
17179             this.editicon = this.wrap.createChild(
17180                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17181             if (this.addicon) {
17182                 this.editicon.setStyle('margin-left', '40px');
17183             }
17184             this.editicon.on('click', function(e) {
17185                 
17186                 // we fire even  if inothing is selected..
17187                 this.fireEvent('edit', this, this.lastData );
17188                 
17189             }, this);
17190         }
17191         */
17192         
17193         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17194             "up" : function(e){
17195                 this.inKeyMode = true;
17196                 this.selectPrev();
17197             },
17198
17199             "down" : function(e){
17200                 if(!this.isExpanded()){
17201                     this.onTriggerClick();
17202                 }else{
17203                     this.inKeyMode = true;
17204                     this.selectNext();
17205                 }
17206             },
17207
17208             "enter" : function(e){
17209 //                this.onViewClick();
17210                 //return true;
17211                 this.collapse();
17212                 
17213                 if(this.fireEvent("specialkey", this, e)){
17214                     this.onViewClick(false);
17215                 }
17216                 
17217                 return true;
17218             },
17219
17220             "esc" : function(e){
17221                 this.collapse();
17222             },
17223
17224             "tab" : function(e){
17225                 this.collapse();
17226                 
17227                 if(this.fireEvent("specialkey", this, e)){
17228                     this.onViewClick(false);
17229                 }
17230                 
17231                 return true;
17232             },
17233
17234             scope : this,
17235
17236             doRelay : function(foo, bar, hname){
17237                 if(hname == 'down' || this.scope.isExpanded()){
17238                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17239                 }
17240                 return true;
17241             },
17242
17243             forceKeyDown: true
17244         });
17245         
17246         
17247         this.queryDelay = Math.max(this.queryDelay || 10,
17248                 this.mode == 'local' ? 10 : 250);
17249         
17250         
17251         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17252         
17253         if(this.typeAhead){
17254             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17255         }
17256         if(this.editable !== false){
17257             this.inputEl().on("keyup", this.onKeyUp, this);
17258         }
17259         if(this.forceSelection){
17260             this.inputEl().on('blur', this.doForce, this);
17261         }
17262         
17263         if(this.multiple){
17264             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17265             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17266         }
17267     },
17268     
17269     initTickableEvents: function()
17270     {   
17271         this.createList();
17272         
17273         if(this.hiddenName){
17274             
17275             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17276             
17277             this.hiddenField.dom.value =
17278                 this.hiddenValue !== undefined ? this.hiddenValue :
17279                 this.value !== undefined ? this.value : '';
17280
17281             // prevent input submission
17282             this.el.dom.removeAttribute('name');
17283             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17284              
17285              
17286         }
17287         
17288 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17289         
17290         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17291         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17292         if(this.triggerList){
17293             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17294         }
17295          
17296         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17297         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17298         
17299         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17300         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17301         
17302         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17303         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17304         
17305         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17306         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17307         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17308         
17309         this.okBtn.hide();
17310         this.cancelBtn.hide();
17311         
17312         var _this = this;
17313         
17314         (function(){
17315             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17316             _this.list.setWidth(lw);
17317         }).defer(100);
17318         
17319         this.list.on('mouseover', this.onViewOver, this);
17320         this.list.on('mousemove', this.onViewMove, this);
17321         
17322         this.list.on('scroll', this.onViewScroll, this);
17323         
17324         if(!this.tpl){
17325             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17326                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17327         }
17328
17329         this.view = new Roo.View(this.list, this.tpl, {
17330             singleSelect:true,
17331             tickable:true,
17332             parent:this,
17333             store: this.store,
17334             selectedClass: this.selectedClass
17335         });
17336         
17337         //this.view.wrapEl.setDisplayed(false);
17338         this.view.on('click', this.onViewClick, this);
17339         
17340         
17341         
17342         this.store.on('beforeload', this.onBeforeLoad, this);
17343         this.store.on('load', this.onLoad, this);
17344         this.store.on('loadexception', this.onLoadException, this);
17345         
17346         if(this.editable){
17347             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17348                 "up" : function(e){
17349                     this.inKeyMode = true;
17350                     this.selectPrev();
17351                 },
17352
17353                 "down" : function(e){
17354                     this.inKeyMode = true;
17355                     this.selectNext();
17356                 },
17357
17358                 "enter" : function(e){
17359                     if(this.fireEvent("specialkey", this, e)){
17360                         this.onViewClick(false);
17361                     }
17362                     
17363                     return true;
17364                 },
17365
17366                 "esc" : function(e){
17367                     this.onTickableFooterButtonClick(e, false, false);
17368                 },
17369
17370                 "tab" : function(e){
17371                     this.fireEvent("specialkey", this, e);
17372                     
17373                     this.onTickableFooterButtonClick(e, false, false);
17374                     
17375                     return true;
17376                 },
17377
17378                 scope : this,
17379
17380                 doRelay : function(e, fn, key){
17381                     if(this.scope.isExpanded()){
17382                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17383                     }
17384                     return true;
17385                 },
17386
17387                 forceKeyDown: true
17388             });
17389         }
17390         
17391         this.queryDelay = Math.max(this.queryDelay || 10,
17392                 this.mode == 'local' ? 10 : 250);
17393         
17394         
17395         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17396         
17397         if(this.typeAhead){
17398             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17399         }
17400         
17401         if(this.editable !== false){
17402             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17403         }
17404         
17405         this.indicator = this.indicatorEl();
17406         
17407         if(this.indicator){
17408             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17409             this.indicator.hide();
17410         }
17411         
17412     },
17413
17414     onDestroy : function(){
17415         if(this.view){
17416             this.view.setStore(null);
17417             this.view.el.removeAllListeners();
17418             this.view.el.remove();
17419             this.view.purgeListeners();
17420         }
17421         if(this.list){
17422             this.list.dom.innerHTML  = '';
17423         }
17424         
17425         if(this.store){
17426             this.store.un('beforeload', this.onBeforeLoad, this);
17427             this.store.un('load', this.onLoad, this);
17428             this.store.un('loadexception', this.onLoadException, this);
17429         }
17430         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17431     },
17432
17433     // private
17434     fireKey : function(e){
17435         if(e.isNavKeyPress() && !this.list.isVisible()){
17436             this.fireEvent("specialkey", this, e);
17437         }
17438     },
17439
17440     // private
17441     onResize: function(w, h)
17442     {
17443         
17444         
17445 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17446 //        
17447 //        if(typeof w != 'number'){
17448 //            // we do not handle it!?!?
17449 //            return;
17450 //        }
17451 //        var tw = this.trigger.getWidth();
17452 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17453 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17454 //        var x = w - tw;
17455 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17456 //            
17457 //        //this.trigger.setStyle('left', x+'px');
17458 //        
17459 //        if(this.list && this.listWidth === undefined){
17460 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17461 //            this.list.setWidth(lw);
17462 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17463 //        }
17464         
17465     
17466         
17467     },
17468
17469     /**
17470      * Allow or prevent the user from directly editing the field text.  If false is passed,
17471      * the user will only be able to select from the items defined in the dropdown list.  This method
17472      * is the runtime equivalent of setting the 'editable' config option at config time.
17473      * @param {Boolean} value True to allow the user to directly edit the field text
17474      */
17475     setEditable : function(value){
17476         if(value == this.editable){
17477             return;
17478         }
17479         this.editable = value;
17480         if(!value){
17481             this.inputEl().dom.setAttribute('readOnly', true);
17482             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17483             this.inputEl().addClass('x-combo-noedit');
17484         }else{
17485             this.inputEl().dom.removeAttribute('readOnly');
17486             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17487             this.inputEl().removeClass('x-combo-noedit');
17488         }
17489     },
17490
17491     // private
17492     
17493     onBeforeLoad : function(combo,opts){
17494         if(!this.hasFocus){
17495             return;
17496         }
17497          if (!opts.add) {
17498             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17499          }
17500         this.restrictHeight();
17501         this.selectedIndex = -1;
17502     },
17503
17504     // private
17505     onLoad : function(){
17506         
17507         this.hasQuery = false;
17508         
17509         if(!this.hasFocus){
17510             return;
17511         }
17512         
17513         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17514             this.loading.hide();
17515         }
17516         
17517         if(this.store.getCount() > 0){
17518             
17519             this.expand();
17520             this.restrictHeight();
17521             if(this.lastQuery == this.allQuery){
17522                 if(this.editable && !this.tickable){
17523                     this.inputEl().dom.select();
17524                 }
17525                 
17526                 if(
17527                     !this.selectByValue(this.value, true) &&
17528                     this.autoFocus && 
17529                     (
17530                         !this.store.lastOptions ||
17531                         typeof(this.store.lastOptions.add) == 'undefined' || 
17532                         this.store.lastOptions.add != true
17533                     )
17534                 ){
17535                     this.select(0, true);
17536                 }
17537             }else{
17538                 if(this.autoFocus){
17539                     this.selectNext();
17540                 }
17541                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17542                     this.taTask.delay(this.typeAheadDelay);
17543                 }
17544             }
17545         }else{
17546             this.onEmptyResults();
17547         }
17548         
17549         //this.el.focus();
17550     },
17551     // private
17552     onLoadException : function()
17553     {
17554         this.hasQuery = false;
17555         
17556         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17557             this.loading.hide();
17558         }
17559         
17560         if(this.tickable && this.editable){
17561             return;
17562         }
17563         
17564         this.collapse();
17565         // only causes errors at present
17566         //Roo.log(this.store.reader.jsonData);
17567         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17568             // fixme
17569             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17570         //}
17571         
17572         
17573     },
17574     // private
17575     onTypeAhead : function(){
17576         if(this.store.getCount() > 0){
17577             var r = this.store.getAt(0);
17578             var newValue = r.data[this.displayField];
17579             var len = newValue.length;
17580             var selStart = this.getRawValue().length;
17581             
17582             if(selStart != len){
17583                 this.setRawValue(newValue);
17584                 this.selectText(selStart, newValue.length);
17585             }
17586         }
17587     },
17588
17589     // private
17590     onSelect : function(record, index){
17591         
17592         if(this.fireEvent('beforeselect', this, record, index) !== false){
17593         
17594             this.setFromData(index > -1 ? record.data : false);
17595             
17596             this.collapse();
17597             this.fireEvent('select', this, record, index);
17598         }
17599     },
17600
17601     /**
17602      * Returns the currently selected field value or empty string if no value is set.
17603      * @return {String} value The selected value
17604      */
17605     getValue : function()
17606     {
17607         if(Roo.isIOS && this.useNativeIOS){
17608             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17609         }
17610         
17611         if(this.multiple){
17612             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17613         }
17614         
17615         if(this.valueField){
17616             return typeof this.value != 'undefined' ? this.value : '';
17617         }else{
17618             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17619         }
17620     },
17621     
17622     getRawValue : function()
17623     {
17624         if(Roo.isIOS && this.useNativeIOS){
17625             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17626         }
17627         
17628         var v = this.inputEl().getValue();
17629         
17630         return v;
17631     },
17632
17633     /**
17634      * Clears any text/value currently set in the field
17635      */
17636     clearValue : function(){
17637         
17638         if(this.hiddenField){
17639             this.hiddenField.dom.value = '';
17640         }
17641         this.value = '';
17642         this.setRawValue('');
17643         this.lastSelectionText = '';
17644         this.lastData = false;
17645         
17646         var close = this.closeTriggerEl();
17647         
17648         if(close){
17649             close.hide();
17650         }
17651         
17652         this.validate();
17653         
17654     },
17655
17656     /**
17657      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17658      * will be displayed in the field.  If the value does not match the data value of an existing item,
17659      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17660      * Otherwise the field will be blank (although the value will still be set).
17661      * @param {String} value The value to match
17662      */
17663     setValue : function(v)
17664     {
17665         if(Roo.isIOS && this.useNativeIOS){
17666             this.setIOSValue(v);
17667             return;
17668         }
17669         
17670         if(this.multiple){
17671             this.syncValue();
17672             return;
17673         }
17674         
17675         var text = v;
17676         if(this.valueField){
17677             var r = this.findRecord(this.valueField, v);
17678             if(r){
17679                 text = r.data[this.displayField];
17680             }else if(this.valueNotFoundText !== undefined){
17681                 text = this.valueNotFoundText;
17682             }
17683         }
17684         this.lastSelectionText = text;
17685         if(this.hiddenField){
17686             this.hiddenField.dom.value = v;
17687         }
17688         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17689         this.value = v;
17690         
17691         var close = this.closeTriggerEl();
17692         
17693         if(close){
17694             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17695         }
17696         
17697         this.validate();
17698     },
17699     /**
17700      * @property {Object} the last set data for the element
17701      */
17702     
17703     lastData : false,
17704     /**
17705      * Sets the value of the field based on a object which is related to the record format for the store.
17706      * @param {Object} value the value to set as. or false on reset?
17707      */
17708     setFromData : function(o){
17709         
17710         if(this.multiple){
17711             this.addItem(o);
17712             return;
17713         }
17714             
17715         var dv = ''; // display value
17716         var vv = ''; // value value..
17717         this.lastData = o;
17718         if (this.displayField) {
17719             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17720         } else {
17721             // this is an error condition!!!
17722             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17723         }
17724         
17725         if(this.valueField){
17726             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17727         }
17728         
17729         var close = this.closeTriggerEl();
17730         
17731         if(close){
17732             if(dv.length || vv * 1 > 0){
17733                 close.show() ;
17734                 this.blockFocus=true;
17735             } else {
17736                 close.hide();
17737             }             
17738         }
17739         
17740         if(this.hiddenField){
17741             this.hiddenField.dom.value = vv;
17742             
17743             this.lastSelectionText = dv;
17744             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17745             this.value = vv;
17746             return;
17747         }
17748         // no hidden field.. - we store the value in 'value', but still display
17749         // display field!!!!
17750         this.lastSelectionText = dv;
17751         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17752         this.value = vv;
17753         
17754         
17755         
17756     },
17757     // private
17758     reset : function(){
17759         // overridden so that last data is reset..
17760         
17761         if(this.multiple){
17762             this.clearItem();
17763             return;
17764         }
17765         
17766         this.setValue(this.originalValue);
17767         //this.clearInvalid();
17768         this.lastData = false;
17769         if (this.view) {
17770             this.view.clearSelections();
17771         }
17772         
17773         this.validate();
17774     },
17775     // private
17776     findRecord : function(prop, value){
17777         var record;
17778         if(this.store.getCount() > 0){
17779             this.store.each(function(r){
17780                 if(r.data[prop] == value){
17781                     record = r;
17782                     return false;
17783                 }
17784                 return true;
17785             });
17786         }
17787         return record;
17788     },
17789     
17790     getName: function()
17791     {
17792         // returns hidden if it's set..
17793         if (!this.rendered) {return ''};
17794         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17795         
17796     },
17797     // private
17798     onViewMove : function(e, t){
17799         this.inKeyMode = false;
17800     },
17801
17802     // private
17803     onViewOver : function(e, t){
17804         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17805             return;
17806         }
17807         var item = this.view.findItemFromChild(t);
17808         
17809         if(item){
17810             var index = this.view.indexOf(item);
17811             this.select(index, false);
17812         }
17813     },
17814
17815     // private
17816     onViewClick : function(view, doFocus, el, e)
17817     {
17818         var index = this.view.getSelectedIndexes()[0];
17819         
17820         var r = this.store.getAt(index);
17821         
17822         if(this.tickable){
17823             
17824             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17825                 return;
17826             }
17827             
17828             var rm = false;
17829             var _this = this;
17830             
17831             Roo.each(this.tickItems, function(v,k){
17832                 
17833                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17834                     Roo.log(v);
17835                     _this.tickItems.splice(k, 1);
17836                     
17837                     if(typeof(e) == 'undefined' && view == false){
17838                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17839                     }
17840                     
17841                     rm = true;
17842                     return;
17843                 }
17844             });
17845             
17846             if(rm){
17847                 return;
17848             }
17849             
17850             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17851                 this.tickItems.push(r.data);
17852             }
17853             
17854             if(typeof(e) == 'undefined' && view == false){
17855                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17856             }
17857                     
17858             return;
17859         }
17860         
17861         if(r){
17862             this.onSelect(r, index);
17863         }
17864         if(doFocus !== false && !this.blockFocus){
17865             this.inputEl().focus();
17866         }
17867     },
17868
17869     // private
17870     restrictHeight : function(){
17871         //this.innerList.dom.style.height = '';
17872         //var inner = this.innerList.dom;
17873         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17874         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17875         //this.list.beginUpdate();
17876         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17877         this.list.alignTo(this.inputEl(), this.listAlign);
17878         this.list.alignTo(this.inputEl(), this.listAlign);
17879         //this.list.endUpdate();
17880     },
17881
17882     // private
17883     onEmptyResults : function(){
17884         
17885         if(this.tickable && this.editable){
17886             this.hasFocus = false;
17887             this.restrictHeight();
17888             return;
17889         }
17890         
17891         this.collapse();
17892     },
17893
17894     /**
17895      * Returns true if the dropdown list is expanded, else false.
17896      */
17897     isExpanded : function(){
17898         return this.list.isVisible();
17899     },
17900
17901     /**
17902      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17903      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17904      * @param {String} value The data value of the item to select
17905      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17906      * selected item if it is not currently in view (defaults to true)
17907      * @return {Boolean} True if the value matched an item in the list, else false
17908      */
17909     selectByValue : function(v, scrollIntoView){
17910         if(v !== undefined && v !== null){
17911             var r = this.findRecord(this.valueField || this.displayField, v);
17912             if(r){
17913                 this.select(this.store.indexOf(r), scrollIntoView);
17914                 return true;
17915             }
17916         }
17917         return false;
17918     },
17919
17920     /**
17921      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17922      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17923      * @param {Number} index The zero-based index of the list item to select
17924      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17925      * selected item if it is not currently in view (defaults to true)
17926      */
17927     select : function(index, scrollIntoView){
17928         this.selectedIndex = index;
17929         this.view.select(index);
17930         if(scrollIntoView !== false){
17931             var el = this.view.getNode(index);
17932             /*
17933              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17934              */
17935             if(el){
17936                 this.list.scrollChildIntoView(el, false);
17937             }
17938         }
17939     },
17940
17941     // private
17942     selectNext : function(){
17943         var ct = this.store.getCount();
17944         if(ct > 0){
17945             if(this.selectedIndex == -1){
17946                 this.select(0);
17947             }else if(this.selectedIndex < ct-1){
17948                 this.select(this.selectedIndex+1);
17949             }
17950         }
17951     },
17952
17953     // private
17954     selectPrev : function(){
17955         var ct = this.store.getCount();
17956         if(ct > 0){
17957             if(this.selectedIndex == -1){
17958                 this.select(0);
17959             }else if(this.selectedIndex != 0){
17960                 this.select(this.selectedIndex-1);
17961             }
17962         }
17963     },
17964
17965     // private
17966     onKeyUp : function(e){
17967         if(this.editable !== false && !e.isSpecialKey()){
17968             this.lastKey = e.getKey();
17969             this.dqTask.delay(this.queryDelay);
17970         }
17971     },
17972
17973     // private
17974     validateBlur : function(){
17975         return !this.list || !this.list.isVisible();   
17976     },
17977
17978     // private
17979     initQuery : function(){
17980         
17981         var v = this.getRawValue();
17982         
17983         if(this.tickable && this.editable){
17984             v = this.tickableInputEl().getValue();
17985         }
17986         
17987         this.doQuery(v);
17988     },
17989
17990     // private
17991     doForce : function(){
17992         if(this.inputEl().dom.value.length > 0){
17993             this.inputEl().dom.value =
17994                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17995              
17996         }
17997     },
17998
17999     /**
18000      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18001      * query allowing the query action to be canceled if needed.
18002      * @param {String} query The SQL query to execute
18003      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18004      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18005      * saved in the current store (defaults to false)
18006      */
18007     doQuery : function(q, forceAll){
18008         
18009         if(q === undefined || q === null){
18010             q = '';
18011         }
18012         var qe = {
18013             query: q,
18014             forceAll: forceAll,
18015             combo: this,
18016             cancel:false
18017         };
18018         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18019             return false;
18020         }
18021         q = qe.query;
18022         
18023         forceAll = qe.forceAll;
18024         if(forceAll === true || (q.length >= this.minChars)){
18025             
18026             this.hasQuery = true;
18027             
18028             if(this.lastQuery != q || this.alwaysQuery){
18029                 this.lastQuery = q;
18030                 if(this.mode == 'local'){
18031                     this.selectedIndex = -1;
18032                     if(forceAll){
18033                         this.store.clearFilter();
18034                     }else{
18035                         
18036                         if(this.specialFilter){
18037                             this.fireEvent('specialfilter', this);
18038                             this.onLoad();
18039                             return;
18040                         }
18041                         
18042                         this.store.filter(this.displayField, q);
18043                     }
18044                     
18045                     this.store.fireEvent("datachanged", this.store);
18046                     
18047                     this.onLoad();
18048                     
18049                     
18050                 }else{
18051                     
18052                     this.store.baseParams[this.queryParam] = q;
18053                     
18054                     var options = {params : this.getParams(q)};
18055                     
18056                     if(this.loadNext){
18057                         options.add = true;
18058                         options.params.start = this.page * this.pageSize;
18059                     }
18060                     
18061                     this.store.load(options);
18062                     
18063                     /*
18064                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18065                      *  we should expand the list on onLoad
18066                      *  so command out it
18067                      */
18068 //                    this.expand();
18069                 }
18070             }else{
18071                 this.selectedIndex = -1;
18072                 this.onLoad();   
18073             }
18074         }
18075         
18076         this.loadNext = false;
18077     },
18078     
18079     // private
18080     getParams : function(q){
18081         var p = {};
18082         //p[this.queryParam] = q;
18083         
18084         if(this.pageSize){
18085             p.start = 0;
18086             p.limit = this.pageSize;
18087         }
18088         return p;
18089     },
18090
18091     /**
18092      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18093      */
18094     collapse : function(){
18095         if(!this.isExpanded()){
18096             return;
18097         }
18098         
18099         this.list.hide();
18100         
18101         this.hasFocus = false;
18102         
18103         if(this.tickable){
18104             this.okBtn.hide();
18105             this.cancelBtn.hide();
18106             this.trigger.show();
18107             
18108             if(this.editable){
18109                 this.tickableInputEl().dom.value = '';
18110                 this.tickableInputEl().blur();
18111             }
18112             
18113         }
18114         
18115         Roo.get(document).un('mousedown', this.collapseIf, this);
18116         Roo.get(document).un('mousewheel', this.collapseIf, this);
18117         if (!this.editable) {
18118             Roo.get(document).un('keydown', this.listKeyPress, this);
18119         }
18120         this.fireEvent('collapse', this);
18121         
18122         this.validate();
18123     },
18124
18125     // private
18126     collapseIf : function(e){
18127         var in_combo  = e.within(this.el);
18128         var in_list =  e.within(this.list);
18129         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18130         
18131         if (in_combo || in_list || is_list) {
18132             //e.stopPropagation();
18133             return;
18134         }
18135         
18136         if(this.tickable){
18137             this.onTickableFooterButtonClick(e, false, false);
18138         }
18139
18140         this.collapse();
18141         
18142     },
18143
18144     /**
18145      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18146      */
18147     expand : function(){
18148        
18149         if(this.isExpanded() || !this.hasFocus){
18150             return;
18151         }
18152         
18153         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18154         this.list.setWidth(lw);
18155         
18156         Roo.log('expand');
18157         
18158         this.list.show();
18159         
18160         this.restrictHeight();
18161         
18162         if(this.tickable){
18163             
18164             this.tickItems = Roo.apply([], this.item);
18165             
18166             this.okBtn.show();
18167             this.cancelBtn.show();
18168             this.trigger.hide();
18169             
18170             if(this.editable){
18171                 this.tickableInputEl().focus();
18172             }
18173             
18174         }
18175         
18176         Roo.get(document).on('mousedown', this.collapseIf, this);
18177         Roo.get(document).on('mousewheel', this.collapseIf, this);
18178         if (!this.editable) {
18179             Roo.get(document).on('keydown', this.listKeyPress, this);
18180         }
18181         
18182         this.fireEvent('expand', this);
18183     },
18184
18185     // private
18186     // Implements the default empty TriggerField.onTriggerClick function
18187     onTriggerClick : function(e)
18188     {
18189         Roo.log('trigger click');
18190         
18191         if(this.disabled || !this.triggerList){
18192             return;
18193         }
18194         
18195         this.page = 0;
18196         this.loadNext = false;
18197         
18198         if(this.isExpanded()){
18199             this.collapse();
18200             if (!this.blockFocus) {
18201                 this.inputEl().focus();
18202             }
18203             
18204         }else {
18205             this.hasFocus = true;
18206             if(this.triggerAction == 'all') {
18207                 this.doQuery(this.allQuery, true);
18208             } else {
18209                 this.doQuery(this.getRawValue());
18210             }
18211             if (!this.blockFocus) {
18212                 this.inputEl().focus();
18213             }
18214         }
18215     },
18216     
18217     onTickableTriggerClick : function(e)
18218     {
18219         if(this.disabled){
18220             return;
18221         }
18222         
18223         this.page = 0;
18224         this.loadNext = false;
18225         this.hasFocus = true;
18226         
18227         if(this.triggerAction == 'all') {
18228             this.doQuery(this.allQuery, true);
18229         } else {
18230             this.doQuery(this.getRawValue());
18231         }
18232     },
18233     
18234     onSearchFieldClick : function(e)
18235     {
18236         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18237             this.onTickableFooterButtonClick(e, false, false);
18238             return;
18239         }
18240         
18241         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18242             return;
18243         }
18244         
18245         this.page = 0;
18246         this.loadNext = false;
18247         this.hasFocus = true;
18248         
18249         if(this.triggerAction == 'all') {
18250             this.doQuery(this.allQuery, true);
18251         } else {
18252             this.doQuery(this.getRawValue());
18253         }
18254     },
18255     
18256     listKeyPress : function(e)
18257     {
18258         //Roo.log('listkeypress');
18259         // scroll to first matching element based on key pres..
18260         if (e.isSpecialKey()) {
18261             return false;
18262         }
18263         var k = String.fromCharCode(e.getKey()).toUpperCase();
18264         //Roo.log(k);
18265         var match  = false;
18266         var csel = this.view.getSelectedNodes();
18267         var cselitem = false;
18268         if (csel.length) {
18269             var ix = this.view.indexOf(csel[0]);
18270             cselitem  = this.store.getAt(ix);
18271             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18272                 cselitem = false;
18273             }
18274             
18275         }
18276         
18277         this.store.each(function(v) { 
18278             if (cselitem) {
18279                 // start at existing selection.
18280                 if (cselitem.id == v.id) {
18281                     cselitem = false;
18282                 }
18283                 return true;
18284             }
18285                 
18286             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18287                 match = this.store.indexOf(v);
18288                 return false;
18289             }
18290             return true;
18291         }, this);
18292         
18293         if (match === false) {
18294             return true; // no more action?
18295         }
18296         // scroll to?
18297         this.view.select(match);
18298         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18299         sn.scrollIntoView(sn.dom.parentNode, false);
18300     },
18301     
18302     onViewScroll : function(e, t){
18303         
18304         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){
18305             return;
18306         }
18307         
18308         this.hasQuery = true;
18309         
18310         this.loading = this.list.select('.loading', true).first();
18311         
18312         if(this.loading === null){
18313             this.list.createChild({
18314                 tag: 'div',
18315                 cls: 'loading roo-select2-more-results roo-select2-active',
18316                 html: 'Loading more results...'
18317             });
18318             
18319             this.loading = this.list.select('.loading', true).first();
18320             
18321             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18322             
18323             this.loading.hide();
18324         }
18325         
18326         this.loading.show();
18327         
18328         var _combo = this;
18329         
18330         this.page++;
18331         this.loadNext = true;
18332         
18333         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18334         
18335         return;
18336     },
18337     
18338     addItem : function(o)
18339     {   
18340         var dv = ''; // display value
18341         
18342         if (this.displayField) {
18343             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18344         } else {
18345             // this is an error condition!!!
18346             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18347         }
18348         
18349         if(!dv.length){
18350             return;
18351         }
18352         
18353         var choice = this.choices.createChild({
18354             tag: 'li',
18355             cls: 'roo-select2-search-choice',
18356             cn: [
18357                 {
18358                     tag: 'div',
18359                     html: dv
18360                 },
18361                 {
18362                     tag: 'a',
18363                     href: '#',
18364                     cls: 'roo-select2-search-choice-close fa fa-times',
18365                     tabindex: '-1'
18366                 }
18367             ]
18368             
18369         }, this.searchField);
18370         
18371         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18372         
18373         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18374         
18375         this.item.push(o);
18376         
18377         this.lastData = o;
18378         
18379         this.syncValue();
18380         
18381         this.inputEl().dom.value = '';
18382         
18383         this.validate();
18384     },
18385     
18386     onRemoveItem : function(e, _self, o)
18387     {
18388         e.preventDefault();
18389         
18390         this.lastItem = Roo.apply([], this.item);
18391         
18392         var index = this.item.indexOf(o.data) * 1;
18393         
18394         if( index < 0){
18395             Roo.log('not this item?!');
18396             return;
18397         }
18398         
18399         this.item.splice(index, 1);
18400         o.item.remove();
18401         
18402         this.syncValue();
18403         
18404         this.fireEvent('remove', this, e);
18405         
18406         this.validate();
18407         
18408     },
18409     
18410     syncValue : function()
18411     {
18412         if(!this.item.length){
18413             this.clearValue();
18414             return;
18415         }
18416             
18417         var value = [];
18418         var _this = this;
18419         Roo.each(this.item, function(i){
18420             if(_this.valueField){
18421                 value.push(i[_this.valueField]);
18422                 return;
18423             }
18424
18425             value.push(i);
18426         });
18427
18428         this.value = value.join(',');
18429
18430         if(this.hiddenField){
18431             this.hiddenField.dom.value = this.value;
18432         }
18433         
18434         this.store.fireEvent("datachanged", this.store);
18435         
18436         this.validate();
18437     },
18438     
18439     clearItem : function()
18440     {
18441         if(!this.multiple){
18442             return;
18443         }
18444         
18445         this.item = [];
18446         
18447         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18448            c.remove();
18449         });
18450         
18451         this.syncValue();
18452         
18453         this.validate();
18454         
18455         if(this.tickable && !Roo.isTouch){
18456             this.view.refresh();
18457         }
18458     },
18459     
18460     inputEl: function ()
18461     {
18462         if(Roo.isIOS && this.useNativeIOS){
18463             return this.el.select('select.roo-ios-select', true).first();
18464         }
18465         
18466         if(Roo.isTouch && this.mobileTouchView){
18467             return this.el.select('input.form-control',true).first();
18468         }
18469         
18470         if(this.tickable){
18471             return this.searchField;
18472         }
18473         
18474         return this.el.select('input.form-control',true).first();
18475     },
18476     
18477     onTickableFooterButtonClick : function(e, btn, el)
18478     {
18479         e.preventDefault();
18480         
18481         this.lastItem = Roo.apply([], this.item);
18482         
18483         if(btn && btn.name == 'cancel'){
18484             this.tickItems = Roo.apply([], this.item);
18485             this.collapse();
18486             return;
18487         }
18488         
18489         this.clearItem();
18490         
18491         var _this = this;
18492         
18493         Roo.each(this.tickItems, function(o){
18494             _this.addItem(o);
18495         });
18496         
18497         this.collapse();
18498         
18499     },
18500     
18501     validate : function()
18502     {
18503         if(this.getVisibilityEl().hasClass('hidden')){
18504             return true;
18505         }
18506         
18507         var v = this.getRawValue();
18508         
18509         if(this.multiple){
18510             v = this.getValue();
18511         }
18512         
18513         if(this.disabled || this.allowBlank || v.length){
18514             this.markValid();
18515             return true;
18516         }
18517         
18518         this.markInvalid();
18519         return false;
18520     },
18521     
18522     tickableInputEl : function()
18523     {
18524         if(!this.tickable || !this.editable){
18525             return this.inputEl();
18526         }
18527         
18528         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18529     },
18530     
18531     
18532     getAutoCreateTouchView : function()
18533     {
18534         var id = Roo.id();
18535         
18536         var cfg = {
18537             cls: 'form-group' //input-group
18538         };
18539         
18540         var input =  {
18541             tag: 'input',
18542             id : id,
18543             type : this.inputType,
18544             cls : 'form-control x-combo-noedit',
18545             autocomplete: 'new-password',
18546             placeholder : this.placeholder || '',
18547             readonly : true
18548         };
18549         
18550         if (this.name) {
18551             input.name = this.name;
18552         }
18553         
18554         if (this.size) {
18555             input.cls += ' input-' + this.size;
18556         }
18557         
18558         if (this.disabled) {
18559             input.disabled = true;
18560         }
18561         
18562         var inputblock = {
18563             cls : 'roo-combobox-wrap',
18564             cn : [
18565                 input
18566             ]
18567         };
18568         
18569         if(this.before){
18570             inputblock.cls += ' input-group';
18571             
18572             inputblock.cn.unshift({
18573                 tag :'span',
18574                 cls : 'input-group-addon input-group-prepend input-group-text',
18575                 html : this.before
18576             });
18577         }
18578         
18579         if(this.removable && !this.multiple){
18580             inputblock.cls += ' roo-removable';
18581             
18582             inputblock.cn.push({
18583                 tag: 'button',
18584                 html : 'x',
18585                 cls : 'roo-combo-removable-btn close'
18586             });
18587         }
18588
18589         if(this.hasFeedback && !this.allowBlank){
18590             
18591             inputblock.cls += ' has-feedback';
18592             
18593             inputblock.cn.push({
18594                 tag: 'span',
18595                 cls: 'glyphicon form-control-feedback'
18596             });
18597             
18598         }
18599         
18600         if (this.after) {
18601             
18602             inputblock.cls += (this.before) ? '' : ' input-group';
18603             
18604             inputblock.cn.push({
18605                 tag :'span',
18606                 cls : 'input-group-addon input-group-append input-group-text',
18607                 html : this.after
18608             });
18609         }
18610
18611         
18612         var ibwrap = inputblock;
18613         
18614         if(this.multiple){
18615             ibwrap = {
18616                 tag: 'ul',
18617                 cls: 'roo-select2-choices',
18618                 cn:[
18619                     {
18620                         tag: 'li',
18621                         cls: 'roo-select2-search-field',
18622                         cn: [
18623
18624                             inputblock
18625                         ]
18626                     }
18627                 ]
18628             };
18629         
18630             
18631         }
18632         
18633         var combobox = {
18634             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18635             cn: [
18636                 {
18637                     tag: 'input',
18638                     type : 'hidden',
18639                     cls: 'form-hidden-field'
18640                 },
18641                 ibwrap
18642             ]
18643         };
18644         
18645         if(!this.multiple && this.showToggleBtn){
18646             
18647             var caret = {
18648                 cls: 'caret'
18649             };
18650             
18651             if (this.caret != false) {
18652                 caret = {
18653                      tag: 'i',
18654                      cls: 'fa fa-' + this.caret
18655                 };
18656                 
18657             }
18658             
18659             combobox.cn.push({
18660                 tag :'span',
18661                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18662                 cn : [
18663                     Roo.bootstrap.version == 3 ? caret : '',
18664                     {
18665                         tag: 'span',
18666                         cls: 'combobox-clear',
18667                         cn  : [
18668                             {
18669                                 tag : 'i',
18670                                 cls: 'icon-remove'
18671                             }
18672                         ]
18673                     }
18674                 ]
18675
18676             })
18677         }
18678         
18679         if(this.multiple){
18680             combobox.cls += ' roo-select2-container-multi';
18681         }
18682         
18683         var required =  this.allowBlank ?  {
18684                     tag : 'i',
18685                     style: 'display: none'
18686                 } : {
18687                    tag : 'i',
18688                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18689                    tooltip : 'This field is required'
18690                 };
18691         
18692         var align = this.labelAlign || this.parentLabelAlign();
18693         
18694         if (align ==='left' && this.fieldLabel.length) {
18695
18696             cfg.cn = [
18697                 required,
18698                 {
18699                     tag: 'label',
18700                     cls : 'control-label col-form-label',
18701                     html : this.fieldLabel
18702
18703                 },
18704                 {
18705                     cls : 'roo-combobox-wrap ', 
18706                     cn: [
18707                         combobox
18708                     ]
18709                 }
18710             ];
18711             
18712             var labelCfg = cfg.cn[1];
18713             var contentCfg = cfg.cn[2];
18714             
18715
18716             if(this.indicatorpos == 'right'){
18717                 cfg.cn = [
18718                     {
18719                         tag: 'label',
18720                         'for' :  id,
18721                         cls : 'control-label col-form-label',
18722                         cn : [
18723                             {
18724                                 tag : 'span',
18725                                 html : this.fieldLabel
18726                             },
18727                             required
18728                         ]
18729                     },
18730                     {
18731                         cls : "roo-combobox-wrap ",
18732                         cn: [
18733                             combobox
18734                         ]
18735                     }
18736
18737                 ];
18738                 
18739                 labelCfg = cfg.cn[0];
18740                 contentCfg = cfg.cn[1];
18741             }
18742             
18743            
18744             
18745             if(this.labelWidth > 12){
18746                 labelCfg.style = "width: " + this.labelWidth + 'px';
18747             }
18748            
18749             if(this.labelWidth < 13 && this.labelmd == 0){
18750                 this.labelmd = this.labelWidth;
18751             }
18752             
18753             if(this.labellg > 0){
18754                 labelCfg.cls += ' col-lg-' + this.labellg;
18755                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18756             }
18757             
18758             if(this.labelmd > 0){
18759                 labelCfg.cls += ' col-md-' + this.labelmd;
18760                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18761             }
18762             
18763             if(this.labelsm > 0){
18764                 labelCfg.cls += ' col-sm-' + this.labelsm;
18765                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18766             }
18767             
18768             if(this.labelxs > 0){
18769                 labelCfg.cls += ' col-xs-' + this.labelxs;
18770                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18771             }
18772                 
18773                 
18774         } else if ( this.fieldLabel.length) {
18775             cfg.cn = [
18776                required,
18777                 {
18778                     tag: 'label',
18779                     cls : 'control-label',
18780                     html : this.fieldLabel
18781
18782                 },
18783                 {
18784                     cls : '', 
18785                     cn: [
18786                         combobox
18787                     ]
18788                 }
18789             ];
18790             
18791             if(this.indicatorpos == 'right'){
18792                 cfg.cn = [
18793                     {
18794                         tag: 'label',
18795                         cls : 'control-label',
18796                         html : this.fieldLabel,
18797                         cn : [
18798                             required
18799                         ]
18800                     },
18801                     {
18802                         cls : '', 
18803                         cn: [
18804                             combobox
18805                         ]
18806                     }
18807                 ];
18808             }
18809         } else {
18810             cfg.cn = combobox;    
18811         }
18812         
18813         
18814         var settings = this;
18815         
18816         ['xs','sm','md','lg'].map(function(size){
18817             if (settings[size]) {
18818                 cfg.cls += ' col-' + size + '-' + settings[size];
18819             }
18820         });
18821         
18822         return cfg;
18823     },
18824     
18825     initTouchView : function()
18826     {
18827         this.renderTouchView();
18828         
18829         this.touchViewEl.on('scroll', function(){
18830             this.el.dom.scrollTop = 0;
18831         }, this);
18832         
18833         this.originalValue = this.getValue();
18834         
18835         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18836         
18837         this.inputEl().on("click", this.showTouchView, this);
18838         if (this.triggerEl) {
18839             this.triggerEl.on("click", this.showTouchView, this);
18840         }
18841         
18842         
18843         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18844         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18845         
18846         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18847         
18848         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18849         this.store.on('load', this.onTouchViewLoad, this);
18850         this.store.on('loadexception', this.onTouchViewLoadException, this);
18851         
18852         if(this.hiddenName){
18853             
18854             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18855             
18856             this.hiddenField.dom.value =
18857                 this.hiddenValue !== undefined ? this.hiddenValue :
18858                 this.value !== undefined ? this.value : '';
18859         
18860             this.el.dom.removeAttribute('name');
18861             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18862         }
18863         
18864         if(this.multiple){
18865             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18866             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18867         }
18868         
18869         if(this.removable && !this.multiple){
18870             var close = this.closeTriggerEl();
18871             if(close){
18872                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18873                 close.on('click', this.removeBtnClick, this, close);
18874             }
18875         }
18876         /*
18877          * fix the bug in Safari iOS8
18878          */
18879         this.inputEl().on("focus", function(e){
18880             document.activeElement.blur();
18881         }, this);
18882         
18883         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18884         
18885         return;
18886         
18887         
18888     },
18889     
18890     renderTouchView : function()
18891     {
18892         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18893         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894         
18895         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18896         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897         
18898         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18899         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900         this.touchViewBodyEl.setStyle('overflow', 'auto');
18901         
18902         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18903         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904         
18905         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18906         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907         
18908     },
18909     
18910     showTouchView : function()
18911     {
18912         if(this.disabled){
18913             return;
18914         }
18915         
18916         this.touchViewHeaderEl.hide();
18917
18918         if(this.modalTitle.length){
18919             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18920             this.touchViewHeaderEl.show();
18921         }
18922
18923         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18924         this.touchViewEl.show();
18925
18926         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18927         
18928         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18929         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18930
18931         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18932
18933         if(this.modalTitle.length){
18934             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18935         }
18936         
18937         this.touchViewBodyEl.setHeight(bodyHeight);
18938
18939         if(this.animate){
18940             var _this = this;
18941             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18942         }else{
18943             this.touchViewEl.addClass(['in','show']);
18944         }
18945         
18946         if(this._touchViewMask){
18947             Roo.get(document.body).addClass("x-body-masked");
18948             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18949             this._touchViewMask.setStyle('z-index', 10000);
18950             this._touchViewMask.addClass('show');
18951         }
18952         
18953         this.doTouchViewQuery();
18954         
18955     },
18956     
18957     hideTouchView : function()
18958     {
18959         this.touchViewEl.removeClass(['in','show']);
18960
18961         if(this.animate){
18962             var _this = this;
18963             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18964         }else{
18965             this.touchViewEl.setStyle('display', 'none');
18966         }
18967         
18968         if(this._touchViewMask){
18969             this._touchViewMask.removeClass('show');
18970             Roo.get(document.body).removeClass("x-body-masked");
18971         }
18972     },
18973     
18974     setTouchViewValue : function()
18975     {
18976         if(this.multiple){
18977             this.clearItem();
18978         
18979             var _this = this;
18980
18981             Roo.each(this.tickItems, function(o){
18982                 this.addItem(o);
18983             }, this);
18984         }
18985         
18986         this.hideTouchView();
18987     },
18988     
18989     doTouchViewQuery : function()
18990     {
18991         var qe = {
18992             query: '',
18993             forceAll: true,
18994             combo: this,
18995             cancel:false
18996         };
18997         
18998         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18999             return false;
19000         }
19001         
19002         if(!this.alwaysQuery || this.mode == 'local'){
19003             this.onTouchViewLoad();
19004             return;
19005         }
19006         
19007         this.store.load();
19008     },
19009     
19010     onTouchViewBeforeLoad : function(combo,opts)
19011     {
19012         return;
19013     },
19014
19015     // private
19016     onTouchViewLoad : function()
19017     {
19018         if(this.store.getCount() < 1){
19019             this.onTouchViewEmptyResults();
19020             return;
19021         }
19022         
19023         this.clearTouchView();
19024         
19025         var rawValue = this.getRawValue();
19026         
19027         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19028         
19029         this.tickItems = [];
19030         
19031         this.store.data.each(function(d, rowIndex){
19032             var row = this.touchViewListGroup.createChild(template);
19033             
19034             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19035                 row.addClass(d.data.cls);
19036             }
19037             
19038             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19039                 var cfg = {
19040                     data : d.data,
19041                     html : d.data[this.displayField]
19042                 };
19043                 
19044                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19045                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19046                 }
19047             }
19048             row.removeClass('selected');
19049             if(!this.multiple && this.valueField &&
19050                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19051             {
19052                 // radio buttons..
19053                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19054                 row.addClass('selected');
19055             }
19056             
19057             if(this.multiple && this.valueField &&
19058                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19059             {
19060                 
19061                 // checkboxes...
19062                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19063                 this.tickItems.push(d.data);
19064             }
19065             
19066             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19067             
19068         }, this);
19069         
19070         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19071         
19072         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19073
19074         if(this.modalTitle.length){
19075             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19076         }
19077
19078         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19079         
19080         if(this.mobile_restrict_height && listHeight < bodyHeight){
19081             this.touchViewBodyEl.setHeight(listHeight);
19082         }
19083         
19084         var _this = this;
19085         
19086         if(firstChecked && listHeight > bodyHeight){
19087             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19088         }
19089         
19090     },
19091     
19092     onTouchViewLoadException : function()
19093     {
19094         this.hideTouchView();
19095     },
19096     
19097     onTouchViewEmptyResults : function()
19098     {
19099         this.clearTouchView();
19100         
19101         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19102         
19103         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19104         
19105     },
19106     
19107     clearTouchView : function()
19108     {
19109         this.touchViewListGroup.dom.innerHTML = '';
19110     },
19111     
19112     onTouchViewClick : function(e, el, o)
19113     {
19114         e.preventDefault();
19115         
19116         var row = o.row;
19117         var rowIndex = o.rowIndex;
19118         
19119         var r = this.store.getAt(rowIndex);
19120         
19121         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19122             
19123             if(!this.multiple){
19124                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19125                     c.dom.removeAttribute('checked');
19126                 }, this);
19127
19128                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19129
19130                 this.setFromData(r.data);
19131
19132                 var close = this.closeTriggerEl();
19133
19134                 if(close){
19135                     close.show();
19136                 }
19137
19138                 this.hideTouchView();
19139
19140                 this.fireEvent('select', this, r, rowIndex);
19141
19142                 return;
19143             }
19144
19145             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19146                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19147                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19148                 return;
19149             }
19150
19151             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19152             this.addItem(r.data);
19153             this.tickItems.push(r.data);
19154         }
19155     },
19156     
19157     getAutoCreateNativeIOS : function()
19158     {
19159         var cfg = {
19160             cls: 'form-group' //input-group,
19161         };
19162         
19163         var combobox =  {
19164             tag: 'select',
19165             cls : 'roo-ios-select'
19166         };
19167         
19168         if (this.name) {
19169             combobox.name = this.name;
19170         }
19171         
19172         if (this.disabled) {
19173             combobox.disabled = true;
19174         }
19175         
19176         var settings = this;
19177         
19178         ['xs','sm','md','lg'].map(function(size){
19179             if (settings[size]) {
19180                 cfg.cls += ' col-' + size + '-' + settings[size];
19181             }
19182         });
19183         
19184         cfg.cn = combobox;
19185         
19186         return cfg;
19187         
19188     },
19189     
19190     initIOSView : function()
19191     {
19192         this.store.on('load', this.onIOSViewLoad, this);
19193         
19194         return;
19195     },
19196     
19197     onIOSViewLoad : function()
19198     {
19199         if(this.store.getCount() < 1){
19200             return;
19201         }
19202         
19203         this.clearIOSView();
19204         
19205         if(this.allowBlank) {
19206             
19207             var default_text = '-- SELECT --';
19208             
19209             if(this.placeholder.length){
19210                 default_text = this.placeholder;
19211             }
19212             
19213             if(this.emptyTitle.length){
19214                 default_text += ' - ' + this.emptyTitle + ' -';
19215             }
19216             
19217             var opt = this.inputEl().createChild({
19218                 tag: 'option',
19219                 value : 0,
19220                 html : default_text
19221             });
19222             
19223             var o = {};
19224             o[this.valueField] = 0;
19225             o[this.displayField] = default_text;
19226             
19227             this.ios_options.push({
19228                 data : o,
19229                 el : opt
19230             });
19231             
19232         }
19233         
19234         this.store.data.each(function(d, rowIndex){
19235             
19236             var html = '';
19237             
19238             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19239                 html = d.data[this.displayField];
19240             }
19241             
19242             var value = '';
19243             
19244             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19245                 value = d.data[this.valueField];
19246             }
19247             
19248             var option = {
19249                 tag: 'option',
19250                 value : value,
19251                 html : html
19252             };
19253             
19254             if(this.value == d.data[this.valueField]){
19255                 option['selected'] = true;
19256             }
19257             
19258             var opt = this.inputEl().createChild(option);
19259             
19260             this.ios_options.push({
19261                 data : d.data,
19262                 el : opt
19263             });
19264             
19265         }, this);
19266         
19267         this.inputEl().on('change', function(){
19268            this.fireEvent('select', this);
19269         }, this);
19270         
19271     },
19272     
19273     clearIOSView: function()
19274     {
19275         this.inputEl().dom.innerHTML = '';
19276         
19277         this.ios_options = [];
19278     },
19279     
19280     setIOSValue: function(v)
19281     {
19282         this.value = v;
19283         
19284         if(!this.ios_options){
19285             return;
19286         }
19287         
19288         Roo.each(this.ios_options, function(opts){
19289            
19290            opts.el.dom.removeAttribute('selected');
19291            
19292            if(opts.data[this.valueField] != v){
19293                return;
19294            }
19295            
19296            opts.el.dom.setAttribute('selected', true);
19297            
19298         }, this);
19299     }
19300
19301     /** 
19302     * @cfg {Boolean} grow 
19303     * @hide 
19304     */
19305     /** 
19306     * @cfg {Number} growMin 
19307     * @hide 
19308     */
19309     /** 
19310     * @cfg {Number} growMax 
19311     * @hide 
19312     */
19313     /**
19314      * @hide
19315      * @method autoSize
19316      */
19317 });
19318
19319 Roo.apply(Roo.bootstrap.ComboBox,  {
19320     
19321     header : {
19322         tag: 'div',
19323         cls: 'modal-header',
19324         cn: [
19325             {
19326                 tag: 'h4',
19327                 cls: 'modal-title'
19328             }
19329         ]
19330     },
19331     
19332     body : {
19333         tag: 'div',
19334         cls: 'modal-body',
19335         cn: [
19336             {
19337                 tag: 'ul',
19338                 cls: 'list-group'
19339             }
19340         ]
19341     },
19342     
19343     listItemRadio : {
19344         tag: 'li',
19345         cls: 'list-group-item',
19346         cn: [
19347             {
19348                 tag: 'span',
19349                 cls: 'roo-combobox-list-group-item-value'
19350             },
19351             {
19352                 tag: 'div',
19353                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19354                 cn: [
19355                     {
19356                         tag: 'input',
19357                         type: 'radio'
19358                     },
19359                     {
19360                         tag: 'label'
19361                     }
19362                 ]
19363             }
19364         ]
19365     },
19366     
19367     listItemCheckbox : {
19368         tag: 'li',
19369         cls: 'list-group-item',
19370         cn: [
19371             {
19372                 tag: 'span',
19373                 cls: 'roo-combobox-list-group-item-value'
19374             },
19375             {
19376                 tag: 'div',
19377                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19378                 cn: [
19379                     {
19380                         tag: 'input',
19381                         type: 'checkbox'
19382                     },
19383                     {
19384                         tag: 'label'
19385                     }
19386                 ]
19387             }
19388         ]
19389     },
19390     
19391     emptyResult : {
19392         tag: 'div',
19393         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19394     },
19395     
19396     footer : {
19397         tag: 'div',
19398         cls: 'modal-footer',
19399         cn: [
19400             {
19401                 tag: 'div',
19402                 cls: 'row',
19403                 cn: [
19404                     {
19405                         tag: 'div',
19406                         cls: 'col-xs-6 text-left',
19407                         cn: {
19408                             tag: 'button',
19409                             cls: 'btn btn-danger roo-touch-view-cancel',
19410                             html: 'Cancel'
19411                         }
19412                     },
19413                     {
19414                         tag: 'div',
19415                         cls: 'col-xs-6 text-right',
19416                         cn: {
19417                             tag: 'button',
19418                             cls: 'btn btn-success roo-touch-view-ok',
19419                             html: 'OK'
19420                         }
19421                     }
19422                 ]
19423             }
19424         ]
19425         
19426     }
19427 });
19428
19429 Roo.apply(Roo.bootstrap.ComboBox,  {
19430     
19431     touchViewTemplate : {
19432         tag: 'div',
19433         cls: 'modal fade roo-combobox-touch-view',
19434         cn: [
19435             {
19436                 tag: 'div',
19437                 cls: 'modal-dialog',
19438                 style : 'position:fixed', // we have to fix position....
19439                 cn: [
19440                     {
19441                         tag: 'div',
19442                         cls: 'modal-content',
19443                         cn: [
19444                             Roo.bootstrap.ComboBox.header,
19445                             Roo.bootstrap.ComboBox.body,
19446                             Roo.bootstrap.ComboBox.footer
19447                         ]
19448                     }
19449                 ]
19450             }
19451         ]
19452     }
19453 });/*
19454  * Based on:
19455  * Ext JS Library 1.1.1
19456  * Copyright(c) 2006-2007, Ext JS, LLC.
19457  *
19458  * Originally Released Under LGPL - original licence link has changed is not relivant.
19459  *
19460  * Fork - LGPL
19461  * <script type="text/javascript">
19462  */
19463
19464 /**
19465  * @class Roo.View
19466  * @extends Roo.util.Observable
19467  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19468  * This class also supports single and multi selection modes. <br>
19469  * Create a data model bound view:
19470  <pre><code>
19471  var store = new Roo.data.Store(...);
19472
19473  var view = new Roo.View({
19474     el : "my-element",
19475     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19476  
19477     singleSelect: true,
19478     selectedClass: "ydataview-selected",
19479     store: store
19480  });
19481
19482  // listen for node click?
19483  view.on("click", function(vw, index, node, e){
19484  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19485  });
19486
19487  // load XML data
19488  dataModel.load("foobar.xml");
19489  </code></pre>
19490  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19491  * <br><br>
19492  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19493  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19494  * 
19495  * Note: old style constructor is still suported (container, template, config)
19496  * 
19497  * @constructor
19498  * Create a new View
19499  * @param {Object} config The config object
19500  * 
19501  */
19502 Roo.View = function(config, depreciated_tpl, depreciated_config){
19503     
19504     this.parent = false;
19505     
19506     if (typeof(depreciated_tpl) == 'undefined') {
19507         // new way.. - universal constructor.
19508         Roo.apply(this, config);
19509         this.el  = Roo.get(this.el);
19510     } else {
19511         // old format..
19512         this.el  = Roo.get(config);
19513         this.tpl = depreciated_tpl;
19514         Roo.apply(this, depreciated_config);
19515     }
19516     this.wrapEl  = this.el.wrap().wrap();
19517     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19518     
19519     
19520     if(typeof(this.tpl) == "string"){
19521         this.tpl = new Roo.Template(this.tpl);
19522     } else {
19523         // support xtype ctors..
19524         this.tpl = new Roo.factory(this.tpl, Roo);
19525     }
19526     
19527     
19528     this.tpl.compile();
19529     
19530     /** @private */
19531     this.addEvents({
19532         /**
19533          * @event beforeclick
19534          * Fires before a click is processed. Returns false to cancel the default action.
19535          * @param {Roo.View} this
19536          * @param {Number} index The index of the target node
19537          * @param {HTMLElement} node The target node
19538          * @param {Roo.EventObject} e The raw event object
19539          */
19540             "beforeclick" : true,
19541         /**
19542          * @event click
19543          * Fires when a template node is clicked.
19544          * @param {Roo.View} this
19545          * @param {Number} index The index of the target node
19546          * @param {HTMLElement} node The target node
19547          * @param {Roo.EventObject} e The raw event object
19548          */
19549             "click" : true,
19550         /**
19551          * @event dblclick
19552          * Fires when a template node is double clicked.
19553          * @param {Roo.View} this
19554          * @param {Number} index The index of the target node
19555          * @param {HTMLElement} node The target node
19556          * @param {Roo.EventObject} e The raw event object
19557          */
19558             "dblclick" : true,
19559         /**
19560          * @event contextmenu
19561          * Fires when a template node is right clicked.
19562          * @param {Roo.View} this
19563          * @param {Number} index The index of the target node
19564          * @param {HTMLElement} node The target node
19565          * @param {Roo.EventObject} e The raw event object
19566          */
19567             "contextmenu" : true,
19568         /**
19569          * @event selectionchange
19570          * Fires when the selected nodes change.
19571          * @param {Roo.View} this
19572          * @param {Array} selections Array of the selected nodes
19573          */
19574             "selectionchange" : true,
19575     
19576         /**
19577          * @event beforeselect
19578          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19579          * @param {Roo.View} this
19580          * @param {HTMLElement} node The node to be selected
19581          * @param {Array} selections Array of currently selected nodes
19582          */
19583             "beforeselect" : true,
19584         /**
19585          * @event preparedata
19586          * Fires on every row to render, to allow you to change the data.
19587          * @param {Roo.View} this
19588          * @param {Object} data to be rendered (change this)
19589          */
19590           "preparedata" : true
19591           
19592           
19593         });
19594
19595
19596
19597     this.el.on({
19598         "click": this.onClick,
19599         "dblclick": this.onDblClick,
19600         "contextmenu": this.onContextMenu,
19601         scope:this
19602     });
19603
19604     this.selections = [];
19605     this.nodes = [];
19606     this.cmp = new Roo.CompositeElementLite([]);
19607     if(this.store){
19608         this.store = Roo.factory(this.store, Roo.data);
19609         this.setStore(this.store, true);
19610     }
19611     
19612     if ( this.footer && this.footer.xtype) {
19613            
19614          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19615         
19616         this.footer.dataSource = this.store;
19617         this.footer.container = fctr;
19618         this.footer = Roo.factory(this.footer, Roo);
19619         fctr.insertFirst(this.el);
19620         
19621         // this is a bit insane - as the paging toolbar seems to detach the el..
19622 //        dom.parentNode.parentNode.parentNode
19623          // they get detached?
19624     }
19625     
19626     
19627     Roo.View.superclass.constructor.call(this);
19628     
19629     
19630 };
19631
19632 Roo.extend(Roo.View, Roo.util.Observable, {
19633     
19634      /**
19635      * @cfg {Roo.data.Store} store Data store to load data from.
19636      */
19637     store : false,
19638     
19639     /**
19640      * @cfg {String|Roo.Element} el The container element.
19641      */
19642     el : '',
19643     
19644     /**
19645      * @cfg {String|Roo.Template} tpl The template used by this View 
19646      */
19647     tpl : false,
19648     /**
19649      * @cfg {String} dataName the named area of the template to use as the data area
19650      *                          Works with domtemplates roo-name="name"
19651      */
19652     dataName: false,
19653     /**
19654      * @cfg {String} selectedClass The css class to add to selected nodes
19655      */
19656     selectedClass : "x-view-selected",
19657      /**
19658      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19659      */
19660     emptyText : "",
19661     
19662     /**
19663      * @cfg {String} text to display on mask (default Loading)
19664      */
19665     mask : false,
19666     /**
19667      * @cfg {Boolean} multiSelect Allow multiple selection
19668      */
19669     multiSelect : false,
19670     /**
19671      * @cfg {Boolean} singleSelect Allow single selection
19672      */
19673     singleSelect:  false,
19674     
19675     /**
19676      * @cfg {Boolean} toggleSelect - selecting 
19677      */
19678     toggleSelect : false,
19679     
19680     /**
19681      * @cfg {Boolean} tickable - selecting 
19682      */
19683     tickable : false,
19684     
19685     /**
19686      * Returns the element this view is bound to.
19687      * @return {Roo.Element}
19688      */
19689     getEl : function(){
19690         return this.wrapEl;
19691     },
19692     
19693     
19694
19695     /**
19696      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19697      */
19698     refresh : function(){
19699         //Roo.log('refresh');
19700         var t = this.tpl;
19701         
19702         // if we are using something like 'domtemplate', then
19703         // the what gets used is:
19704         // t.applySubtemplate(NAME, data, wrapping data..)
19705         // the outer template then get' applied with
19706         //     the store 'extra data'
19707         // and the body get's added to the
19708         //      roo-name="data" node?
19709         //      <span class='roo-tpl-{name}'></span> ?????
19710         
19711         
19712         
19713         this.clearSelections();
19714         this.el.update("");
19715         var html = [];
19716         var records = this.store.getRange();
19717         if(records.length < 1) {
19718             
19719             // is this valid??  = should it render a template??
19720             
19721             this.el.update(this.emptyText);
19722             return;
19723         }
19724         var el = this.el;
19725         if (this.dataName) {
19726             this.el.update(t.apply(this.store.meta)); //????
19727             el = this.el.child('.roo-tpl-' + this.dataName);
19728         }
19729         
19730         for(var i = 0, len = records.length; i < len; i++){
19731             var data = this.prepareData(records[i].data, i, records[i]);
19732             this.fireEvent("preparedata", this, data, i, records[i]);
19733             
19734             var d = Roo.apply({}, data);
19735             
19736             if(this.tickable){
19737                 Roo.apply(d, {'roo-id' : Roo.id()});
19738                 
19739                 var _this = this;
19740             
19741                 Roo.each(this.parent.item, function(item){
19742                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19743                         return;
19744                     }
19745                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19746                 });
19747             }
19748             
19749             html[html.length] = Roo.util.Format.trim(
19750                 this.dataName ?
19751                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19752                     t.apply(d)
19753             );
19754         }
19755         
19756         
19757         
19758         el.update(html.join(""));
19759         this.nodes = el.dom.childNodes;
19760         this.updateIndexes(0);
19761     },
19762     
19763
19764     /**
19765      * Function to override to reformat the data that is sent to
19766      * the template for each node.
19767      * DEPRICATED - use the preparedata event handler.
19768      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19769      * a JSON object for an UpdateManager bound view).
19770      */
19771     prepareData : function(data, index, record)
19772     {
19773         this.fireEvent("preparedata", this, data, index, record);
19774         return data;
19775     },
19776
19777     onUpdate : function(ds, record){
19778         // Roo.log('on update');   
19779         this.clearSelections();
19780         var index = this.store.indexOf(record);
19781         var n = this.nodes[index];
19782         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19783         n.parentNode.removeChild(n);
19784         this.updateIndexes(index, index);
19785     },
19786
19787     
19788     
19789 // --------- FIXME     
19790     onAdd : function(ds, records, index)
19791     {
19792         //Roo.log(['on Add', ds, records, index] );        
19793         this.clearSelections();
19794         if(this.nodes.length == 0){
19795             this.refresh();
19796             return;
19797         }
19798         var n = this.nodes[index];
19799         for(var i = 0, len = records.length; i < len; i++){
19800             var d = this.prepareData(records[i].data, i, records[i]);
19801             if(n){
19802                 this.tpl.insertBefore(n, d);
19803             }else{
19804                 
19805                 this.tpl.append(this.el, d);
19806             }
19807         }
19808         this.updateIndexes(index);
19809     },
19810
19811     onRemove : function(ds, record, index){
19812        // Roo.log('onRemove');
19813         this.clearSelections();
19814         var el = this.dataName  ?
19815             this.el.child('.roo-tpl-' + this.dataName) :
19816             this.el; 
19817         
19818         el.dom.removeChild(this.nodes[index]);
19819         this.updateIndexes(index);
19820     },
19821
19822     /**
19823      * Refresh an individual node.
19824      * @param {Number} index
19825      */
19826     refreshNode : function(index){
19827         this.onUpdate(this.store, this.store.getAt(index));
19828     },
19829
19830     updateIndexes : function(startIndex, endIndex){
19831         var ns = this.nodes;
19832         startIndex = startIndex || 0;
19833         endIndex = endIndex || ns.length - 1;
19834         for(var i = startIndex; i <= endIndex; i++){
19835             ns[i].nodeIndex = i;
19836         }
19837     },
19838
19839     /**
19840      * Changes the data store this view uses and refresh the view.
19841      * @param {Store} store
19842      */
19843     setStore : function(store, initial){
19844         if(!initial && this.store){
19845             this.store.un("datachanged", this.refresh);
19846             this.store.un("add", this.onAdd);
19847             this.store.un("remove", this.onRemove);
19848             this.store.un("update", this.onUpdate);
19849             this.store.un("clear", this.refresh);
19850             this.store.un("beforeload", this.onBeforeLoad);
19851             this.store.un("load", this.onLoad);
19852             this.store.un("loadexception", this.onLoad);
19853         }
19854         if(store){
19855           
19856             store.on("datachanged", this.refresh, this);
19857             store.on("add", this.onAdd, this);
19858             store.on("remove", this.onRemove, this);
19859             store.on("update", this.onUpdate, this);
19860             store.on("clear", this.refresh, this);
19861             store.on("beforeload", this.onBeforeLoad, this);
19862             store.on("load", this.onLoad, this);
19863             store.on("loadexception", this.onLoad, this);
19864         }
19865         
19866         if(store){
19867             this.refresh();
19868         }
19869     },
19870     /**
19871      * onbeforeLoad - masks the loading area.
19872      *
19873      */
19874     onBeforeLoad : function(store,opts)
19875     {
19876          //Roo.log('onBeforeLoad');   
19877         if (!opts.add) {
19878             this.el.update("");
19879         }
19880         this.el.mask(this.mask ? this.mask : "Loading" ); 
19881     },
19882     onLoad : function ()
19883     {
19884         this.el.unmask();
19885     },
19886     
19887
19888     /**
19889      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19890      * @param {HTMLElement} node
19891      * @return {HTMLElement} The template node
19892      */
19893     findItemFromChild : function(node){
19894         var el = this.dataName  ?
19895             this.el.child('.roo-tpl-' + this.dataName,true) :
19896             this.el.dom; 
19897         
19898         if(!node || node.parentNode == el){
19899                     return node;
19900             }
19901             var p = node.parentNode;
19902             while(p && p != el){
19903             if(p.parentNode == el){
19904                 return p;
19905             }
19906             p = p.parentNode;
19907         }
19908             return null;
19909     },
19910
19911     /** @ignore */
19912     onClick : function(e){
19913         var item = this.findItemFromChild(e.getTarget());
19914         if(item){
19915             var index = this.indexOf(item);
19916             if(this.onItemClick(item, index, e) !== false){
19917                 this.fireEvent("click", this, index, item, e);
19918             }
19919         }else{
19920             this.clearSelections();
19921         }
19922     },
19923
19924     /** @ignore */
19925     onContextMenu : function(e){
19926         var item = this.findItemFromChild(e.getTarget());
19927         if(item){
19928             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19929         }
19930     },
19931
19932     /** @ignore */
19933     onDblClick : function(e){
19934         var item = this.findItemFromChild(e.getTarget());
19935         if(item){
19936             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19937         }
19938     },
19939
19940     onItemClick : function(item, index, e)
19941     {
19942         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19943             return false;
19944         }
19945         if (this.toggleSelect) {
19946             var m = this.isSelected(item) ? 'unselect' : 'select';
19947             //Roo.log(m);
19948             var _t = this;
19949             _t[m](item, true, false);
19950             return true;
19951         }
19952         if(this.multiSelect || this.singleSelect){
19953             if(this.multiSelect && e.shiftKey && this.lastSelection){
19954                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19955             }else{
19956                 this.select(item, this.multiSelect && e.ctrlKey);
19957                 this.lastSelection = item;
19958             }
19959             
19960             if(!this.tickable){
19961                 e.preventDefault();
19962             }
19963             
19964         }
19965         return true;
19966     },
19967
19968     /**
19969      * Get the number of selected nodes.
19970      * @return {Number}
19971      */
19972     getSelectionCount : function(){
19973         return this.selections.length;
19974     },
19975
19976     /**
19977      * Get the currently selected nodes.
19978      * @return {Array} An array of HTMLElements
19979      */
19980     getSelectedNodes : function(){
19981         return this.selections;
19982     },
19983
19984     /**
19985      * Get the indexes of the selected nodes.
19986      * @return {Array}
19987      */
19988     getSelectedIndexes : function(){
19989         var indexes = [], s = this.selections;
19990         for(var i = 0, len = s.length; i < len; i++){
19991             indexes.push(s[i].nodeIndex);
19992         }
19993         return indexes;
19994     },
19995
19996     /**
19997      * Clear all selections
19998      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19999      */
20000     clearSelections : function(suppressEvent){
20001         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20002             this.cmp.elements = this.selections;
20003             this.cmp.removeClass(this.selectedClass);
20004             this.selections = [];
20005             if(!suppressEvent){
20006                 this.fireEvent("selectionchange", this, this.selections);
20007             }
20008         }
20009     },
20010
20011     /**
20012      * Returns true if the passed node is selected
20013      * @param {HTMLElement/Number} node The node or node index
20014      * @return {Boolean}
20015      */
20016     isSelected : function(node){
20017         var s = this.selections;
20018         if(s.length < 1){
20019             return false;
20020         }
20021         node = this.getNode(node);
20022         return s.indexOf(node) !== -1;
20023     },
20024
20025     /**
20026      * Selects nodes.
20027      * @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
20028      * @param {Boolean} keepExisting (optional) true to keep existing selections
20029      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20030      */
20031     select : function(nodeInfo, keepExisting, suppressEvent){
20032         if(nodeInfo instanceof Array){
20033             if(!keepExisting){
20034                 this.clearSelections(true);
20035             }
20036             for(var i = 0, len = nodeInfo.length; i < len; i++){
20037                 this.select(nodeInfo[i], true, true);
20038             }
20039             return;
20040         } 
20041         var node = this.getNode(nodeInfo);
20042         if(!node || this.isSelected(node)){
20043             return; // already selected.
20044         }
20045         if(!keepExisting){
20046             this.clearSelections(true);
20047         }
20048         
20049         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20050             Roo.fly(node).addClass(this.selectedClass);
20051             this.selections.push(node);
20052             if(!suppressEvent){
20053                 this.fireEvent("selectionchange", this, this.selections);
20054             }
20055         }
20056         
20057         
20058     },
20059       /**
20060      * Unselects nodes.
20061      * @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
20062      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20063      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20064      */
20065     unselect : function(nodeInfo, keepExisting, suppressEvent)
20066     {
20067         if(nodeInfo instanceof Array){
20068             Roo.each(this.selections, function(s) {
20069                 this.unselect(s, nodeInfo);
20070             }, this);
20071             return;
20072         }
20073         var node = this.getNode(nodeInfo);
20074         if(!node || !this.isSelected(node)){
20075             //Roo.log("not selected");
20076             return; // not selected.
20077         }
20078         // fireevent???
20079         var ns = [];
20080         Roo.each(this.selections, function(s) {
20081             if (s == node ) {
20082                 Roo.fly(node).removeClass(this.selectedClass);
20083
20084                 return;
20085             }
20086             ns.push(s);
20087         },this);
20088         
20089         this.selections= ns;
20090         this.fireEvent("selectionchange", this, this.selections);
20091     },
20092
20093     /**
20094      * Gets a template node.
20095      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20096      * @return {HTMLElement} The node or null if it wasn't found
20097      */
20098     getNode : function(nodeInfo){
20099         if(typeof nodeInfo == "string"){
20100             return document.getElementById(nodeInfo);
20101         }else if(typeof nodeInfo == "number"){
20102             return this.nodes[nodeInfo];
20103         }
20104         return nodeInfo;
20105     },
20106
20107     /**
20108      * Gets a range template nodes.
20109      * @param {Number} startIndex
20110      * @param {Number} endIndex
20111      * @return {Array} An array of nodes
20112      */
20113     getNodes : function(start, end){
20114         var ns = this.nodes;
20115         start = start || 0;
20116         end = typeof end == "undefined" ? ns.length - 1 : end;
20117         var nodes = [];
20118         if(start <= end){
20119             for(var i = start; i <= end; i++){
20120                 nodes.push(ns[i]);
20121             }
20122         } else{
20123             for(var i = start; i >= end; i--){
20124                 nodes.push(ns[i]);
20125             }
20126         }
20127         return nodes;
20128     },
20129
20130     /**
20131      * Finds the index of the passed node
20132      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20133      * @return {Number} The index of the node or -1
20134      */
20135     indexOf : function(node){
20136         node = this.getNode(node);
20137         if(typeof node.nodeIndex == "number"){
20138             return node.nodeIndex;
20139         }
20140         var ns = this.nodes;
20141         for(var i = 0, len = ns.length; i < len; i++){
20142             if(ns[i] == node){
20143                 return i;
20144             }
20145         }
20146         return -1;
20147     }
20148 });
20149 /*
20150  * - LGPL
20151  *
20152  * based on jquery fullcalendar
20153  * 
20154  */
20155
20156 Roo.bootstrap = Roo.bootstrap || {};
20157 /**
20158  * @class Roo.bootstrap.Calendar
20159  * @extends Roo.bootstrap.Component
20160  * Bootstrap Calendar class
20161  * @cfg {Boolean} loadMask (true|false) default false
20162  * @cfg {Object} header generate the user specific header of the calendar, default false
20163
20164  * @constructor
20165  * Create a new Container
20166  * @param {Object} config The config object
20167  */
20168
20169
20170
20171 Roo.bootstrap.Calendar = function(config){
20172     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20173      this.addEvents({
20174         /**
20175              * @event select
20176              * Fires when a date is selected
20177              * @param {DatePicker} this
20178              * @param {Date} date The selected date
20179              */
20180         'select': true,
20181         /**
20182              * @event monthchange
20183              * Fires when the displayed month changes 
20184              * @param {DatePicker} this
20185              * @param {Date} date The selected month
20186              */
20187         'monthchange': true,
20188         /**
20189              * @event evententer
20190              * Fires when mouse over an event
20191              * @param {Calendar} this
20192              * @param {event} Event
20193              */
20194         'evententer': true,
20195         /**
20196              * @event eventleave
20197              * Fires when the mouse leaves an
20198              * @param {Calendar} this
20199              * @param {event}
20200              */
20201         'eventleave': true,
20202         /**
20203              * @event eventclick
20204              * Fires when the mouse click an
20205              * @param {Calendar} this
20206              * @param {event}
20207              */
20208         'eventclick': true
20209         
20210     });
20211
20212 };
20213
20214 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20215     
20216           /**
20217      * @cfg {Roo.data.Store} store
20218      * The data source for the calendar
20219      */
20220         store : false,
20221      /**
20222      * @cfg {Number} startDay
20223      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20224      */
20225     startDay : 0,
20226     
20227     loadMask : false,
20228     
20229     header : false,
20230       
20231     getAutoCreate : function(){
20232         
20233         
20234         var fc_button = function(name, corner, style, content ) {
20235             return Roo.apply({},{
20236                 tag : 'span',
20237                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20238                          (corner.length ?
20239                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20240                             ''
20241                         ),
20242                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20243                 unselectable: 'on'
20244             });
20245         };
20246         
20247         var header = {};
20248         
20249         if(!this.header){
20250             header = {
20251                 tag : 'table',
20252                 cls : 'fc-header',
20253                 style : 'width:100%',
20254                 cn : [
20255                     {
20256                         tag: 'tr',
20257                         cn : [
20258                             {
20259                                 tag : 'td',
20260                                 cls : 'fc-header-left',
20261                                 cn : [
20262                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20263                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20264                                     { tag: 'span', cls: 'fc-header-space' },
20265                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20266
20267
20268                                 ]
20269                             },
20270
20271                             {
20272                                 tag : 'td',
20273                                 cls : 'fc-header-center',
20274                                 cn : [
20275                                     {
20276                                         tag: 'span',
20277                                         cls: 'fc-header-title',
20278                                         cn : {
20279                                             tag: 'H2',
20280                                             html : 'month / year'
20281                                         }
20282                                     }
20283
20284                                 ]
20285                             },
20286                             {
20287                                 tag : 'td',
20288                                 cls : 'fc-header-right',
20289                                 cn : [
20290                               /*      fc_button('month', 'left', '', 'month' ),
20291                                     fc_button('week', '', '', 'week' ),
20292                                     fc_button('day', 'right', '', 'day' )
20293                                 */    
20294
20295                                 ]
20296                             }
20297
20298                         ]
20299                     }
20300                 ]
20301             };
20302         }
20303         
20304         header = this.header;
20305         
20306        
20307         var cal_heads = function() {
20308             var ret = [];
20309             // fixme - handle this.
20310             
20311             for (var i =0; i < Date.dayNames.length; i++) {
20312                 var d = Date.dayNames[i];
20313                 ret.push({
20314                     tag: 'th',
20315                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20316                     html : d.substring(0,3)
20317                 });
20318                 
20319             }
20320             ret[0].cls += ' fc-first';
20321             ret[6].cls += ' fc-last';
20322             return ret;
20323         };
20324         var cal_cell = function(n) {
20325             return  {
20326                 tag: 'td',
20327                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20328                 cn : [
20329                     {
20330                         cn : [
20331                             {
20332                                 cls: 'fc-day-number',
20333                                 html: 'D'
20334                             },
20335                             {
20336                                 cls: 'fc-day-content',
20337                              
20338                                 cn : [
20339                                      {
20340                                         style: 'position: relative;' // height: 17px;
20341                                     }
20342                                 ]
20343                             }
20344                             
20345                             
20346                         ]
20347                     }
20348                 ]
20349                 
20350             }
20351         };
20352         var cal_rows = function() {
20353             
20354             var ret = [];
20355             for (var r = 0; r < 6; r++) {
20356                 var row= {
20357                     tag : 'tr',
20358                     cls : 'fc-week',
20359                     cn : []
20360                 };
20361                 
20362                 for (var i =0; i < Date.dayNames.length; i++) {
20363                     var d = Date.dayNames[i];
20364                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20365
20366                 }
20367                 row.cn[0].cls+=' fc-first';
20368                 row.cn[0].cn[0].style = 'min-height:90px';
20369                 row.cn[6].cls+=' fc-last';
20370                 ret.push(row);
20371                 
20372             }
20373             ret[0].cls += ' fc-first';
20374             ret[4].cls += ' fc-prev-last';
20375             ret[5].cls += ' fc-last';
20376             return ret;
20377             
20378         };
20379         
20380         var cal_table = {
20381             tag: 'table',
20382             cls: 'fc-border-separate',
20383             style : 'width:100%',
20384             cellspacing  : 0,
20385             cn : [
20386                 { 
20387                     tag: 'thead',
20388                     cn : [
20389                         { 
20390                             tag: 'tr',
20391                             cls : 'fc-first fc-last',
20392                             cn : cal_heads()
20393                         }
20394                     ]
20395                 },
20396                 { 
20397                     tag: 'tbody',
20398                     cn : cal_rows()
20399                 }
20400                   
20401             ]
20402         };
20403          
20404          var cfg = {
20405             cls : 'fc fc-ltr',
20406             cn : [
20407                 header,
20408                 {
20409                     cls : 'fc-content',
20410                     style : "position: relative;",
20411                     cn : [
20412                         {
20413                             cls : 'fc-view fc-view-month fc-grid',
20414                             style : 'position: relative',
20415                             unselectable : 'on',
20416                             cn : [
20417                                 {
20418                                     cls : 'fc-event-container',
20419                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20420                                 },
20421                                 cal_table
20422                             ]
20423                         }
20424                     ]
20425     
20426                 }
20427            ] 
20428             
20429         };
20430         
20431          
20432         
20433         return cfg;
20434     },
20435     
20436     
20437     initEvents : function()
20438     {
20439         if(!this.store){
20440             throw "can not find store for calendar";
20441         }
20442         
20443         var mark = {
20444             tag: "div",
20445             cls:"x-dlg-mask",
20446             style: "text-align:center",
20447             cn: [
20448                 {
20449                     tag: "div",
20450                     style: "background-color:white;width:50%;margin:250 auto",
20451                     cn: [
20452                         {
20453                             tag: "img",
20454                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20455                         },
20456                         {
20457                             tag: "span",
20458                             html: "Loading"
20459                         }
20460                         
20461                     ]
20462                 }
20463             ]
20464         };
20465         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20466         
20467         var size = this.el.select('.fc-content', true).first().getSize();
20468         this.maskEl.setSize(size.width, size.height);
20469         this.maskEl.enableDisplayMode("block");
20470         if(!this.loadMask){
20471             this.maskEl.hide();
20472         }
20473         
20474         this.store = Roo.factory(this.store, Roo.data);
20475         this.store.on('load', this.onLoad, this);
20476         this.store.on('beforeload', this.onBeforeLoad, this);
20477         
20478         this.resize();
20479         
20480         this.cells = this.el.select('.fc-day',true);
20481         //Roo.log(this.cells);
20482         this.textNodes = this.el.query('.fc-day-number');
20483         this.cells.addClassOnOver('fc-state-hover');
20484         
20485         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20486         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20487         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20488         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20489         
20490         this.on('monthchange', this.onMonthChange, this);
20491         
20492         this.update(new Date().clearTime());
20493     },
20494     
20495     resize : function() {
20496         var sz  = this.el.getSize();
20497         
20498         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20499         this.el.select('.fc-day-content div',true).setHeight(34);
20500     },
20501     
20502     
20503     // private
20504     showPrevMonth : function(e){
20505         this.update(this.activeDate.add("mo", -1));
20506     },
20507     showToday : function(e){
20508         this.update(new Date().clearTime());
20509     },
20510     // private
20511     showNextMonth : function(e){
20512         this.update(this.activeDate.add("mo", 1));
20513     },
20514
20515     // private
20516     showPrevYear : function(){
20517         this.update(this.activeDate.add("y", -1));
20518     },
20519
20520     // private
20521     showNextYear : function(){
20522         this.update(this.activeDate.add("y", 1));
20523     },
20524
20525     
20526    // private
20527     update : function(date)
20528     {
20529         var vd = this.activeDate;
20530         this.activeDate = date;
20531 //        if(vd && this.el){
20532 //            var t = date.getTime();
20533 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20534 //                Roo.log('using add remove');
20535 //                
20536 //                this.fireEvent('monthchange', this, date);
20537 //                
20538 //                this.cells.removeClass("fc-state-highlight");
20539 //                this.cells.each(function(c){
20540 //                   if(c.dateValue == t){
20541 //                       c.addClass("fc-state-highlight");
20542 //                       setTimeout(function(){
20543 //                            try{c.dom.firstChild.focus();}catch(e){}
20544 //                       }, 50);
20545 //                       return false;
20546 //                   }
20547 //                   return true;
20548 //                });
20549 //                return;
20550 //            }
20551 //        }
20552         
20553         var days = date.getDaysInMonth();
20554         
20555         var firstOfMonth = date.getFirstDateOfMonth();
20556         var startingPos = firstOfMonth.getDay()-this.startDay;
20557         
20558         if(startingPos < this.startDay){
20559             startingPos += 7;
20560         }
20561         
20562         var pm = date.add(Date.MONTH, -1);
20563         var prevStart = pm.getDaysInMonth()-startingPos;
20564 //        
20565         this.cells = this.el.select('.fc-day',true);
20566         this.textNodes = this.el.query('.fc-day-number');
20567         this.cells.addClassOnOver('fc-state-hover');
20568         
20569         var cells = this.cells.elements;
20570         var textEls = this.textNodes;
20571         
20572         Roo.each(cells, function(cell){
20573             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20574         });
20575         
20576         days += startingPos;
20577
20578         // convert everything to numbers so it's fast
20579         var day = 86400000;
20580         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20581         //Roo.log(d);
20582         //Roo.log(pm);
20583         //Roo.log(prevStart);
20584         
20585         var today = new Date().clearTime().getTime();
20586         var sel = date.clearTime().getTime();
20587         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20588         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20589         var ddMatch = this.disabledDatesRE;
20590         var ddText = this.disabledDatesText;
20591         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20592         var ddaysText = this.disabledDaysText;
20593         var format = this.format;
20594         
20595         var setCellClass = function(cal, cell){
20596             cell.row = 0;
20597             cell.events = [];
20598             cell.more = [];
20599             //Roo.log('set Cell Class');
20600             cell.title = "";
20601             var t = d.getTime();
20602             
20603             //Roo.log(d);
20604             
20605             cell.dateValue = t;
20606             if(t == today){
20607                 cell.className += " fc-today";
20608                 cell.className += " fc-state-highlight";
20609                 cell.title = cal.todayText;
20610             }
20611             if(t == sel){
20612                 // disable highlight in other month..
20613                 //cell.className += " fc-state-highlight";
20614                 
20615             }
20616             // disabling
20617             if(t < min) {
20618                 cell.className = " fc-state-disabled";
20619                 cell.title = cal.minText;
20620                 return;
20621             }
20622             if(t > max) {
20623                 cell.className = " fc-state-disabled";
20624                 cell.title = cal.maxText;
20625                 return;
20626             }
20627             if(ddays){
20628                 if(ddays.indexOf(d.getDay()) != -1){
20629                     cell.title = ddaysText;
20630                     cell.className = " fc-state-disabled";
20631                 }
20632             }
20633             if(ddMatch && format){
20634                 var fvalue = d.dateFormat(format);
20635                 if(ddMatch.test(fvalue)){
20636                     cell.title = ddText.replace("%0", fvalue);
20637                     cell.className = " fc-state-disabled";
20638                 }
20639             }
20640             
20641             if (!cell.initialClassName) {
20642                 cell.initialClassName = cell.dom.className;
20643             }
20644             
20645             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20646         };
20647
20648         var i = 0;
20649         
20650         for(; i < startingPos; i++) {
20651             textEls[i].innerHTML = (++prevStart);
20652             d.setDate(d.getDate()+1);
20653             
20654             cells[i].className = "fc-past fc-other-month";
20655             setCellClass(this, cells[i]);
20656         }
20657         
20658         var intDay = 0;
20659         
20660         for(; i < days; i++){
20661             intDay = i - startingPos + 1;
20662             textEls[i].innerHTML = (intDay);
20663             d.setDate(d.getDate()+1);
20664             
20665             cells[i].className = ''; // "x-date-active";
20666             setCellClass(this, cells[i]);
20667         }
20668         var extraDays = 0;
20669         
20670         for(; i < 42; i++) {
20671             textEls[i].innerHTML = (++extraDays);
20672             d.setDate(d.getDate()+1);
20673             
20674             cells[i].className = "fc-future fc-other-month";
20675             setCellClass(this, cells[i]);
20676         }
20677         
20678         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20679         
20680         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20681         
20682         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20683         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20684         
20685         if(totalRows != 6){
20686             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20687             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20688         }
20689         
20690         this.fireEvent('monthchange', this, date);
20691         
20692         
20693         /*
20694         if(!this.internalRender){
20695             var main = this.el.dom.firstChild;
20696             var w = main.offsetWidth;
20697             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20698             Roo.fly(main).setWidth(w);
20699             this.internalRender = true;
20700             // opera does not respect the auto grow header center column
20701             // then, after it gets a width opera refuses to recalculate
20702             // without a second pass
20703             if(Roo.isOpera && !this.secondPass){
20704                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20705                 this.secondPass = true;
20706                 this.update.defer(10, this, [date]);
20707             }
20708         }
20709         */
20710         
20711     },
20712     
20713     findCell : function(dt) {
20714         dt = dt.clearTime().getTime();
20715         var ret = false;
20716         this.cells.each(function(c){
20717             //Roo.log("check " +c.dateValue + '?=' + dt);
20718             if(c.dateValue == dt){
20719                 ret = c;
20720                 return false;
20721             }
20722             return true;
20723         });
20724         
20725         return ret;
20726     },
20727     
20728     findCells : function(ev) {
20729         var s = ev.start.clone().clearTime().getTime();
20730        // Roo.log(s);
20731         var e= ev.end.clone().clearTime().getTime();
20732        // Roo.log(e);
20733         var ret = [];
20734         this.cells.each(function(c){
20735              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20736             
20737             if(c.dateValue > e){
20738                 return ;
20739             }
20740             if(c.dateValue < s){
20741                 return ;
20742             }
20743             ret.push(c);
20744         });
20745         
20746         return ret;    
20747     },
20748     
20749 //    findBestRow: function(cells)
20750 //    {
20751 //        var ret = 0;
20752 //        
20753 //        for (var i =0 ; i < cells.length;i++) {
20754 //            ret  = Math.max(cells[i].rows || 0,ret);
20755 //        }
20756 //        return ret;
20757 //        
20758 //    },
20759     
20760     
20761     addItem : function(ev)
20762     {
20763         // look for vertical location slot in
20764         var cells = this.findCells(ev);
20765         
20766 //        ev.row = this.findBestRow(cells);
20767         
20768         // work out the location.
20769         
20770         var crow = false;
20771         var rows = [];
20772         for(var i =0; i < cells.length; i++) {
20773             
20774             cells[i].row = cells[0].row;
20775             
20776             if(i == 0){
20777                 cells[i].row = cells[i].row + 1;
20778             }
20779             
20780             if (!crow) {
20781                 crow = {
20782                     start : cells[i],
20783                     end :  cells[i]
20784                 };
20785                 continue;
20786             }
20787             if (crow.start.getY() == cells[i].getY()) {
20788                 // on same row.
20789                 crow.end = cells[i];
20790                 continue;
20791             }
20792             // different row.
20793             rows.push(crow);
20794             crow = {
20795                 start: cells[i],
20796                 end : cells[i]
20797             };
20798             
20799         }
20800         
20801         rows.push(crow);
20802         ev.els = [];
20803         ev.rows = rows;
20804         ev.cells = cells;
20805         
20806         cells[0].events.push(ev);
20807         
20808         this.calevents.push(ev);
20809     },
20810     
20811     clearEvents: function() {
20812         
20813         if(!this.calevents){
20814             return;
20815         }
20816         
20817         Roo.each(this.cells.elements, function(c){
20818             c.row = 0;
20819             c.events = [];
20820             c.more = [];
20821         });
20822         
20823         Roo.each(this.calevents, function(e) {
20824             Roo.each(e.els, function(el) {
20825                 el.un('mouseenter' ,this.onEventEnter, this);
20826                 el.un('mouseleave' ,this.onEventLeave, this);
20827                 el.remove();
20828             },this);
20829         },this);
20830         
20831         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20832             e.remove();
20833         });
20834         
20835     },
20836     
20837     renderEvents: function()
20838     {   
20839         var _this = this;
20840         
20841         this.cells.each(function(c) {
20842             
20843             if(c.row < 5){
20844                 return;
20845             }
20846             
20847             var ev = c.events;
20848             
20849             var r = 4;
20850             if(c.row != c.events.length){
20851                 r = 4 - (4 - (c.row - c.events.length));
20852             }
20853             
20854             c.events = ev.slice(0, r);
20855             c.more = ev.slice(r);
20856             
20857             if(c.more.length && c.more.length == 1){
20858                 c.events.push(c.more.pop());
20859             }
20860             
20861             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20862             
20863         });
20864             
20865         this.cells.each(function(c) {
20866             
20867             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20868             
20869             
20870             for (var e = 0; e < c.events.length; e++){
20871                 var ev = c.events[e];
20872                 var rows = ev.rows;
20873                 
20874                 for(var i = 0; i < rows.length; i++) {
20875                 
20876                     // how many rows should it span..
20877
20878                     var  cfg = {
20879                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20880                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20881
20882                         unselectable : "on",
20883                         cn : [
20884                             {
20885                                 cls: 'fc-event-inner',
20886                                 cn : [
20887     //                                {
20888     //                                  tag:'span',
20889     //                                  cls: 'fc-event-time',
20890     //                                  html : cells.length > 1 ? '' : ev.time
20891     //                                },
20892                                     {
20893                                       tag:'span',
20894                                       cls: 'fc-event-title',
20895                                       html : String.format('{0}', ev.title)
20896                                     }
20897
20898
20899                                 ]
20900                             },
20901                             {
20902                                 cls: 'ui-resizable-handle ui-resizable-e',
20903                                 html : '&nbsp;&nbsp;&nbsp'
20904                             }
20905
20906                         ]
20907                     };
20908
20909                     if (i == 0) {
20910                         cfg.cls += ' fc-event-start';
20911                     }
20912                     if ((i+1) == rows.length) {
20913                         cfg.cls += ' fc-event-end';
20914                     }
20915
20916                     var ctr = _this.el.select('.fc-event-container',true).first();
20917                     var cg = ctr.createChild(cfg);
20918
20919                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20920                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20921
20922                     var r = (c.more.length) ? 1 : 0;
20923                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20924                     cg.setWidth(ebox.right - sbox.x -2);
20925
20926                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20927                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20928                     cg.on('click', _this.onEventClick, _this, ev);
20929
20930                     ev.els.push(cg);
20931                     
20932                 }
20933                 
20934             }
20935             
20936             
20937             if(c.more.length){
20938                 var  cfg = {
20939                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20940                     style : 'position: absolute',
20941                     unselectable : "on",
20942                     cn : [
20943                         {
20944                             cls: 'fc-event-inner',
20945                             cn : [
20946                                 {
20947                                   tag:'span',
20948                                   cls: 'fc-event-title',
20949                                   html : 'More'
20950                                 }
20951
20952
20953                             ]
20954                         },
20955                         {
20956                             cls: 'ui-resizable-handle ui-resizable-e',
20957                             html : '&nbsp;&nbsp;&nbsp'
20958                         }
20959
20960                     ]
20961                 };
20962
20963                 var ctr = _this.el.select('.fc-event-container',true).first();
20964                 var cg = ctr.createChild(cfg);
20965
20966                 var sbox = c.select('.fc-day-content',true).first().getBox();
20967                 var ebox = c.select('.fc-day-content',true).first().getBox();
20968                 //Roo.log(cg);
20969                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20970                 cg.setWidth(ebox.right - sbox.x -2);
20971
20972                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20973                 
20974             }
20975             
20976         });
20977         
20978         
20979         
20980     },
20981     
20982     onEventEnter: function (e, el,event,d) {
20983         this.fireEvent('evententer', this, el, event);
20984     },
20985     
20986     onEventLeave: function (e, el,event,d) {
20987         this.fireEvent('eventleave', this, el, event);
20988     },
20989     
20990     onEventClick: function (e, el,event,d) {
20991         this.fireEvent('eventclick', this, el, event);
20992     },
20993     
20994     onMonthChange: function () {
20995         this.store.load();
20996     },
20997     
20998     onMoreEventClick: function(e, el, more)
20999     {
21000         var _this = this;
21001         
21002         this.calpopover.placement = 'right';
21003         this.calpopover.setTitle('More');
21004         
21005         this.calpopover.setContent('');
21006         
21007         var ctr = this.calpopover.el.select('.popover-content', true).first();
21008         
21009         Roo.each(more, function(m){
21010             var cfg = {
21011                 cls : 'fc-event-hori fc-event-draggable',
21012                 html : m.title
21013             };
21014             var cg = ctr.createChild(cfg);
21015             
21016             cg.on('click', _this.onEventClick, _this, m);
21017         });
21018         
21019         this.calpopover.show(el);
21020         
21021         
21022     },
21023     
21024     onLoad: function () 
21025     {   
21026         this.calevents = [];
21027         var cal = this;
21028         
21029         if(this.store.getCount() > 0){
21030             this.store.data.each(function(d){
21031                cal.addItem({
21032                     id : d.data.id,
21033                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21034                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21035                     time : d.data.start_time,
21036                     title : d.data.title,
21037                     description : d.data.description,
21038                     venue : d.data.venue
21039                 });
21040             });
21041         }
21042         
21043         this.renderEvents();
21044         
21045         if(this.calevents.length && this.loadMask){
21046             this.maskEl.hide();
21047         }
21048     },
21049     
21050     onBeforeLoad: function()
21051     {
21052         this.clearEvents();
21053         if(this.loadMask){
21054             this.maskEl.show();
21055         }
21056     }
21057 });
21058
21059  
21060  /*
21061  * - LGPL
21062  *
21063  * element
21064  * 
21065  */
21066
21067 /**
21068  * @class Roo.bootstrap.Popover
21069  * @extends Roo.bootstrap.Component
21070  * @builder-top
21071  * Bootstrap Popover class
21072  * @cfg {String} html contents of the popover   (or false to use children..)
21073  * @cfg {String} title of popover (or false to hide)
21074  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21075  * @cfg {String} trigger click || hover (or false to trigger manually)
21076  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21077  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21078  *      - if false and it has a 'parent' then it will be automatically added to that element
21079  *      - if string - Roo.get  will be called 
21080  * @cfg {Number} delay - delay before showing
21081  
21082  * @constructor
21083  * Create a new Popover
21084  * @param {Object} config The config object
21085  */
21086
21087 Roo.bootstrap.Popover = function(config){
21088     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21089     
21090     this.addEvents({
21091         // raw events
21092          /**
21093          * @event show
21094          * After the popover show
21095          * 
21096          * @param {Roo.bootstrap.Popover} this
21097          */
21098         "show" : true,
21099         /**
21100          * @event hide
21101          * After the popover hide
21102          * 
21103          * @param {Roo.bootstrap.Popover} this
21104          */
21105         "hide" : true
21106     });
21107 };
21108
21109 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21110     
21111     title: false,
21112     html: false,
21113     
21114     placement : 'right',
21115     trigger : 'hover', // hover
21116     modal : false,
21117     delay : 0,
21118     
21119     over: false,
21120     
21121     can_build_overlaid : false,
21122     
21123     maskEl : false, // the mask element
21124     headerEl : false,
21125     contentEl : false,
21126     alignEl : false, // when show is called with an element - this get's stored.
21127     
21128     getChildContainer : function()
21129     {
21130         return this.contentEl;
21131         
21132     },
21133     getPopoverHeader : function()
21134     {
21135         this.title = true; // flag not to hide it..
21136         this.headerEl.addClass('p-0');
21137         return this.headerEl
21138     },
21139     
21140     
21141     getAutoCreate : function(){
21142          
21143         var cfg = {
21144            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21145            style: 'display:block',
21146            cn : [
21147                 {
21148                     cls : 'arrow'
21149                 },
21150                 {
21151                     cls : 'popover-inner ',
21152                     cn : [
21153                         {
21154                             tag: 'h3',
21155                             cls: 'popover-title popover-header',
21156                             html : this.title === false ? '' : this.title
21157                         },
21158                         {
21159                             cls : 'popover-content popover-body '  + (this.cls || ''),
21160                             html : this.html || ''
21161                         }
21162                     ]
21163                     
21164                 }
21165            ]
21166         };
21167         
21168         return cfg;
21169     },
21170     /**
21171      * @param {string} the title
21172      */
21173     setTitle: function(str)
21174     {
21175         this.title = str;
21176         if (this.el) {
21177             this.headerEl.dom.innerHTML = str;
21178         }
21179         
21180     },
21181     /**
21182      * @param {string} the body content
21183      */
21184     setContent: function(str)
21185     {
21186         this.html = str;
21187         if (this.contentEl) {
21188             this.contentEl.dom.innerHTML = str;
21189         }
21190         
21191     },
21192     // as it get's added to the bottom of the page.
21193     onRender : function(ct, position)
21194     {
21195         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21196         
21197         
21198         
21199         if(!this.el){
21200             var cfg = Roo.apply({},  this.getAutoCreate());
21201             cfg.id = Roo.id();
21202             
21203             if (this.cls) {
21204                 cfg.cls += ' ' + this.cls;
21205             }
21206             if (this.style) {
21207                 cfg.style = this.style;
21208             }
21209             //Roo.log("adding to ");
21210             this.el = Roo.get(document.body).createChild(cfg, position);
21211 //            Roo.log(this.el);
21212         }
21213         
21214         this.contentEl = this.el.select('.popover-content',true).first();
21215         this.headerEl =  this.el.select('.popover-title',true).first();
21216         
21217         var nitems = [];
21218         if(typeof(this.items) != 'undefined'){
21219             var items = this.items;
21220             delete this.items;
21221
21222             for(var i =0;i < items.length;i++) {
21223                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21224             }
21225         }
21226
21227         this.items = nitems;
21228         
21229         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21230         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21231         
21232         
21233         
21234         this.initEvents();
21235     },
21236     
21237     resizeMask : function()
21238     {
21239         this.maskEl.setSize(
21240             Roo.lib.Dom.getViewWidth(true),
21241             Roo.lib.Dom.getViewHeight(true)
21242         );
21243     },
21244     
21245     initEvents : function()
21246     {
21247         
21248         if (!this.modal) { 
21249             Roo.bootstrap.Popover.register(this);
21250         }
21251          
21252         this.arrowEl = this.el.select('.arrow',true).first();
21253         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21254         this.el.enableDisplayMode('block');
21255         this.el.hide();
21256  
21257         
21258         if (this.over === false && !this.parent()) {
21259             return; 
21260         }
21261         if (this.triggers === false) {
21262             return;
21263         }
21264          
21265         // support parent
21266         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21267         var triggers = this.trigger ? this.trigger.split(' ') : [];
21268         Roo.each(triggers, function(trigger) {
21269         
21270             if (trigger == 'click') {
21271                 on_el.on('click', this.toggle, this);
21272             } else if (trigger != 'manual') {
21273                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21274                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21275       
21276                 on_el.on(eventIn  ,this.enter, this);
21277                 on_el.on(eventOut, this.leave, this);
21278             }
21279         }, this);
21280     },
21281     
21282     
21283     // private
21284     timeout : null,
21285     hoverState : null,
21286     
21287     toggle : function () {
21288         this.hoverState == 'in' ? this.leave() : this.enter();
21289     },
21290     
21291     enter : function () {
21292         
21293         clearTimeout(this.timeout);
21294     
21295         this.hoverState = 'in';
21296     
21297         if (!this.delay || !this.delay.show) {
21298             this.show();
21299             return;
21300         }
21301         var _t = this;
21302         this.timeout = setTimeout(function () {
21303             if (_t.hoverState == 'in') {
21304                 _t.show();
21305             }
21306         }, this.delay.show)
21307     },
21308     
21309     leave : function() {
21310         clearTimeout(this.timeout);
21311     
21312         this.hoverState = 'out';
21313     
21314         if (!this.delay || !this.delay.hide) {
21315             this.hide();
21316             return;
21317         }
21318         var _t = this;
21319         this.timeout = setTimeout(function () {
21320             if (_t.hoverState == 'out') {
21321                 _t.hide();
21322             }
21323         }, this.delay.hide)
21324     },
21325     /**
21326      * Show the popover
21327      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21328      * @param {string} (left|right|top|bottom) position
21329      */
21330     show : function (on_el, placement)
21331     {
21332         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21333         on_el = on_el || false; // default to false
21334          
21335         if (!on_el) {
21336             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21337                 on_el = this.parent().el;
21338             } else if (this.over) {
21339                 on_el = Roo.get(this.over);
21340             }
21341             
21342         }
21343         
21344         this.alignEl = Roo.get( on_el );
21345
21346         if (!this.el) {
21347             this.render(document.body);
21348         }
21349         
21350         
21351          
21352         
21353         if (this.title === false) {
21354             this.headerEl.hide();
21355         }
21356         
21357        
21358         this.el.show();
21359         this.el.dom.style.display = 'block';
21360          
21361  
21362         if (this.alignEl) {
21363             this.updatePosition(this.placement, true);
21364              
21365         } else {
21366             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21367             var es = this.el.getSize();
21368             var x = Roo.lib.Dom.getViewWidth()/2;
21369             var y = Roo.lib.Dom.getViewHeight()/2;
21370             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21371             
21372         }
21373
21374         
21375         //var arrow = this.el.select('.arrow',true).first();
21376         //arrow.set(align[2], 
21377         
21378         this.el.addClass('in');
21379         
21380          
21381         
21382         this.hoverState = 'in';
21383         
21384         if (this.modal) {
21385             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21386             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21387             this.maskEl.dom.style.display = 'block';
21388             this.maskEl.addClass('show');
21389         }
21390         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21391  
21392         this.fireEvent('show', this);
21393         
21394     },
21395     /**
21396      * fire this manually after loading a grid in the table for example
21397      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21398      * @param {Boolean} try and move it if we cant get right position.
21399      */
21400     updatePosition : function(placement, try_move)
21401     {
21402         // allow for calling with no parameters
21403         placement = placement   ? placement :  this.placement;
21404         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21405         
21406         this.el.removeClass([
21407             'fade','top','bottom', 'left', 'right','in',
21408             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21409         ]);
21410         this.el.addClass(placement + ' bs-popover-' + placement);
21411         
21412         if (!this.alignEl ) {
21413             return false;
21414         }
21415         
21416         switch (placement) {
21417             case 'right':
21418                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21419                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21420                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21421                     //normal display... or moved up/down.
21422                     this.el.setXY(offset);
21423                     var xy = this.alignEl.getAnchorXY('tr', false);
21424                     xy[0]+=2;xy[1]+=5;
21425                     this.arrowEl.setXY(xy);
21426                     return true;
21427                 }
21428                 // continue through...
21429                 return this.updatePosition('left', false);
21430                 
21431             
21432             case 'left':
21433                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21434                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21435                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21436                     //normal display... or moved up/down.
21437                     this.el.setXY(offset);
21438                     var xy = this.alignEl.getAnchorXY('tl', false);
21439                     xy[0]-=10;xy[1]+=5; // << fix me
21440                     this.arrowEl.setXY(xy);
21441                     return true;
21442                 }
21443                 // call self...
21444                 return this.updatePosition('right', false);
21445             
21446             case 'top':
21447                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21448                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21449                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21450                     //normal display... or moved up/down.
21451                     this.el.setXY(offset);
21452                     var xy = this.alignEl.getAnchorXY('t', false);
21453                     xy[1]-=10; // << fix me
21454                     this.arrowEl.setXY(xy);
21455                     return true;
21456                 }
21457                 // fall through
21458                return this.updatePosition('bottom', false);
21459             
21460             case 'bottom':
21461                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21462                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21463                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21464                     //normal display... or moved up/down.
21465                     this.el.setXY(offset);
21466                     var xy = this.alignEl.getAnchorXY('b', false);
21467                      xy[1]+=2; // << fix me
21468                     this.arrowEl.setXY(xy);
21469                     return true;
21470                 }
21471                 // fall through
21472                 return this.updatePosition('top', false);
21473                 
21474             
21475         }
21476         
21477         
21478         return false;
21479     },
21480     
21481     hide : function()
21482     {
21483         this.el.setXY([0,0]);
21484         this.el.removeClass('in');
21485         this.el.hide();
21486         this.hoverState = null;
21487         this.maskEl.hide(); // always..
21488         this.fireEvent('hide', this);
21489     }
21490     
21491 });
21492
21493
21494 Roo.apply(Roo.bootstrap.Popover, {
21495
21496     alignment : {
21497         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21498         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21499         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21500         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21501     },
21502     
21503     zIndex : 20001,
21504
21505     clickHander : false,
21506     
21507     
21508
21509     onMouseDown : function(e)
21510     {
21511         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21512             /// what is nothing is showing..
21513             this.hideAll();
21514         }
21515          
21516     },
21517     
21518     
21519     popups : [],
21520     
21521     register : function(popup)
21522     {
21523         if (!Roo.bootstrap.Popover.clickHandler) {
21524             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21525         }
21526         // hide other popups.
21527         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21528         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21529         this.hideAll(); //<< why?
21530         //this.popups.push(popup);
21531     },
21532     hideAll : function()
21533     {
21534         this.popups.forEach(function(p) {
21535             p.hide();
21536         });
21537     },
21538     onShow : function() {
21539         Roo.bootstrap.Popover.popups.push(this);
21540     },
21541     onHide : function() {
21542         Roo.bootstrap.Popover.popups.remove(this);
21543     } 
21544
21545 });/*
21546  * - LGPL
21547  *
21548  * Card header - holder for the card header elements.
21549  * 
21550  */
21551
21552 /**
21553  * @class Roo.bootstrap.PopoverNav
21554  * @extends Roo.bootstrap.NavGroup
21555  * Bootstrap Popover header navigation class
21556  * @constructor
21557  * Create a new Popover Header Navigation 
21558  * @param {Object} config The config object
21559  */
21560
21561 Roo.bootstrap.PopoverNav = function(config){
21562     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21563 };
21564
21565 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21566     
21567     
21568     container_method : 'getPopoverHeader' 
21569     
21570      
21571     
21572     
21573    
21574 });
21575
21576  
21577
21578  /*
21579  * - LGPL
21580  *
21581  * Progress
21582  * 
21583  */
21584
21585 /**
21586  * @class Roo.bootstrap.Progress
21587  * @extends Roo.bootstrap.Component
21588  * Bootstrap Progress class
21589  * @cfg {Boolean} striped striped of the progress bar
21590  * @cfg {Boolean} active animated of the progress bar
21591  * 
21592  * 
21593  * @constructor
21594  * Create a new Progress
21595  * @param {Object} config The config object
21596  */
21597
21598 Roo.bootstrap.Progress = function(config){
21599     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21600 };
21601
21602 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21603     
21604     striped : false,
21605     active: false,
21606     
21607     getAutoCreate : function(){
21608         var cfg = {
21609             tag: 'div',
21610             cls: 'progress'
21611         };
21612         
21613         
21614         if(this.striped){
21615             cfg.cls += ' progress-striped';
21616         }
21617       
21618         if(this.active){
21619             cfg.cls += ' active';
21620         }
21621         
21622         
21623         return cfg;
21624     }
21625    
21626 });
21627
21628  
21629
21630  /*
21631  * - LGPL
21632  *
21633  * ProgressBar
21634  * 
21635  */
21636
21637 /**
21638  * @class Roo.bootstrap.ProgressBar
21639  * @extends Roo.bootstrap.Component
21640  * Bootstrap ProgressBar class
21641  * @cfg {Number} aria_valuenow aria-value now
21642  * @cfg {Number} aria_valuemin aria-value min
21643  * @cfg {Number} aria_valuemax aria-value max
21644  * @cfg {String} label label for the progress bar
21645  * @cfg {String} panel (success | info | warning | danger )
21646  * @cfg {String} role role of the progress bar
21647  * @cfg {String} sr_only text
21648  * 
21649  * 
21650  * @constructor
21651  * Create a new ProgressBar
21652  * @param {Object} config The config object
21653  */
21654
21655 Roo.bootstrap.ProgressBar = function(config){
21656     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21657 };
21658
21659 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21660     
21661     aria_valuenow : 0,
21662     aria_valuemin : 0,
21663     aria_valuemax : 100,
21664     label : false,
21665     panel : false,
21666     role : false,
21667     sr_only: false,
21668     
21669     getAutoCreate : function()
21670     {
21671         
21672         var cfg = {
21673             tag: 'div',
21674             cls: 'progress-bar',
21675             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21676         };
21677         
21678         if(this.sr_only){
21679             cfg.cn = {
21680                 tag: 'span',
21681                 cls: 'sr-only',
21682                 html: this.sr_only
21683             }
21684         }
21685         
21686         if(this.role){
21687             cfg.role = this.role;
21688         }
21689         
21690         if(this.aria_valuenow){
21691             cfg['aria-valuenow'] = this.aria_valuenow;
21692         }
21693         
21694         if(this.aria_valuemin){
21695             cfg['aria-valuemin'] = this.aria_valuemin;
21696         }
21697         
21698         if(this.aria_valuemax){
21699             cfg['aria-valuemax'] = this.aria_valuemax;
21700         }
21701         
21702         if(this.label && !this.sr_only){
21703             cfg.html = this.label;
21704         }
21705         
21706         if(this.panel){
21707             cfg.cls += ' progress-bar-' + this.panel;
21708         }
21709         
21710         return cfg;
21711     },
21712     
21713     update : function(aria_valuenow)
21714     {
21715         this.aria_valuenow = aria_valuenow;
21716         
21717         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21718     }
21719    
21720 });
21721
21722  
21723
21724  /*
21725  * - LGPL
21726  *
21727  * column
21728  * 
21729  */
21730
21731 /**
21732  * @class Roo.bootstrap.TabGroup
21733  * @extends Roo.bootstrap.Column
21734  * Bootstrap Column class
21735  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21736  * @cfg {Boolean} carousel true to make the group behave like a carousel
21737  * @cfg {Boolean} bullets show bullets for the panels
21738  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21739  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21740  * @cfg {Boolean} showarrow (true|false) show arrow default true
21741  * 
21742  * @constructor
21743  * Create a new TabGroup
21744  * @param {Object} config The config object
21745  */
21746
21747 Roo.bootstrap.TabGroup = function(config){
21748     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21749     if (!this.navId) {
21750         this.navId = Roo.id();
21751     }
21752     this.tabs = [];
21753     Roo.bootstrap.TabGroup.register(this);
21754     
21755 };
21756
21757 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21758     
21759     carousel : false,
21760     transition : false,
21761     bullets : 0,
21762     timer : 0,
21763     autoslide : false,
21764     slideFn : false,
21765     slideOnTouch : false,
21766     showarrow : true,
21767     
21768     getAutoCreate : function()
21769     {
21770         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21771         
21772         cfg.cls += ' tab-content';
21773         
21774         if (this.carousel) {
21775             cfg.cls += ' carousel slide';
21776             
21777             cfg.cn = [{
21778                cls : 'carousel-inner',
21779                cn : []
21780             }];
21781         
21782             if(this.bullets  && !Roo.isTouch){
21783                 
21784                 var bullets = {
21785                     cls : 'carousel-bullets',
21786                     cn : []
21787                 };
21788                
21789                 if(this.bullets_cls){
21790                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21791                 }
21792                 
21793                 bullets.cn.push({
21794                     cls : 'clear'
21795                 });
21796                 
21797                 cfg.cn[0].cn.push(bullets);
21798             }
21799             
21800             if(this.showarrow){
21801                 cfg.cn[0].cn.push({
21802                     tag : 'div',
21803                     class : 'carousel-arrow',
21804                     cn : [
21805                         {
21806                             tag : 'div',
21807                             class : 'carousel-prev',
21808                             cn : [
21809                                 {
21810                                     tag : 'i',
21811                                     class : 'fa fa-chevron-left'
21812                                 }
21813                             ]
21814                         },
21815                         {
21816                             tag : 'div',
21817                             class : 'carousel-next',
21818                             cn : [
21819                                 {
21820                                     tag : 'i',
21821                                     class : 'fa fa-chevron-right'
21822                                 }
21823                             ]
21824                         }
21825                     ]
21826                 });
21827             }
21828             
21829         }
21830         
21831         return cfg;
21832     },
21833     
21834     initEvents:  function()
21835     {
21836 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21837 //            this.el.on("touchstart", this.onTouchStart, this);
21838 //        }
21839         
21840         if(this.autoslide){
21841             var _this = this;
21842             
21843             this.slideFn = window.setInterval(function() {
21844                 _this.showPanelNext();
21845             }, this.timer);
21846         }
21847         
21848         if(this.showarrow){
21849             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21850             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21851         }
21852         
21853         
21854     },
21855     
21856 //    onTouchStart : function(e, el, o)
21857 //    {
21858 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21859 //            return;
21860 //        }
21861 //        
21862 //        this.showPanelNext();
21863 //    },
21864     
21865     
21866     getChildContainer : function()
21867     {
21868         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21869     },
21870     
21871     /**
21872     * register a Navigation item
21873     * @param {Roo.bootstrap.NavItem} the navitem to add
21874     */
21875     register : function(item)
21876     {
21877         this.tabs.push( item);
21878         item.navId = this.navId; // not really needed..
21879         this.addBullet();
21880     
21881     },
21882     
21883     getActivePanel : function()
21884     {
21885         var r = false;
21886         Roo.each(this.tabs, function(t) {
21887             if (t.active) {
21888                 r = t;
21889                 return false;
21890             }
21891             return null;
21892         });
21893         return r;
21894         
21895     },
21896     getPanelByName : function(n)
21897     {
21898         var r = false;
21899         Roo.each(this.tabs, function(t) {
21900             if (t.tabId == n) {
21901                 r = t;
21902                 return false;
21903             }
21904             return null;
21905         });
21906         return r;
21907     },
21908     indexOfPanel : function(p)
21909     {
21910         var r = false;
21911         Roo.each(this.tabs, function(t,i) {
21912             if (t.tabId == p.tabId) {
21913                 r = i;
21914                 return false;
21915             }
21916             return null;
21917         });
21918         return r;
21919     },
21920     /**
21921      * show a specific panel
21922      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21923      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21924      */
21925     showPanel : function (pan)
21926     {
21927         if(this.transition || typeof(pan) == 'undefined'){
21928             Roo.log("waiting for the transitionend");
21929             return false;
21930         }
21931         
21932         if (typeof(pan) == 'number') {
21933             pan = this.tabs[pan];
21934         }
21935         
21936         if (typeof(pan) == 'string') {
21937             pan = this.getPanelByName(pan);
21938         }
21939         
21940         var cur = this.getActivePanel();
21941         
21942         if(!pan || !cur){
21943             Roo.log('pan or acitve pan is undefined');
21944             return false;
21945         }
21946         
21947         if (pan.tabId == this.getActivePanel().tabId) {
21948             return true;
21949         }
21950         
21951         if (false === cur.fireEvent('beforedeactivate')) {
21952             return false;
21953         }
21954         
21955         if(this.bullets > 0 && !Roo.isTouch){
21956             this.setActiveBullet(this.indexOfPanel(pan));
21957         }
21958         
21959         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21960             
21961             //class="carousel-item carousel-item-next carousel-item-left"
21962             
21963             this.transition = true;
21964             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21965             var lr = dir == 'next' ? 'left' : 'right';
21966             pan.el.addClass(dir); // or prev
21967             pan.el.addClass('carousel-item-' + dir); // or prev
21968             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21969             cur.el.addClass(lr); // or right
21970             pan.el.addClass(lr);
21971             cur.el.addClass('carousel-item-' +lr); // or right
21972             pan.el.addClass('carousel-item-' +lr);
21973             
21974             
21975             var _this = this;
21976             cur.el.on('transitionend', function() {
21977                 Roo.log("trans end?");
21978                 
21979                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21980                 pan.setActive(true);
21981                 
21982                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21983                 cur.setActive(false);
21984                 
21985                 _this.transition = false;
21986                 
21987             }, this, { single:  true } );
21988             
21989             return true;
21990         }
21991         
21992         cur.setActive(false);
21993         pan.setActive(true);
21994         
21995         return true;
21996         
21997     },
21998     showPanelNext : function()
21999     {
22000         var i = this.indexOfPanel(this.getActivePanel());
22001         
22002         if (i >= this.tabs.length - 1 && !this.autoslide) {
22003             return;
22004         }
22005         
22006         if (i >= this.tabs.length - 1 && this.autoslide) {
22007             i = -1;
22008         }
22009         
22010         this.showPanel(this.tabs[i+1]);
22011     },
22012     
22013     showPanelPrev : function()
22014     {
22015         var i = this.indexOfPanel(this.getActivePanel());
22016         
22017         if (i  < 1 && !this.autoslide) {
22018             return;
22019         }
22020         
22021         if (i < 1 && this.autoslide) {
22022             i = this.tabs.length;
22023         }
22024         
22025         this.showPanel(this.tabs[i-1]);
22026     },
22027     
22028     
22029     addBullet: function()
22030     {
22031         if(!this.bullets || Roo.isTouch){
22032             return;
22033         }
22034         var ctr = this.el.select('.carousel-bullets',true).first();
22035         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22036         var bullet = ctr.createChild({
22037             cls : 'bullet bullet-' + i
22038         },ctr.dom.lastChild);
22039         
22040         
22041         var _this = this;
22042         
22043         bullet.on('click', (function(e, el, o, ii, t){
22044
22045             e.preventDefault();
22046
22047             this.showPanel(ii);
22048
22049             if(this.autoslide && this.slideFn){
22050                 clearInterval(this.slideFn);
22051                 this.slideFn = window.setInterval(function() {
22052                     _this.showPanelNext();
22053                 }, this.timer);
22054             }
22055
22056         }).createDelegate(this, [i, bullet], true));
22057                 
22058         
22059     },
22060      
22061     setActiveBullet : function(i)
22062     {
22063         if(Roo.isTouch){
22064             return;
22065         }
22066         
22067         Roo.each(this.el.select('.bullet', true).elements, function(el){
22068             el.removeClass('selected');
22069         });
22070
22071         var bullet = this.el.select('.bullet-' + i, true).first();
22072         
22073         if(!bullet){
22074             return;
22075         }
22076         
22077         bullet.addClass('selected');
22078     }
22079     
22080     
22081   
22082 });
22083
22084  
22085
22086  
22087  
22088 Roo.apply(Roo.bootstrap.TabGroup, {
22089     
22090     groups: {},
22091      /**
22092     * register a Navigation Group
22093     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22094     */
22095     register : function(navgrp)
22096     {
22097         this.groups[navgrp.navId] = navgrp;
22098         
22099     },
22100     /**
22101     * fetch a Navigation Group based on the navigation ID
22102     * if one does not exist , it will get created.
22103     * @param {string} the navgroup to add
22104     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22105     */
22106     get: function(navId) {
22107         if (typeof(this.groups[navId]) == 'undefined') {
22108             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22109         }
22110         return this.groups[navId] ;
22111     }
22112     
22113     
22114     
22115 });
22116
22117  /*
22118  * - LGPL
22119  *
22120  * TabPanel
22121  * 
22122  */
22123
22124 /**
22125  * @class Roo.bootstrap.TabPanel
22126  * @extends Roo.bootstrap.Component
22127  * Bootstrap TabPanel class
22128  * @cfg {Boolean} active panel active
22129  * @cfg {String} html panel content
22130  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22131  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22132  * @cfg {String} href click to link..
22133  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22134  * 
22135  * 
22136  * @constructor
22137  * Create a new TabPanel
22138  * @param {Object} config The config object
22139  */
22140
22141 Roo.bootstrap.TabPanel = function(config){
22142     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22143     this.addEvents({
22144         /**
22145              * @event changed
22146              * Fires when the active status changes
22147              * @param {Roo.bootstrap.TabPanel} this
22148              * @param {Boolean} state the new state
22149             
22150          */
22151         'changed': true,
22152         /**
22153              * @event beforedeactivate
22154              * Fires before a tab is de-activated - can be used to do validation on a form.
22155              * @param {Roo.bootstrap.TabPanel} this
22156              * @return {Boolean} false if there is an error
22157             
22158          */
22159         'beforedeactivate': true
22160      });
22161     
22162     this.tabId = this.tabId || Roo.id();
22163   
22164 };
22165
22166 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22167     
22168     active: false,
22169     html: false,
22170     tabId: false,
22171     navId : false,
22172     href : '',
22173     touchSlide : false,
22174     getAutoCreate : function(){
22175         
22176         
22177         var cfg = {
22178             tag: 'div',
22179             // item is needed for carousel - not sure if it has any effect otherwise
22180             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22181             html: this.html || ''
22182         };
22183         
22184         if(this.active){
22185             cfg.cls += ' active';
22186         }
22187         
22188         if(this.tabId){
22189             cfg.tabId = this.tabId;
22190         }
22191         
22192         
22193         
22194         return cfg;
22195     },
22196     
22197     initEvents:  function()
22198     {
22199         var p = this.parent();
22200         
22201         this.navId = this.navId || p.navId;
22202         
22203         if (typeof(this.navId) != 'undefined') {
22204             // not really needed.. but just in case.. parent should be a NavGroup.
22205             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22206             
22207             tg.register(this);
22208             
22209             var i = tg.tabs.length - 1;
22210             
22211             if(this.active && tg.bullets > 0 && i < tg.bullets){
22212                 tg.setActiveBullet(i);
22213             }
22214         }
22215         
22216         this.el.on('click', this.onClick, this);
22217         
22218         if(Roo.isTouch && this.touchSlide){
22219             this.el.on("touchstart", this.onTouchStart, this);
22220             this.el.on("touchmove", this.onTouchMove, this);
22221             this.el.on("touchend", this.onTouchEnd, this);
22222         }
22223         
22224     },
22225     
22226     onRender : function(ct, position)
22227     {
22228         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22229     },
22230     
22231     setActive : function(state)
22232     {
22233         Roo.log("panel - set active " + this.tabId + "=" + state);
22234         
22235         this.active = state;
22236         if (!state) {
22237             this.el.removeClass('active');
22238             
22239         } else  if (!this.el.hasClass('active')) {
22240             this.el.addClass('active');
22241         }
22242         
22243         this.fireEvent('changed', this, state);
22244     },
22245     
22246     onClick : function(e)
22247     {
22248         e.preventDefault();
22249         
22250         if(!this.href.length){
22251             return;
22252         }
22253         
22254         window.location.href = this.href;
22255     },
22256     
22257     startX : 0,
22258     startY : 0,
22259     endX : 0,
22260     endY : 0,
22261     swiping : false,
22262     
22263     onTouchStart : function(e)
22264     {
22265         this.swiping = false;
22266         
22267         this.startX = e.browserEvent.touches[0].clientX;
22268         this.startY = e.browserEvent.touches[0].clientY;
22269     },
22270     
22271     onTouchMove : function(e)
22272     {
22273         this.swiping = true;
22274         
22275         this.endX = e.browserEvent.touches[0].clientX;
22276         this.endY = e.browserEvent.touches[0].clientY;
22277     },
22278     
22279     onTouchEnd : function(e)
22280     {
22281         if(!this.swiping){
22282             this.onClick(e);
22283             return;
22284         }
22285         
22286         var tabGroup = this.parent();
22287         
22288         if(this.endX > this.startX){ // swiping right
22289             tabGroup.showPanelPrev();
22290             return;
22291         }
22292         
22293         if(this.startX > this.endX){ // swiping left
22294             tabGroup.showPanelNext();
22295             return;
22296         }
22297     }
22298     
22299     
22300 });
22301  
22302
22303  
22304
22305  /*
22306  * - LGPL
22307  *
22308  * DateField
22309  * 
22310  */
22311
22312 /**
22313  * @class Roo.bootstrap.DateField
22314  * @extends Roo.bootstrap.Input
22315  * Bootstrap DateField class
22316  * @cfg {Number} weekStart default 0
22317  * @cfg {String} viewMode default empty, (months|years)
22318  * @cfg {String} minViewMode default empty, (months|years)
22319  * @cfg {Number} startDate default -Infinity
22320  * @cfg {Number} endDate default Infinity
22321  * @cfg {Boolean} todayHighlight default false
22322  * @cfg {Boolean} todayBtn default false
22323  * @cfg {Boolean} calendarWeeks default false
22324  * @cfg {Object} daysOfWeekDisabled default empty
22325  * @cfg {Boolean} singleMode default false (true | false)
22326  * 
22327  * @cfg {Boolean} keyboardNavigation default true
22328  * @cfg {String} language default en
22329  * 
22330  * @constructor
22331  * Create a new DateField
22332  * @param {Object} config The config object
22333  */
22334
22335 Roo.bootstrap.DateField = function(config){
22336     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22337      this.addEvents({
22338             /**
22339              * @event show
22340              * Fires when this field show.
22341              * @param {Roo.bootstrap.DateField} this
22342              * @param {Mixed} date The date value
22343              */
22344             show : true,
22345             /**
22346              * @event show
22347              * Fires when this field hide.
22348              * @param {Roo.bootstrap.DateField} this
22349              * @param {Mixed} date The date value
22350              */
22351             hide : true,
22352             /**
22353              * @event select
22354              * Fires when select a date.
22355              * @param {Roo.bootstrap.DateField} this
22356              * @param {Mixed} date The date value
22357              */
22358             select : true,
22359             /**
22360              * @event beforeselect
22361              * Fires when before select a date.
22362              * @param {Roo.bootstrap.DateField} this
22363              * @param {Mixed} date The date value
22364              */
22365             beforeselect : true
22366         });
22367 };
22368
22369 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22370     
22371     /**
22372      * @cfg {String} format
22373      * The default date format string which can be overriden for localization support.  The format must be
22374      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22375      */
22376     format : "m/d/y",
22377     /**
22378      * @cfg {String} altFormats
22379      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22380      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22381      */
22382     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22383     
22384     weekStart : 0,
22385     
22386     viewMode : '',
22387     
22388     minViewMode : '',
22389     
22390     todayHighlight : false,
22391     
22392     todayBtn: false,
22393     
22394     language: 'en',
22395     
22396     keyboardNavigation: true,
22397     
22398     calendarWeeks: false,
22399     
22400     startDate: -Infinity,
22401     
22402     endDate: Infinity,
22403     
22404     daysOfWeekDisabled: [],
22405     
22406     _events: [],
22407     
22408     singleMode : false,
22409     
22410     UTCDate: function()
22411     {
22412         return new Date(Date.UTC.apply(Date, arguments));
22413     },
22414     
22415     UTCToday: function()
22416     {
22417         var today = new Date();
22418         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22419     },
22420     
22421     getDate: function() {
22422             var d = this.getUTCDate();
22423             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22424     },
22425     
22426     getUTCDate: function() {
22427             return this.date;
22428     },
22429     
22430     setDate: function(d) {
22431             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22432     },
22433     
22434     setUTCDate: function(d) {
22435             this.date = d;
22436             this.setValue(this.formatDate(this.date));
22437     },
22438         
22439     onRender: function(ct, position)
22440     {
22441         
22442         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22443         
22444         this.language = this.language || 'en';
22445         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22446         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22447         
22448         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22449         this.format = this.format || 'm/d/y';
22450         this.isInline = false;
22451         this.isInput = true;
22452         this.component = this.el.select('.add-on', true).first() || false;
22453         this.component = (this.component && this.component.length === 0) ? false : this.component;
22454         this.hasInput = this.component && this.inputEl().length;
22455         
22456         if (typeof(this.minViewMode === 'string')) {
22457             switch (this.minViewMode) {
22458                 case 'months':
22459                     this.minViewMode = 1;
22460                     break;
22461                 case 'years':
22462                     this.minViewMode = 2;
22463                     break;
22464                 default:
22465                     this.minViewMode = 0;
22466                     break;
22467             }
22468         }
22469         
22470         if (typeof(this.viewMode === 'string')) {
22471             switch (this.viewMode) {
22472                 case 'months':
22473                     this.viewMode = 1;
22474                     break;
22475                 case 'years':
22476                     this.viewMode = 2;
22477                     break;
22478                 default:
22479                     this.viewMode = 0;
22480                     break;
22481             }
22482         }
22483                 
22484         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22485         
22486 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22487         
22488         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22489         
22490         this.picker().on('mousedown', this.onMousedown, this);
22491         this.picker().on('click', this.onClick, this);
22492         
22493         this.picker().addClass('datepicker-dropdown');
22494         
22495         this.startViewMode = this.viewMode;
22496         
22497         if(this.singleMode){
22498             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22499                 v.setVisibilityMode(Roo.Element.DISPLAY);
22500                 v.hide();
22501             });
22502             
22503             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22504                 v.setStyle('width', '189px');
22505             });
22506         }
22507         
22508         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22509             if(!this.calendarWeeks){
22510                 v.remove();
22511                 return;
22512             }
22513             
22514             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22515             v.attr('colspan', function(i, val){
22516                 return parseInt(val) + 1;
22517             });
22518         });
22519                         
22520         
22521         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22522         
22523         this.setStartDate(this.startDate);
22524         this.setEndDate(this.endDate);
22525         
22526         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22527         
22528         this.fillDow();
22529         this.fillMonths();
22530         this.update();
22531         this.showMode();
22532         
22533         if(this.isInline) {
22534             this.showPopup();
22535         }
22536     },
22537     
22538     picker : function()
22539     {
22540         return this.pickerEl;
22541 //        return this.el.select('.datepicker', true).first();
22542     },
22543     
22544     fillDow: function()
22545     {
22546         var dowCnt = this.weekStart;
22547         
22548         var dow = {
22549             tag: 'tr',
22550             cn: [
22551                 
22552             ]
22553         };
22554         
22555         if(this.calendarWeeks){
22556             dow.cn.push({
22557                 tag: 'th',
22558                 cls: 'cw',
22559                 html: '&nbsp;'
22560             })
22561         }
22562         
22563         while (dowCnt < this.weekStart + 7) {
22564             dow.cn.push({
22565                 tag: 'th',
22566                 cls: 'dow',
22567                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22568             });
22569         }
22570         
22571         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22572     },
22573     
22574     fillMonths: function()
22575     {    
22576         var i = 0;
22577         var months = this.picker().select('>.datepicker-months td', true).first();
22578         
22579         months.dom.innerHTML = '';
22580         
22581         while (i < 12) {
22582             var month = {
22583                 tag: 'span',
22584                 cls: 'month',
22585                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22586             };
22587             
22588             months.createChild(month);
22589         }
22590         
22591     },
22592     
22593     update: function()
22594     {
22595         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;
22596         
22597         if (this.date < this.startDate) {
22598             this.viewDate = new Date(this.startDate);
22599         } else if (this.date > this.endDate) {
22600             this.viewDate = new Date(this.endDate);
22601         } else {
22602             this.viewDate = new Date(this.date);
22603         }
22604         
22605         this.fill();
22606     },
22607     
22608     fill: function() 
22609     {
22610         var d = new Date(this.viewDate),
22611                 year = d.getUTCFullYear(),
22612                 month = d.getUTCMonth(),
22613                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22614                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22615                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22616                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22617                 currentDate = this.date && this.date.valueOf(),
22618                 today = this.UTCToday();
22619         
22620         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22621         
22622 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22623         
22624 //        this.picker.select('>tfoot th.today').
22625 //                                              .text(dates[this.language].today)
22626 //                                              .toggle(this.todayBtn !== false);
22627     
22628         this.updateNavArrows();
22629         this.fillMonths();
22630                                                 
22631         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22632         
22633         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22634          
22635         prevMonth.setUTCDate(day);
22636         
22637         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22638         
22639         var nextMonth = new Date(prevMonth);
22640         
22641         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22642         
22643         nextMonth = nextMonth.valueOf();
22644         
22645         var fillMonths = false;
22646         
22647         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22648         
22649         while(prevMonth.valueOf() <= nextMonth) {
22650             var clsName = '';
22651             
22652             if (prevMonth.getUTCDay() === this.weekStart) {
22653                 if(fillMonths){
22654                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22655                 }
22656                     
22657                 fillMonths = {
22658                     tag: 'tr',
22659                     cn: []
22660                 };
22661                 
22662                 if(this.calendarWeeks){
22663                     // ISO 8601: First week contains first thursday.
22664                     // ISO also states week starts on Monday, but we can be more abstract here.
22665                     var
22666                     // Start of current week: based on weekstart/current date
22667                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22668                     // Thursday of this week
22669                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22670                     // First Thursday of year, year from thursday
22671                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22672                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22673                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22674                     
22675                     fillMonths.cn.push({
22676                         tag: 'td',
22677                         cls: 'cw',
22678                         html: calWeek
22679                     });
22680                 }
22681             }
22682             
22683             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22684                 clsName += ' old';
22685             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22686                 clsName += ' new';
22687             }
22688             if (this.todayHighlight &&
22689                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22690                 prevMonth.getUTCMonth() == today.getMonth() &&
22691                 prevMonth.getUTCDate() == today.getDate()) {
22692                 clsName += ' today';
22693             }
22694             
22695             if (currentDate && prevMonth.valueOf() === currentDate) {
22696                 clsName += ' active';
22697             }
22698             
22699             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22700                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22701                     clsName += ' disabled';
22702             }
22703             
22704             fillMonths.cn.push({
22705                 tag: 'td',
22706                 cls: 'day ' + clsName,
22707                 html: prevMonth.getDate()
22708             });
22709             
22710             prevMonth.setDate(prevMonth.getDate()+1);
22711         }
22712           
22713         var currentYear = this.date && this.date.getUTCFullYear();
22714         var currentMonth = this.date && this.date.getUTCMonth();
22715         
22716         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22717         
22718         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22719             v.removeClass('active');
22720             
22721             if(currentYear === year && k === currentMonth){
22722                 v.addClass('active');
22723             }
22724             
22725             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22726                 v.addClass('disabled');
22727             }
22728             
22729         });
22730         
22731         
22732         year = parseInt(year/10, 10) * 10;
22733         
22734         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22735         
22736         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22737         
22738         year -= 1;
22739         for (var i = -1; i < 11; i++) {
22740             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22741                 tag: 'span',
22742                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22743                 html: year
22744             });
22745             
22746             year += 1;
22747         }
22748     },
22749     
22750     showMode: function(dir) 
22751     {
22752         if (dir) {
22753             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22754         }
22755         
22756         Roo.each(this.picker().select('>div',true).elements, function(v){
22757             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22758             v.hide();
22759         });
22760         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22761     },
22762     
22763     place: function()
22764     {
22765         if(this.isInline) {
22766             return;
22767         }
22768         
22769         this.picker().removeClass(['bottom', 'top']);
22770         
22771         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22772             /*
22773              * place to the top of element!
22774              *
22775              */
22776             
22777             this.picker().addClass('top');
22778             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22779             
22780             return;
22781         }
22782         
22783         this.picker().addClass('bottom');
22784         
22785         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22786     },
22787     
22788     parseDate : function(value)
22789     {
22790         if(!value || value instanceof Date){
22791             return value;
22792         }
22793         var v = Date.parseDate(value, this.format);
22794         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22795             v = Date.parseDate(value, 'Y-m-d');
22796         }
22797         if(!v && this.altFormats){
22798             if(!this.altFormatsArray){
22799                 this.altFormatsArray = this.altFormats.split("|");
22800             }
22801             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22802                 v = Date.parseDate(value, this.altFormatsArray[i]);
22803             }
22804         }
22805         return v;
22806     },
22807     
22808     formatDate : function(date, fmt)
22809     {   
22810         return (!date || !(date instanceof Date)) ?
22811         date : date.dateFormat(fmt || this.format);
22812     },
22813     
22814     onFocus : function()
22815     {
22816         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22817         this.showPopup();
22818     },
22819     
22820     onBlur : function()
22821     {
22822         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22823         
22824         var d = this.inputEl().getValue();
22825         
22826         this.setValue(d);
22827                 
22828         this.hidePopup();
22829     },
22830     
22831     showPopup : function()
22832     {
22833         this.picker().show();
22834         this.update();
22835         this.place();
22836         
22837         this.fireEvent('showpopup', this, this.date);
22838     },
22839     
22840     hidePopup : function()
22841     {
22842         if(this.isInline) {
22843             return;
22844         }
22845         this.picker().hide();
22846         this.viewMode = this.startViewMode;
22847         this.showMode();
22848         
22849         this.fireEvent('hidepopup', this, this.date);
22850         
22851     },
22852     
22853     onMousedown: function(e)
22854     {
22855         e.stopPropagation();
22856         e.preventDefault();
22857     },
22858     
22859     keyup: function(e)
22860     {
22861         Roo.bootstrap.DateField.superclass.keyup.call(this);
22862         this.update();
22863     },
22864
22865     setValue: function(v)
22866     {
22867         if(this.fireEvent('beforeselect', this, v) !== false){
22868             var d = new Date(this.parseDate(v) ).clearTime();
22869         
22870             if(isNaN(d.getTime())){
22871                 this.date = this.viewDate = '';
22872                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22873                 return;
22874             }
22875
22876             v = this.formatDate(d);
22877
22878             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22879
22880             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22881
22882             this.update();
22883
22884             this.fireEvent('select', this, this.date);
22885         }
22886     },
22887     
22888     getValue: function()
22889     {
22890         return this.formatDate(this.date);
22891     },
22892     
22893     fireKey: function(e)
22894     {
22895         if (!this.picker().isVisible()){
22896             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22897                 this.showPopup();
22898             }
22899             return;
22900         }
22901         
22902         var dateChanged = false,
22903         dir, day, month,
22904         newDate, newViewDate;
22905         
22906         switch(e.keyCode){
22907             case 27: // escape
22908                 this.hidePopup();
22909                 e.preventDefault();
22910                 break;
22911             case 37: // left
22912             case 39: // right
22913                 if (!this.keyboardNavigation) {
22914                     break;
22915                 }
22916                 dir = e.keyCode == 37 ? -1 : 1;
22917                 
22918                 if (e.ctrlKey){
22919                     newDate = this.moveYear(this.date, dir);
22920                     newViewDate = this.moveYear(this.viewDate, dir);
22921                 } else if (e.shiftKey){
22922                     newDate = this.moveMonth(this.date, dir);
22923                     newViewDate = this.moveMonth(this.viewDate, dir);
22924                 } else {
22925                     newDate = new Date(this.date);
22926                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22927                     newViewDate = new Date(this.viewDate);
22928                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22929                 }
22930                 if (this.dateWithinRange(newDate)){
22931                     this.date = newDate;
22932                     this.viewDate = newViewDate;
22933                     this.setValue(this.formatDate(this.date));
22934 //                    this.update();
22935                     e.preventDefault();
22936                     dateChanged = true;
22937                 }
22938                 break;
22939             case 38: // up
22940             case 40: // down
22941                 if (!this.keyboardNavigation) {
22942                     break;
22943                 }
22944                 dir = e.keyCode == 38 ? -1 : 1;
22945                 if (e.ctrlKey){
22946                     newDate = this.moveYear(this.date, dir);
22947                     newViewDate = this.moveYear(this.viewDate, dir);
22948                 } else if (e.shiftKey){
22949                     newDate = this.moveMonth(this.date, dir);
22950                     newViewDate = this.moveMonth(this.viewDate, dir);
22951                 } else {
22952                     newDate = new Date(this.date);
22953                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22954                     newViewDate = new Date(this.viewDate);
22955                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22956                 }
22957                 if (this.dateWithinRange(newDate)){
22958                     this.date = newDate;
22959                     this.viewDate = newViewDate;
22960                     this.setValue(this.formatDate(this.date));
22961 //                    this.update();
22962                     e.preventDefault();
22963                     dateChanged = true;
22964                 }
22965                 break;
22966             case 13: // enter
22967                 this.setValue(this.formatDate(this.date));
22968                 this.hidePopup();
22969                 e.preventDefault();
22970                 break;
22971             case 9: // tab
22972                 this.setValue(this.formatDate(this.date));
22973                 this.hidePopup();
22974                 break;
22975             case 16: // shift
22976             case 17: // ctrl
22977             case 18: // alt
22978                 break;
22979             default :
22980                 this.hidePopup();
22981                 
22982         }
22983     },
22984     
22985     
22986     onClick: function(e) 
22987     {
22988         e.stopPropagation();
22989         e.preventDefault();
22990         
22991         var target = e.getTarget();
22992         
22993         if(target.nodeName.toLowerCase() === 'i'){
22994             target = Roo.get(target).dom.parentNode;
22995         }
22996         
22997         var nodeName = target.nodeName;
22998         var className = target.className;
22999         var html = target.innerHTML;
23000         //Roo.log(nodeName);
23001         
23002         switch(nodeName.toLowerCase()) {
23003             case 'th':
23004                 switch(className) {
23005                     case 'switch':
23006                         this.showMode(1);
23007                         break;
23008                     case 'prev':
23009                     case 'next':
23010                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23011                         switch(this.viewMode){
23012                                 case 0:
23013                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23014                                         break;
23015                                 case 1:
23016                                 case 2:
23017                                         this.viewDate = this.moveYear(this.viewDate, dir);
23018                                         break;
23019                         }
23020                         this.fill();
23021                         break;
23022                     case 'today':
23023                         var date = new Date();
23024                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23025 //                        this.fill()
23026                         this.setValue(this.formatDate(this.date));
23027                         
23028                         this.hidePopup();
23029                         break;
23030                 }
23031                 break;
23032             case 'span':
23033                 if (className.indexOf('disabled') < 0) {
23034                 if (!this.viewDate) {
23035                     this.viewDate = new Date();
23036                 }
23037                 this.viewDate.setUTCDate(1);
23038                     if (className.indexOf('month') > -1) {
23039                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23040                     } else {
23041                         var year = parseInt(html, 10) || 0;
23042                         this.viewDate.setUTCFullYear(year);
23043                         
23044                     }
23045                     
23046                     if(this.singleMode){
23047                         this.setValue(this.formatDate(this.viewDate));
23048                         this.hidePopup();
23049                         return;
23050                     }
23051                     
23052                     this.showMode(-1);
23053                     this.fill();
23054                 }
23055                 break;
23056                 
23057             case 'td':
23058                 //Roo.log(className);
23059                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23060                     var day = parseInt(html, 10) || 1;
23061                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23062                         month = (this.viewDate || new Date()).getUTCMonth();
23063
23064                     if (className.indexOf('old') > -1) {
23065                         if(month === 0 ){
23066                             month = 11;
23067                             year -= 1;
23068                         }else{
23069                             month -= 1;
23070                         }
23071                     } else if (className.indexOf('new') > -1) {
23072                         if (month == 11) {
23073                             month = 0;
23074                             year += 1;
23075                         } else {
23076                             month += 1;
23077                         }
23078                     }
23079                     //Roo.log([year,month,day]);
23080                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23081                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23082 //                    this.fill();
23083                     //Roo.log(this.formatDate(this.date));
23084                     this.setValue(this.formatDate(this.date));
23085                     this.hidePopup();
23086                 }
23087                 break;
23088         }
23089     },
23090     
23091     setStartDate: function(startDate)
23092     {
23093         this.startDate = startDate || -Infinity;
23094         if (this.startDate !== -Infinity) {
23095             this.startDate = this.parseDate(this.startDate);
23096         }
23097         this.update();
23098         this.updateNavArrows();
23099     },
23100
23101     setEndDate: function(endDate)
23102     {
23103         this.endDate = endDate || Infinity;
23104         if (this.endDate !== Infinity) {
23105             this.endDate = this.parseDate(this.endDate);
23106         }
23107         this.update();
23108         this.updateNavArrows();
23109     },
23110     
23111     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23112     {
23113         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23114         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23115             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23116         }
23117         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23118             return parseInt(d, 10);
23119         });
23120         this.update();
23121         this.updateNavArrows();
23122     },
23123     
23124     updateNavArrows: function() 
23125     {
23126         if(this.singleMode){
23127             return;
23128         }
23129         
23130         var d = new Date(this.viewDate),
23131         year = d.getUTCFullYear(),
23132         month = d.getUTCMonth();
23133         
23134         Roo.each(this.picker().select('.prev', true).elements, function(v){
23135             v.show();
23136             switch (this.viewMode) {
23137                 case 0:
23138
23139                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23140                         v.hide();
23141                     }
23142                     break;
23143                 case 1:
23144                 case 2:
23145                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23146                         v.hide();
23147                     }
23148                     break;
23149             }
23150         });
23151         
23152         Roo.each(this.picker().select('.next', true).elements, function(v){
23153             v.show();
23154             switch (this.viewMode) {
23155                 case 0:
23156
23157                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23158                         v.hide();
23159                     }
23160                     break;
23161                 case 1:
23162                 case 2:
23163                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23164                         v.hide();
23165                     }
23166                     break;
23167             }
23168         })
23169     },
23170     
23171     moveMonth: function(date, dir)
23172     {
23173         if (!dir) {
23174             return date;
23175         }
23176         var new_date = new Date(date.valueOf()),
23177         day = new_date.getUTCDate(),
23178         month = new_date.getUTCMonth(),
23179         mag = Math.abs(dir),
23180         new_month, test;
23181         dir = dir > 0 ? 1 : -1;
23182         if (mag == 1){
23183             test = dir == -1
23184             // If going back one month, make sure month is not current month
23185             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23186             ? function(){
23187                 return new_date.getUTCMonth() == month;
23188             }
23189             // If going forward one month, make sure month is as expected
23190             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23191             : function(){
23192                 return new_date.getUTCMonth() != new_month;
23193             };
23194             new_month = month + dir;
23195             new_date.setUTCMonth(new_month);
23196             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23197             if (new_month < 0 || new_month > 11) {
23198                 new_month = (new_month + 12) % 12;
23199             }
23200         } else {
23201             // For magnitudes >1, move one month at a time...
23202             for (var i=0; i<mag; i++) {
23203                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23204                 new_date = this.moveMonth(new_date, dir);
23205             }
23206             // ...then reset the day, keeping it in the new month
23207             new_month = new_date.getUTCMonth();
23208             new_date.setUTCDate(day);
23209             test = function(){
23210                 return new_month != new_date.getUTCMonth();
23211             };
23212         }
23213         // Common date-resetting loop -- if date is beyond end of month, make it
23214         // end of month
23215         while (test()){
23216             new_date.setUTCDate(--day);
23217             new_date.setUTCMonth(new_month);
23218         }
23219         return new_date;
23220     },
23221
23222     moveYear: function(date, dir)
23223     {
23224         return this.moveMonth(date, dir*12);
23225     },
23226
23227     dateWithinRange: function(date)
23228     {
23229         return date >= this.startDate && date <= this.endDate;
23230     },
23231
23232     
23233     remove: function() 
23234     {
23235         this.picker().remove();
23236     },
23237     
23238     validateValue : function(value)
23239     {
23240         if(this.getVisibilityEl().hasClass('hidden')){
23241             return true;
23242         }
23243         
23244         if(value.length < 1)  {
23245             if(this.allowBlank){
23246                 return true;
23247             }
23248             return false;
23249         }
23250         
23251         if(value.length < this.minLength){
23252             return false;
23253         }
23254         if(value.length > this.maxLength){
23255             return false;
23256         }
23257         if(this.vtype){
23258             var vt = Roo.form.VTypes;
23259             if(!vt[this.vtype](value, this)){
23260                 return false;
23261             }
23262         }
23263         if(typeof this.validator == "function"){
23264             var msg = this.validator(value);
23265             if(msg !== true){
23266                 return false;
23267             }
23268         }
23269         
23270         if(this.regex && !this.regex.test(value)){
23271             return false;
23272         }
23273         
23274         if(typeof(this.parseDate(value)) == 'undefined'){
23275             return false;
23276         }
23277         
23278         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23279             return false;
23280         }      
23281         
23282         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23283             return false;
23284         } 
23285         
23286         
23287         return true;
23288     },
23289     
23290     reset : function()
23291     {
23292         this.date = this.viewDate = '';
23293         
23294         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23295     }
23296    
23297 });
23298
23299 Roo.apply(Roo.bootstrap.DateField,  {
23300     
23301     head : {
23302         tag: 'thead',
23303         cn: [
23304         {
23305             tag: 'tr',
23306             cn: [
23307             {
23308                 tag: 'th',
23309                 cls: 'prev',
23310                 html: '<i class="fa fa-arrow-left"/>'
23311             },
23312             {
23313                 tag: 'th',
23314                 cls: 'switch',
23315                 colspan: '5'
23316             },
23317             {
23318                 tag: 'th',
23319                 cls: 'next',
23320                 html: '<i class="fa fa-arrow-right"/>'
23321             }
23322
23323             ]
23324         }
23325         ]
23326     },
23327     
23328     content : {
23329         tag: 'tbody',
23330         cn: [
23331         {
23332             tag: 'tr',
23333             cn: [
23334             {
23335                 tag: 'td',
23336                 colspan: '7'
23337             }
23338             ]
23339         }
23340         ]
23341     },
23342     
23343     footer : {
23344         tag: 'tfoot',
23345         cn: [
23346         {
23347             tag: 'tr',
23348             cn: [
23349             {
23350                 tag: 'th',
23351                 colspan: '7',
23352                 cls: 'today'
23353             }
23354                     
23355             ]
23356         }
23357         ]
23358     },
23359     
23360     dates:{
23361         en: {
23362             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23363             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23364             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23365             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23366             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23367             today: "Today"
23368         }
23369     },
23370     
23371     modes: [
23372     {
23373         clsName: 'days',
23374         navFnc: 'Month',
23375         navStep: 1
23376     },
23377     {
23378         clsName: 'months',
23379         navFnc: 'FullYear',
23380         navStep: 1
23381     },
23382     {
23383         clsName: 'years',
23384         navFnc: 'FullYear',
23385         navStep: 10
23386     }]
23387 });
23388
23389 Roo.apply(Roo.bootstrap.DateField,  {
23390   
23391     template : {
23392         tag: 'div',
23393         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23394         cn: [
23395         {
23396             tag: 'div',
23397             cls: 'datepicker-days',
23398             cn: [
23399             {
23400                 tag: 'table',
23401                 cls: 'table-condensed',
23402                 cn:[
23403                 Roo.bootstrap.DateField.head,
23404                 {
23405                     tag: 'tbody'
23406                 },
23407                 Roo.bootstrap.DateField.footer
23408                 ]
23409             }
23410             ]
23411         },
23412         {
23413             tag: 'div',
23414             cls: 'datepicker-months',
23415             cn: [
23416             {
23417                 tag: 'table',
23418                 cls: 'table-condensed',
23419                 cn:[
23420                 Roo.bootstrap.DateField.head,
23421                 Roo.bootstrap.DateField.content,
23422                 Roo.bootstrap.DateField.footer
23423                 ]
23424             }
23425             ]
23426         },
23427         {
23428             tag: 'div',
23429             cls: 'datepicker-years',
23430             cn: [
23431             {
23432                 tag: 'table',
23433                 cls: 'table-condensed',
23434                 cn:[
23435                 Roo.bootstrap.DateField.head,
23436                 Roo.bootstrap.DateField.content,
23437                 Roo.bootstrap.DateField.footer
23438                 ]
23439             }
23440             ]
23441         }
23442         ]
23443     }
23444 });
23445
23446  
23447
23448  /*
23449  * - LGPL
23450  *
23451  * TimeField
23452  * 
23453  */
23454
23455 /**
23456  * @class Roo.bootstrap.TimeField
23457  * @extends Roo.bootstrap.Input
23458  * Bootstrap DateField class
23459  * 
23460  * 
23461  * @constructor
23462  * Create a new TimeField
23463  * @param {Object} config The config object
23464  */
23465
23466 Roo.bootstrap.TimeField = function(config){
23467     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23468     this.addEvents({
23469             /**
23470              * @event show
23471              * Fires when this field show.
23472              * @param {Roo.bootstrap.DateField} thisthis
23473              * @param {Mixed} date The date value
23474              */
23475             show : true,
23476             /**
23477              * @event show
23478              * Fires when this field hide.
23479              * @param {Roo.bootstrap.DateField} this
23480              * @param {Mixed} date The date value
23481              */
23482             hide : true,
23483             /**
23484              * @event select
23485              * Fires when select a date.
23486              * @param {Roo.bootstrap.DateField} this
23487              * @param {Mixed} date The date value
23488              */
23489             select : true
23490         });
23491 };
23492
23493 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23494     
23495     /**
23496      * @cfg {String} format
23497      * The default time format string which can be overriden for localization support.  The format must be
23498      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23499      */
23500     format : "H:i",
23501
23502     getAutoCreate : function()
23503     {
23504         this.after = '<i class="fa far fa-clock"></i>';
23505         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23506         
23507          
23508     },
23509     onRender: function(ct, position)
23510     {
23511         
23512         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23513                 
23514         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23515         
23516         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23517         
23518         this.pop = this.picker().select('>.datepicker-time',true).first();
23519         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23520         
23521         this.picker().on('mousedown', this.onMousedown, this);
23522         this.picker().on('click', this.onClick, this);
23523         
23524         this.picker().addClass('datepicker-dropdown');
23525     
23526         this.fillTime();
23527         this.update();
23528             
23529         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23530         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23531         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23532         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23533         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23534         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23535
23536     },
23537     
23538     fireKey: function(e){
23539         if (!this.picker().isVisible()){
23540             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23541                 this.show();
23542             }
23543             return;
23544         }
23545
23546         e.preventDefault();
23547         
23548         switch(e.keyCode){
23549             case 27: // escape
23550                 this.hide();
23551                 break;
23552             case 37: // left
23553             case 39: // right
23554                 this.onTogglePeriod();
23555                 break;
23556             case 38: // up
23557                 this.onIncrementMinutes();
23558                 break;
23559             case 40: // down
23560                 this.onDecrementMinutes();
23561                 break;
23562             case 13: // enter
23563             case 9: // tab
23564                 this.setTime();
23565                 break;
23566         }
23567     },
23568     
23569     onClick: function(e) {
23570         e.stopPropagation();
23571         e.preventDefault();
23572     },
23573     
23574     picker : function()
23575     {
23576         return this.pickerEl;
23577     },
23578     
23579     fillTime: function()
23580     {    
23581         var time = this.pop.select('tbody', true).first();
23582         
23583         time.dom.innerHTML = '';
23584         
23585         time.createChild({
23586             tag: 'tr',
23587             cn: [
23588                 {
23589                     tag: 'td',
23590                     cn: [
23591                         {
23592                             tag: 'a',
23593                             href: '#',
23594                             cls: 'btn',
23595                             cn: [
23596                                 {
23597                                     tag: 'i',
23598                                     cls: 'hours-up fa fas fa-chevron-up'
23599                                 }
23600                             ]
23601                         } 
23602                     ]
23603                 },
23604                 {
23605                     tag: 'td',
23606                     cls: 'separator'
23607                 },
23608                 {
23609                     tag: 'td',
23610                     cn: [
23611                         {
23612                             tag: 'a',
23613                             href: '#',
23614                             cls: 'btn',
23615                             cn: [
23616                                 {
23617                                     tag: 'i',
23618                                     cls: 'minutes-up fa fas fa-chevron-up'
23619                                 }
23620                             ]
23621                         }
23622                     ]
23623                 },
23624                 {
23625                     tag: 'td',
23626                     cls: 'separator'
23627                 }
23628             ]
23629         });
23630         
23631         time.createChild({
23632             tag: 'tr',
23633             cn: [
23634                 {
23635                     tag: 'td',
23636                     cn: [
23637                         {
23638                             tag: 'span',
23639                             cls: 'timepicker-hour',
23640                             html: '00'
23641                         }  
23642                     ]
23643                 },
23644                 {
23645                     tag: 'td',
23646                     cls: 'separator',
23647                     html: ':'
23648                 },
23649                 {
23650                     tag: 'td',
23651                     cn: [
23652                         {
23653                             tag: 'span',
23654                             cls: 'timepicker-minute',
23655                             html: '00'
23656                         }  
23657                     ]
23658                 },
23659                 {
23660                     tag: 'td',
23661                     cls: 'separator'
23662                 },
23663                 {
23664                     tag: 'td',
23665                     cn: [
23666                         {
23667                             tag: 'button',
23668                             type: 'button',
23669                             cls: 'btn btn-primary period',
23670                             html: 'AM'
23671                             
23672                         }
23673                     ]
23674                 }
23675             ]
23676         });
23677         
23678         time.createChild({
23679             tag: 'tr',
23680             cn: [
23681                 {
23682                     tag: 'td',
23683                     cn: [
23684                         {
23685                             tag: 'a',
23686                             href: '#',
23687                             cls: 'btn',
23688                             cn: [
23689                                 {
23690                                     tag: 'span',
23691                                     cls: 'hours-down fa fas fa-chevron-down'
23692                                 }
23693                             ]
23694                         }
23695                     ]
23696                 },
23697                 {
23698                     tag: 'td',
23699                     cls: 'separator'
23700                 },
23701                 {
23702                     tag: 'td',
23703                     cn: [
23704                         {
23705                             tag: 'a',
23706                             href: '#',
23707                             cls: 'btn',
23708                             cn: [
23709                                 {
23710                                     tag: 'span',
23711                                     cls: 'minutes-down fa fas fa-chevron-down'
23712                                 }
23713                             ]
23714                         }
23715                     ]
23716                 },
23717                 {
23718                     tag: 'td',
23719                     cls: 'separator'
23720                 }
23721             ]
23722         });
23723         
23724     },
23725     
23726     update: function()
23727     {
23728         
23729         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23730         
23731         this.fill();
23732     },
23733     
23734     fill: function() 
23735     {
23736         var hours = this.time.getHours();
23737         var minutes = this.time.getMinutes();
23738         var period = 'AM';
23739         
23740         if(hours > 11){
23741             period = 'PM';
23742         }
23743         
23744         if(hours == 0){
23745             hours = 12;
23746         }
23747         
23748         
23749         if(hours > 12){
23750             hours = hours - 12;
23751         }
23752         
23753         if(hours < 10){
23754             hours = '0' + hours;
23755         }
23756         
23757         if(minutes < 10){
23758             minutes = '0' + minutes;
23759         }
23760         
23761         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23762         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23763         this.pop.select('button', true).first().dom.innerHTML = period;
23764         
23765     },
23766     
23767     place: function()
23768     {   
23769         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23770         
23771         var cls = ['bottom'];
23772         
23773         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23774             cls.pop();
23775             cls.push('top');
23776         }
23777         
23778         cls.push('right');
23779         
23780         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23781             cls.pop();
23782             cls.push('left');
23783         }
23784         //this.picker().setXY(20000,20000);
23785         this.picker().addClass(cls.join('-'));
23786         
23787         var _this = this;
23788         
23789         Roo.each(cls, function(c){
23790             if(c == 'bottom'){
23791                 (function() {
23792                  //  
23793                 }).defer(200);
23794                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23795                 //_this.picker().setTop(_this.inputEl().getHeight());
23796                 return;
23797             }
23798             if(c == 'top'){
23799                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23800                 
23801                 //_this.picker().setTop(0 - _this.picker().getHeight());
23802                 return;
23803             }
23804             /*
23805             if(c == 'left'){
23806                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23807                 return;
23808             }
23809             if(c == 'right'){
23810                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23811                 return;
23812             }
23813             */
23814         });
23815         
23816     },
23817   
23818     onFocus : function()
23819     {
23820         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23821         this.show();
23822     },
23823     
23824     onBlur : function()
23825     {
23826         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23827         this.hide();
23828     },
23829     
23830     show : function()
23831     {
23832         this.picker().show();
23833         this.pop.show();
23834         this.update();
23835         this.place();
23836         
23837         this.fireEvent('show', this, this.date);
23838     },
23839     
23840     hide : function()
23841     {
23842         this.picker().hide();
23843         this.pop.hide();
23844         
23845         this.fireEvent('hide', this, this.date);
23846     },
23847     
23848     setTime : function()
23849     {
23850         this.hide();
23851         this.setValue(this.time.format(this.format));
23852         
23853         this.fireEvent('select', this, this.date);
23854         
23855         
23856     },
23857     
23858     onMousedown: function(e){
23859         e.stopPropagation();
23860         e.preventDefault();
23861     },
23862     
23863     onIncrementHours: function()
23864     {
23865         Roo.log('onIncrementHours');
23866         this.time = this.time.add(Date.HOUR, 1);
23867         this.update();
23868         
23869     },
23870     
23871     onDecrementHours: function()
23872     {
23873         Roo.log('onDecrementHours');
23874         this.time = this.time.add(Date.HOUR, -1);
23875         this.update();
23876     },
23877     
23878     onIncrementMinutes: function()
23879     {
23880         Roo.log('onIncrementMinutes');
23881         this.time = this.time.add(Date.MINUTE, 1);
23882         this.update();
23883     },
23884     
23885     onDecrementMinutes: function()
23886     {
23887         Roo.log('onDecrementMinutes');
23888         this.time = this.time.add(Date.MINUTE, -1);
23889         this.update();
23890     },
23891     
23892     onTogglePeriod: function()
23893     {
23894         Roo.log('onTogglePeriod');
23895         this.time = this.time.add(Date.HOUR, 12);
23896         this.update();
23897     }
23898     
23899    
23900 });
23901  
23902
23903 Roo.apply(Roo.bootstrap.TimeField,  {
23904   
23905     template : {
23906         tag: 'div',
23907         cls: 'datepicker dropdown-menu',
23908         cn: [
23909             {
23910                 tag: 'div',
23911                 cls: 'datepicker-time',
23912                 cn: [
23913                 {
23914                     tag: 'table',
23915                     cls: 'table-condensed',
23916                     cn:[
23917                         {
23918                             tag: 'tbody',
23919                             cn: [
23920                                 {
23921                                     tag: 'tr',
23922                                     cn: [
23923                                     {
23924                                         tag: 'td',
23925                                         colspan: '7'
23926                                     }
23927                                     ]
23928                                 }
23929                             ]
23930                         },
23931                         {
23932                             tag: 'tfoot',
23933                             cn: [
23934                                 {
23935                                     tag: 'tr',
23936                                     cn: [
23937                                     {
23938                                         tag: 'th',
23939                                         colspan: '7',
23940                                         cls: '',
23941                                         cn: [
23942                                             {
23943                                                 tag: 'button',
23944                                                 cls: 'btn btn-info ok',
23945                                                 html: 'OK'
23946                                             }
23947                                         ]
23948                                     }
23949                     
23950                                     ]
23951                                 }
23952                             ]
23953                         }
23954                     ]
23955                 }
23956                 ]
23957             }
23958         ]
23959     }
23960 });
23961
23962  
23963
23964  /*
23965  * - LGPL
23966  *
23967  * MonthField
23968  * 
23969  */
23970
23971 /**
23972  * @class Roo.bootstrap.MonthField
23973  * @extends Roo.bootstrap.Input
23974  * Bootstrap MonthField class
23975  * 
23976  * @cfg {String} language default en
23977  * 
23978  * @constructor
23979  * Create a new MonthField
23980  * @param {Object} config The config object
23981  */
23982
23983 Roo.bootstrap.MonthField = function(config){
23984     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23985     
23986     this.addEvents({
23987         /**
23988          * @event show
23989          * Fires when this field show.
23990          * @param {Roo.bootstrap.MonthField} this
23991          * @param {Mixed} date The date value
23992          */
23993         show : true,
23994         /**
23995          * @event show
23996          * Fires when this field hide.
23997          * @param {Roo.bootstrap.MonthField} this
23998          * @param {Mixed} date The date value
23999          */
24000         hide : true,
24001         /**
24002          * @event select
24003          * Fires when select a date.
24004          * @param {Roo.bootstrap.MonthField} this
24005          * @param {String} oldvalue The old value
24006          * @param {String} newvalue The new value
24007          */
24008         select : true
24009     });
24010 };
24011
24012 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24013     
24014     onRender: function(ct, position)
24015     {
24016         
24017         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24018         
24019         this.language = this.language || 'en';
24020         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24021         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24022         
24023         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24024         this.isInline = false;
24025         this.isInput = true;
24026         this.component = this.el.select('.add-on', true).first() || false;
24027         this.component = (this.component && this.component.length === 0) ? false : this.component;
24028         this.hasInput = this.component && this.inputEL().length;
24029         
24030         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24031         
24032         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24033         
24034         this.picker().on('mousedown', this.onMousedown, this);
24035         this.picker().on('click', this.onClick, this);
24036         
24037         this.picker().addClass('datepicker-dropdown');
24038         
24039         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24040             v.setStyle('width', '189px');
24041         });
24042         
24043         this.fillMonths();
24044         
24045         this.update();
24046         
24047         if(this.isInline) {
24048             this.show();
24049         }
24050         
24051     },
24052     
24053     setValue: function(v, suppressEvent)
24054     {   
24055         var o = this.getValue();
24056         
24057         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24058         
24059         this.update();
24060
24061         if(suppressEvent !== true){
24062             this.fireEvent('select', this, o, v);
24063         }
24064         
24065     },
24066     
24067     getValue: function()
24068     {
24069         return this.value;
24070     },
24071     
24072     onClick: function(e) 
24073     {
24074         e.stopPropagation();
24075         e.preventDefault();
24076         
24077         var target = e.getTarget();
24078         
24079         if(target.nodeName.toLowerCase() === 'i'){
24080             target = Roo.get(target).dom.parentNode;
24081         }
24082         
24083         var nodeName = target.nodeName;
24084         var className = target.className;
24085         var html = target.innerHTML;
24086         
24087         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24088             return;
24089         }
24090         
24091         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24092         
24093         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24094         
24095         this.hide();
24096                         
24097     },
24098     
24099     picker : function()
24100     {
24101         return this.pickerEl;
24102     },
24103     
24104     fillMonths: function()
24105     {    
24106         var i = 0;
24107         var months = this.picker().select('>.datepicker-months td', true).first();
24108         
24109         months.dom.innerHTML = '';
24110         
24111         while (i < 12) {
24112             var month = {
24113                 tag: 'span',
24114                 cls: 'month',
24115                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24116             };
24117             
24118             months.createChild(month);
24119         }
24120         
24121     },
24122     
24123     update: function()
24124     {
24125         var _this = this;
24126         
24127         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24128             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24129         }
24130         
24131         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24132             e.removeClass('active');
24133             
24134             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24135                 e.addClass('active');
24136             }
24137         })
24138     },
24139     
24140     place: function()
24141     {
24142         if(this.isInline) {
24143             return;
24144         }
24145         
24146         this.picker().removeClass(['bottom', 'top']);
24147         
24148         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24149             /*
24150              * place to the top of element!
24151              *
24152              */
24153             
24154             this.picker().addClass('top');
24155             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24156             
24157             return;
24158         }
24159         
24160         this.picker().addClass('bottom');
24161         
24162         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24163     },
24164     
24165     onFocus : function()
24166     {
24167         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24168         this.show();
24169     },
24170     
24171     onBlur : function()
24172     {
24173         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24174         
24175         var d = this.inputEl().getValue();
24176         
24177         this.setValue(d);
24178                 
24179         this.hide();
24180     },
24181     
24182     show : function()
24183     {
24184         this.picker().show();
24185         this.picker().select('>.datepicker-months', true).first().show();
24186         this.update();
24187         this.place();
24188         
24189         this.fireEvent('show', this, this.date);
24190     },
24191     
24192     hide : function()
24193     {
24194         if(this.isInline) {
24195             return;
24196         }
24197         this.picker().hide();
24198         this.fireEvent('hide', this, this.date);
24199         
24200     },
24201     
24202     onMousedown: function(e)
24203     {
24204         e.stopPropagation();
24205         e.preventDefault();
24206     },
24207     
24208     keyup: function(e)
24209     {
24210         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24211         this.update();
24212     },
24213
24214     fireKey: function(e)
24215     {
24216         if (!this.picker().isVisible()){
24217             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24218                 this.show();
24219             }
24220             return;
24221         }
24222         
24223         var dir;
24224         
24225         switch(e.keyCode){
24226             case 27: // escape
24227                 this.hide();
24228                 e.preventDefault();
24229                 break;
24230             case 37: // left
24231             case 39: // right
24232                 dir = e.keyCode == 37 ? -1 : 1;
24233                 
24234                 this.vIndex = this.vIndex + dir;
24235                 
24236                 if(this.vIndex < 0){
24237                     this.vIndex = 0;
24238                 }
24239                 
24240                 if(this.vIndex > 11){
24241                     this.vIndex = 11;
24242                 }
24243                 
24244                 if(isNaN(this.vIndex)){
24245                     this.vIndex = 0;
24246                 }
24247                 
24248                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24249                 
24250                 break;
24251             case 38: // up
24252             case 40: // down
24253                 
24254                 dir = e.keyCode == 38 ? -1 : 1;
24255                 
24256                 this.vIndex = this.vIndex + dir * 4;
24257                 
24258                 if(this.vIndex < 0){
24259                     this.vIndex = 0;
24260                 }
24261                 
24262                 if(this.vIndex > 11){
24263                     this.vIndex = 11;
24264                 }
24265                 
24266                 if(isNaN(this.vIndex)){
24267                     this.vIndex = 0;
24268                 }
24269                 
24270                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24271                 break;
24272                 
24273             case 13: // enter
24274                 
24275                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24276                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24277                 }
24278                 
24279                 this.hide();
24280                 e.preventDefault();
24281                 break;
24282             case 9: // tab
24283                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24284                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24285                 }
24286                 this.hide();
24287                 break;
24288             case 16: // shift
24289             case 17: // ctrl
24290             case 18: // alt
24291                 break;
24292             default :
24293                 this.hide();
24294                 
24295         }
24296     },
24297     
24298     remove: function() 
24299     {
24300         this.picker().remove();
24301     }
24302    
24303 });
24304
24305 Roo.apply(Roo.bootstrap.MonthField,  {
24306     
24307     content : {
24308         tag: 'tbody',
24309         cn: [
24310         {
24311             tag: 'tr',
24312             cn: [
24313             {
24314                 tag: 'td',
24315                 colspan: '7'
24316             }
24317             ]
24318         }
24319         ]
24320     },
24321     
24322     dates:{
24323         en: {
24324             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24325             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24326         }
24327     }
24328 });
24329
24330 Roo.apply(Roo.bootstrap.MonthField,  {
24331   
24332     template : {
24333         tag: 'div',
24334         cls: 'datepicker dropdown-menu roo-dynamic',
24335         cn: [
24336             {
24337                 tag: 'div',
24338                 cls: 'datepicker-months',
24339                 cn: [
24340                 {
24341                     tag: 'table',
24342                     cls: 'table-condensed',
24343                     cn:[
24344                         Roo.bootstrap.DateField.content
24345                     ]
24346                 }
24347                 ]
24348             }
24349         ]
24350     }
24351 });
24352
24353  
24354
24355  
24356  /*
24357  * - LGPL
24358  *
24359  * CheckBox
24360  * 
24361  */
24362
24363 /**
24364  * @class Roo.bootstrap.CheckBox
24365  * @extends Roo.bootstrap.Input
24366  * Bootstrap CheckBox class
24367  * 
24368  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24369  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24370  * @cfg {String} boxLabel The text that appears beside the checkbox
24371  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24372  * @cfg {Boolean} checked initnal the element
24373  * @cfg {Boolean} inline inline the element (default false)
24374  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24375  * @cfg {String} tooltip label tooltip
24376  * 
24377  * @constructor
24378  * Create a new CheckBox
24379  * @param {Object} config The config object
24380  */
24381
24382 Roo.bootstrap.CheckBox = function(config){
24383     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24384    
24385     this.addEvents({
24386         /**
24387         * @event check
24388         * Fires when the element is checked or unchecked.
24389         * @param {Roo.bootstrap.CheckBox} this This input
24390         * @param {Boolean} checked The new checked value
24391         */
24392        check : true,
24393        /**
24394         * @event click
24395         * Fires when the element is click.
24396         * @param {Roo.bootstrap.CheckBox} this This input
24397         */
24398        click : true
24399     });
24400     
24401 };
24402
24403 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24404   
24405     inputType: 'checkbox',
24406     inputValue: 1,
24407     valueOff: 0,
24408     boxLabel: false,
24409     checked: false,
24410     weight : false,
24411     inline: false,
24412     tooltip : '',
24413     
24414     // checkbox success does not make any sense really.. 
24415     invalidClass : "",
24416     validClass : "",
24417     
24418     
24419     getAutoCreate : function()
24420     {
24421         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24422         
24423         var id = Roo.id();
24424         
24425         var cfg = {};
24426         
24427         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24428         
24429         if(this.inline){
24430             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24431         }
24432         
24433         var input =  {
24434             tag: 'input',
24435             id : id,
24436             type : this.inputType,
24437             value : this.inputValue,
24438             cls : 'roo-' + this.inputType, //'form-box',
24439             placeholder : this.placeholder || ''
24440             
24441         };
24442         
24443         if(this.inputType != 'radio'){
24444             var hidden =  {
24445                 tag: 'input',
24446                 type : 'hidden',
24447                 cls : 'roo-hidden-value',
24448                 value : this.checked ? this.inputValue : this.valueOff
24449             };
24450         }
24451         
24452             
24453         if (this.weight) { // Validity check?
24454             cfg.cls += " " + this.inputType + "-" + this.weight;
24455         }
24456         
24457         if (this.disabled) {
24458             input.disabled=true;
24459         }
24460         
24461         if(this.checked){
24462             input.checked = this.checked;
24463         }
24464         
24465         if (this.name) {
24466             
24467             input.name = this.name;
24468             
24469             if(this.inputType != 'radio'){
24470                 hidden.name = this.name;
24471                 input.name = '_hidden_' + this.name;
24472             }
24473         }
24474         
24475         if (this.size) {
24476             input.cls += ' input-' + this.size;
24477         }
24478         
24479         var settings=this;
24480         
24481         ['xs','sm','md','lg'].map(function(size){
24482             if (settings[size]) {
24483                 cfg.cls += ' col-' + size + '-' + settings[size];
24484             }
24485         });
24486         
24487         var inputblock = input;
24488          
24489         if (this.before || this.after) {
24490             
24491             inputblock = {
24492                 cls : 'input-group',
24493                 cn :  [] 
24494             };
24495             
24496             if (this.before) {
24497                 inputblock.cn.push({
24498                     tag :'span',
24499                     cls : 'input-group-addon',
24500                     html : this.before
24501                 });
24502             }
24503             
24504             inputblock.cn.push(input);
24505             
24506             if(this.inputType != 'radio'){
24507                 inputblock.cn.push(hidden);
24508             }
24509             
24510             if (this.after) {
24511                 inputblock.cn.push({
24512                     tag :'span',
24513                     cls : 'input-group-addon',
24514                     html : this.after
24515                 });
24516             }
24517             
24518         }
24519         var boxLabelCfg = false;
24520         
24521         if(this.boxLabel){
24522            
24523             boxLabelCfg = {
24524                 tag: 'label',
24525                 //'for': id, // box label is handled by onclick - so no for...
24526                 cls: 'box-label',
24527                 html: this.boxLabel
24528             };
24529             if(this.tooltip){
24530                 boxLabelCfg.tooltip = this.tooltip;
24531             }
24532              
24533         }
24534         
24535         
24536         if (align ==='left' && this.fieldLabel.length) {
24537 //                Roo.log("left and has label");
24538             cfg.cn = [
24539                 {
24540                     tag: 'label',
24541                     'for' :  id,
24542                     cls : 'control-label',
24543                     html : this.fieldLabel
24544                 },
24545                 {
24546                     cls : "", 
24547                     cn: [
24548                         inputblock
24549                     ]
24550                 }
24551             ];
24552             
24553             if (boxLabelCfg) {
24554                 cfg.cn[1].cn.push(boxLabelCfg);
24555             }
24556             
24557             if(this.labelWidth > 12){
24558                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24559             }
24560             
24561             if(this.labelWidth < 13 && this.labelmd == 0){
24562                 this.labelmd = this.labelWidth;
24563             }
24564             
24565             if(this.labellg > 0){
24566                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24567                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24568             }
24569             
24570             if(this.labelmd > 0){
24571                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24572                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24573             }
24574             
24575             if(this.labelsm > 0){
24576                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24577                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24578             }
24579             
24580             if(this.labelxs > 0){
24581                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24582                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24583             }
24584             
24585         } else if ( this.fieldLabel.length) {
24586 //                Roo.log(" label");
24587                 cfg.cn = [
24588                    
24589                     {
24590                         tag: this.boxLabel ? 'span' : 'label',
24591                         'for': id,
24592                         cls: 'control-label box-input-label',
24593                         //cls : 'input-group-addon',
24594                         html : this.fieldLabel
24595                     },
24596                     
24597                     inputblock
24598                     
24599                 ];
24600                 if (boxLabelCfg) {
24601                     cfg.cn.push(boxLabelCfg);
24602                 }
24603
24604         } else {
24605             
24606 //                Roo.log(" no label && no align");
24607                 cfg.cn = [  inputblock ] ;
24608                 if (boxLabelCfg) {
24609                     cfg.cn.push(boxLabelCfg);
24610                 }
24611
24612                 
24613         }
24614         
24615        
24616         
24617         if(this.inputType != 'radio'){
24618             cfg.cn.push(hidden);
24619         }
24620         
24621         return cfg;
24622         
24623     },
24624     
24625     /**
24626      * return the real input element.
24627      */
24628     inputEl: function ()
24629     {
24630         return this.el.select('input.roo-' + this.inputType,true).first();
24631     },
24632     hiddenEl: function ()
24633     {
24634         return this.el.select('input.roo-hidden-value',true).first();
24635     },
24636     
24637     labelEl: function()
24638     {
24639         return this.el.select('label.control-label',true).first();
24640     },
24641     /* depricated... */
24642     
24643     label: function()
24644     {
24645         return this.labelEl();
24646     },
24647     
24648     boxLabelEl: function()
24649     {
24650         return this.el.select('label.box-label',true).first();
24651     },
24652     
24653     initEvents : function()
24654     {
24655 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24656         
24657         this.inputEl().on('click', this.onClick,  this);
24658         
24659         if (this.boxLabel) { 
24660             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24661         }
24662         
24663         this.startValue = this.getValue();
24664         
24665         if(this.groupId){
24666             Roo.bootstrap.CheckBox.register(this);
24667         }
24668     },
24669     
24670     onClick : function(e)
24671     {   
24672         if(this.fireEvent('click', this, e) !== false){
24673             this.setChecked(!this.checked);
24674         }
24675         
24676     },
24677     
24678     setChecked : function(state,suppressEvent)
24679     {
24680         this.startValue = this.getValue();
24681
24682         if(this.inputType == 'radio'){
24683             
24684             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24685                 e.dom.checked = false;
24686             });
24687             
24688             this.inputEl().dom.checked = true;
24689             
24690             this.inputEl().dom.value = this.inputValue;
24691             
24692             if(suppressEvent !== true){
24693                 this.fireEvent('check', this, true);
24694             }
24695             
24696             this.validate();
24697             
24698             return;
24699         }
24700         
24701         this.checked = state;
24702         
24703         this.inputEl().dom.checked = state;
24704         
24705         
24706         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24707         
24708         if(suppressEvent !== true){
24709             this.fireEvent('check', this, state);
24710         }
24711         
24712         this.validate();
24713     },
24714     
24715     getValue : function()
24716     {
24717         if(this.inputType == 'radio'){
24718             return this.getGroupValue();
24719         }
24720         
24721         return this.hiddenEl().dom.value;
24722         
24723     },
24724     
24725     getGroupValue : function()
24726     {
24727         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24728             return '';
24729         }
24730         
24731         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24732     },
24733     
24734     setValue : function(v,suppressEvent)
24735     {
24736         if(this.inputType == 'radio'){
24737             this.setGroupValue(v, suppressEvent);
24738             return;
24739         }
24740         
24741         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24742         
24743         this.validate();
24744     },
24745     
24746     setGroupValue : function(v, suppressEvent)
24747     {
24748         this.startValue = this.getValue();
24749         
24750         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24751             e.dom.checked = false;
24752             
24753             if(e.dom.value == v){
24754                 e.dom.checked = true;
24755             }
24756         });
24757         
24758         if(suppressEvent !== true){
24759             this.fireEvent('check', this, true);
24760         }
24761
24762         this.validate();
24763         
24764         return;
24765     },
24766     
24767     validate : function()
24768     {
24769         if(this.getVisibilityEl().hasClass('hidden')){
24770             return true;
24771         }
24772         
24773         if(
24774                 this.disabled || 
24775                 (this.inputType == 'radio' && this.validateRadio()) ||
24776                 (this.inputType == 'checkbox' && this.validateCheckbox())
24777         ){
24778             this.markValid();
24779             return true;
24780         }
24781         
24782         this.markInvalid();
24783         return false;
24784     },
24785     
24786     validateRadio : function()
24787     {
24788         if(this.getVisibilityEl().hasClass('hidden')){
24789             return true;
24790         }
24791         
24792         if(this.allowBlank){
24793             return true;
24794         }
24795         
24796         var valid = false;
24797         
24798         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24799             if(!e.dom.checked){
24800                 return;
24801             }
24802             
24803             valid = true;
24804             
24805             return false;
24806         });
24807         
24808         return valid;
24809     },
24810     
24811     validateCheckbox : function()
24812     {
24813         if(!this.groupId){
24814             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24815             //return (this.getValue() == this.inputValue) ? true : false;
24816         }
24817         
24818         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24819         
24820         if(!group){
24821             return false;
24822         }
24823         
24824         var r = false;
24825         
24826         for(var i in group){
24827             if(group[i].el.isVisible(true)){
24828                 r = false;
24829                 break;
24830             }
24831             
24832             r = true;
24833         }
24834         
24835         for(var i in group){
24836             if(r){
24837                 break;
24838             }
24839             
24840             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24841         }
24842         
24843         return r;
24844     },
24845     
24846     /**
24847      * Mark this field as valid
24848      */
24849     markValid : function()
24850     {
24851         var _this = this;
24852         
24853         this.fireEvent('valid', this);
24854         
24855         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24856         
24857         if(this.groupId){
24858             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24859         }
24860         
24861         if(label){
24862             label.markValid();
24863         }
24864
24865         if(this.inputType == 'radio'){
24866             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24867                 var fg = e.findParent('.form-group', false, true);
24868                 if (Roo.bootstrap.version == 3) {
24869                     fg.removeClass([_this.invalidClass, _this.validClass]);
24870                     fg.addClass(_this.validClass);
24871                 } else {
24872                     fg.removeClass(['is-valid', 'is-invalid']);
24873                     fg.addClass('is-valid');
24874                 }
24875             });
24876             
24877             return;
24878         }
24879
24880         if(!this.groupId){
24881             var fg = this.el.findParent('.form-group', false, true);
24882             if (Roo.bootstrap.version == 3) {
24883                 fg.removeClass([this.invalidClass, this.validClass]);
24884                 fg.addClass(this.validClass);
24885             } else {
24886                 fg.removeClass(['is-valid', 'is-invalid']);
24887                 fg.addClass('is-valid');
24888             }
24889             return;
24890         }
24891         
24892         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24893         
24894         if(!group){
24895             return;
24896         }
24897         
24898         for(var i in group){
24899             var fg = group[i].el.findParent('.form-group', false, true);
24900             if (Roo.bootstrap.version == 3) {
24901                 fg.removeClass([this.invalidClass, this.validClass]);
24902                 fg.addClass(this.validClass);
24903             } else {
24904                 fg.removeClass(['is-valid', 'is-invalid']);
24905                 fg.addClass('is-valid');
24906             }
24907         }
24908     },
24909     
24910      /**
24911      * Mark this field as invalid
24912      * @param {String} msg The validation message
24913      */
24914     markInvalid : function(msg)
24915     {
24916         if(this.allowBlank){
24917             return;
24918         }
24919         
24920         var _this = this;
24921         
24922         this.fireEvent('invalid', this, msg);
24923         
24924         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24925         
24926         if(this.groupId){
24927             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24928         }
24929         
24930         if(label){
24931             label.markInvalid();
24932         }
24933             
24934         if(this.inputType == 'radio'){
24935             
24936             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24937                 var fg = e.findParent('.form-group', false, true);
24938                 if (Roo.bootstrap.version == 3) {
24939                     fg.removeClass([_this.invalidClass, _this.validClass]);
24940                     fg.addClass(_this.invalidClass);
24941                 } else {
24942                     fg.removeClass(['is-invalid', 'is-valid']);
24943                     fg.addClass('is-invalid');
24944                 }
24945             });
24946             
24947             return;
24948         }
24949         
24950         if(!this.groupId){
24951             var fg = this.el.findParent('.form-group', false, true);
24952             if (Roo.bootstrap.version == 3) {
24953                 fg.removeClass([_this.invalidClass, _this.validClass]);
24954                 fg.addClass(_this.invalidClass);
24955             } else {
24956                 fg.removeClass(['is-invalid', 'is-valid']);
24957                 fg.addClass('is-invalid');
24958             }
24959             return;
24960         }
24961         
24962         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24963         
24964         if(!group){
24965             return;
24966         }
24967         
24968         for(var i in group){
24969             var fg = group[i].el.findParent('.form-group', false, true);
24970             if (Roo.bootstrap.version == 3) {
24971                 fg.removeClass([_this.invalidClass, _this.validClass]);
24972                 fg.addClass(_this.invalidClass);
24973             } else {
24974                 fg.removeClass(['is-invalid', 'is-valid']);
24975                 fg.addClass('is-invalid');
24976             }
24977         }
24978         
24979     },
24980     
24981     clearInvalid : function()
24982     {
24983         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24984         
24985         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24986         
24987         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24988         
24989         if (label && label.iconEl) {
24990             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24991             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24992         }
24993     },
24994     
24995     disable : function()
24996     {
24997         if(this.inputType != 'radio'){
24998             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24999             return;
25000         }
25001         
25002         var _this = this;
25003         
25004         if(this.rendered){
25005             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25006                 _this.getActionEl().addClass(this.disabledClass);
25007                 e.dom.disabled = true;
25008             });
25009         }
25010         
25011         this.disabled = true;
25012         this.fireEvent("disable", this);
25013         return this;
25014     },
25015
25016     enable : function()
25017     {
25018         if(this.inputType != 'radio'){
25019             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25020             return;
25021         }
25022         
25023         var _this = this;
25024         
25025         if(this.rendered){
25026             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25027                 _this.getActionEl().removeClass(this.disabledClass);
25028                 e.dom.disabled = false;
25029             });
25030         }
25031         
25032         this.disabled = false;
25033         this.fireEvent("enable", this);
25034         return this;
25035     },
25036     
25037     setBoxLabel : function(v)
25038     {
25039         this.boxLabel = v;
25040         
25041         if(this.rendered){
25042             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25043         }
25044     }
25045
25046 });
25047
25048 Roo.apply(Roo.bootstrap.CheckBox, {
25049     
25050     groups: {},
25051     
25052      /**
25053     * register a CheckBox Group
25054     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25055     */
25056     register : function(checkbox)
25057     {
25058         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25059             this.groups[checkbox.groupId] = {};
25060         }
25061         
25062         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25063             return;
25064         }
25065         
25066         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25067         
25068     },
25069     /**
25070     * fetch a CheckBox Group based on the group ID
25071     * @param {string} the group ID
25072     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25073     */
25074     get: function(groupId) {
25075         if (typeof(this.groups[groupId]) == 'undefined') {
25076             return false;
25077         }
25078         
25079         return this.groups[groupId] ;
25080     }
25081     
25082     
25083 });
25084 /*
25085  * - LGPL
25086  *
25087  * RadioItem
25088  * 
25089  */
25090
25091 /**
25092  * @class Roo.bootstrap.Radio
25093  * @extends Roo.bootstrap.Component
25094  * Bootstrap Radio class
25095  * @cfg {String} boxLabel - the label associated
25096  * @cfg {String} value - the value of radio
25097  * 
25098  * @constructor
25099  * Create a new Radio
25100  * @param {Object} config The config object
25101  */
25102 Roo.bootstrap.Radio = function(config){
25103     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25104     
25105 };
25106
25107 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25108     
25109     boxLabel : '',
25110     
25111     value : '',
25112     
25113     getAutoCreate : function()
25114     {
25115         var cfg = {
25116             tag : 'div',
25117             cls : 'form-group radio',
25118             cn : [
25119                 {
25120                     tag : 'label',
25121                     cls : 'box-label',
25122                     html : this.boxLabel
25123                 }
25124             ]
25125         };
25126         
25127         return cfg;
25128     },
25129     
25130     initEvents : function() 
25131     {
25132         this.parent().register(this);
25133         
25134         this.el.on('click', this.onClick, this);
25135         
25136     },
25137     
25138     onClick : function(e)
25139     {
25140         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25141             this.setChecked(true);
25142         }
25143     },
25144     
25145     setChecked : function(state, suppressEvent)
25146     {
25147         this.parent().setValue(this.value, suppressEvent);
25148         
25149     },
25150     
25151     setBoxLabel : function(v)
25152     {
25153         this.boxLabel = v;
25154         
25155         if(this.rendered){
25156             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25157         }
25158     }
25159     
25160 });
25161  
25162
25163  /*
25164  * - LGPL
25165  *
25166  * Input
25167  * 
25168  */
25169
25170 /**
25171  * @class Roo.bootstrap.SecurePass
25172  * @extends Roo.bootstrap.Input
25173  * Bootstrap SecurePass class
25174  *
25175  * 
25176  * @constructor
25177  * Create a new SecurePass
25178  * @param {Object} config The config object
25179  */
25180  
25181 Roo.bootstrap.SecurePass = function (config) {
25182     // these go here, so the translation tool can replace them..
25183     this.errors = {
25184         PwdEmpty: "Please type a password, and then retype it to confirm.",
25185         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25186         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25187         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25188         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25189         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25190         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25191         TooWeak: "Your password is Too Weak."
25192     },
25193     this.meterLabel = "Password strength:";
25194     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25195     this.meterClass = [
25196         "roo-password-meter-tooweak", 
25197         "roo-password-meter-weak", 
25198         "roo-password-meter-medium", 
25199         "roo-password-meter-strong", 
25200         "roo-password-meter-grey"
25201     ];
25202     
25203     this.errors = {};
25204     
25205     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25206 }
25207
25208 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25209     /**
25210      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25211      * {
25212      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25213      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25214      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25215      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25216      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25217      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25218      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25219      * })
25220      */
25221     // private
25222     
25223     meterWidth: 300,
25224     errorMsg :'',    
25225     errors: false,
25226     imageRoot: '/',
25227     /**
25228      * @cfg {String/Object} Label for the strength meter (defaults to
25229      * 'Password strength:')
25230      */
25231     // private
25232     meterLabel: '',
25233     /**
25234      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25235      * ['Weak', 'Medium', 'Strong'])
25236      */
25237     // private    
25238     pwdStrengths: false,    
25239     // private
25240     strength: 0,
25241     // private
25242     _lastPwd: null,
25243     // private
25244     kCapitalLetter: 0,
25245     kSmallLetter: 1,
25246     kDigit: 2,
25247     kPunctuation: 3,
25248     
25249     insecure: false,
25250     // private
25251     initEvents: function ()
25252     {
25253         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25254
25255         if (this.el.is('input[type=password]') && Roo.isSafari) {
25256             this.el.on('keydown', this.SafariOnKeyDown, this);
25257         }
25258
25259         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25260     },
25261     // private
25262     onRender: function (ct, position)
25263     {
25264         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25265         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25266         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25267
25268         this.trigger.createChild({
25269                    cn: [
25270                     {
25271                     //id: 'PwdMeter',
25272                     tag: 'div',
25273                     cls: 'roo-password-meter-grey col-xs-12',
25274                     style: {
25275                         //width: 0,
25276                         //width: this.meterWidth + 'px'                                                
25277                         }
25278                     },
25279                     {                            
25280                          cls: 'roo-password-meter-text'                          
25281                     }
25282                 ]            
25283         });
25284
25285          
25286         if (this.hideTrigger) {
25287             this.trigger.setDisplayed(false);
25288         }
25289         this.setSize(this.width || '', this.height || '');
25290     },
25291     // private
25292     onDestroy: function ()
25293     {
25294         if (this.trigger) {
25295             this.trigger.removeAllListeners();
25296             this.trigger.remove();
25297         }
25298         if (this.wrap) {
25299             this.wrap.remove();
25300         }
25301         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25302     },
25303     // private
25304     checkStrength: function ()
25305     {
25306         var pwd = this.inputEl().getValue();
25307         if (pwd == this._lastPwd) {
25308             return;
25309         }
25310
25311         var strength;
25312         if (this.ClientSideStrongPassword(pwd)) {
25313             strength = 3;
25314         } else if (this.ClientSideMediumPassword(pwd)) {
25315             strength = 2;
25316         } else if (this.ClientSideWeakPassword(pwd)) {
25317             strength = 1;
25318         } else {
25319             strength = 0;
25320         }
25321         
25322         Roo.log('strength1: ' + strength);
25323         
25324         //var pm = this.trigger.child('div/div/div').dom;
25325         var pm = this.trigger.child('div/div');
25326         pm.removeClass(this.meterClass);
25327         pm.addClass(this.meterClass[strength]);
25328                 
25329         
25330         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25331                 
25332         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25333         
25334         this._lastPwd = pwd;
25335     },
25336     reset: function ()
25337     {
25338         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25339         
25340         this._lastPwd = '';
25341         
25342         var pm = this.trigger.child('div/div');
25343         pm.removeClass(this.meterClass);
25344         pm.addClass('roo-password-meter-grey');        
25345         
25346         
25347         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25348         
25349         pt.innerHTML = '';
25350         this.inputEl().dom.type='password';
25351     },
25352     // private
25353     validateValue: function (value)
25354     {
25355         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25356             return false;
25357         }
25358         if (value.length == 0) {
25359             if (this.allowBlank) {
25360                 this.clearInvalid();
25361                 return true;
25362             }
25363
25364             this.markInvalid(this.errors.PwdEmpty);
25365             this.errorMsg = this.errors.PwdEmpty;
25366             return false;
25367         }
25368         
25369         if(this.insecure){
25370             return true;
25371         }
25372         
25373         if (!value.match(/[\x21-\x7e]+/)) {
25374             this.markInvalid(this.errors.PwdBadChar);
25375             this.errorMsg = this.errors.PwdBadChar;
25376             return false;
25377         }
25378         if (value.length < 6) {
25379             this.markInvalid(this.errors.PwdShort);
25380             this.errorMsg = this.errors.PwdShort;
25381             return false;
25382         }
25383         if (value.length > 16) {
25384             this.markInvalid(this.errors.PwdLong);
25385             this.errorMsg = this.errors.PwdLong;
25386             return false;
25387         }
25388         var strength;
25389         if (this.ClientSideStrongPassword(value)) {
25390             strength = 3;
25391         } else if (this.ClientSideMediumPassword(value)) {
25392             strength = 2;
25393         } else if (this.ClientSideWeakPassword(value)) {
25394             strength = 1;
25395         } else {
25396             strength = 0;
25397         }
25398
25399         
25400         if (strength < 2) {
25401             //this.markInvalid(this.errors.TooWeak);
25402             this.errorMsg = this.errors.TooWeak;
25403             //return false;
25404         }
25405         
25406         
25407         console.log('strength2: ' + strength);
25408         
25409         //var pm = this.trigger.child('div/div/div').dom;
25410         
25411         var pm = this.trigger.child('div/div');
25412         pm.removeClass(this.meterClass);
25413         pm.addClass(this.meterClass[strength]);
25414                 
25415         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25416                 
25417         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25418         
25419         this.errorMsg = ''; 
25420         return true;
25421     },
25422     // private
25423     CharacterSetChecks: function (type)
25424     {
25425         this.type = type;
25426         this.fResult = false;
25427     },
25428     // private
25429     isctype: function (character, type)
25430     {
25431         switch (type) {  
25432             case this.kCapitalLetter:
25433                 if (character >= 'A' && character <= 'Z') {
25434                     return true;
25435                 }
25436                 break;
25437             
25438             case this.kSmallLetter:
25439                 if (character >= 'a' && character <= 'z') {
25440                     return true;
25441                 }
25442                 break;
25443             
25444             case this.kDigit:
25445                 if (character >= '0' && character <= '9') {
25446                     return true;
25447                 }
25448                 break;
25449             
25450             case this.kPunctuation:
25451                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25452                     return true;
25453                 }
25454                 break;
25455             
25456             default:
25457                 return false;
25458         }
25459
25460     },
25461     // private
25462     IsLongEnough: function (pwd, size)
25463     {
25464         return !(pwd == null || isNaN(size) || pwd.length < size);
25465     },
25466     // private
25467     SpansEnoughCharacterSets: function (word, nb)
25468     {
25469         if (!this.IsLongEnough(word, nb))
25470         {
25471             return false;
25472         }
25473
25474         var characterSetChecks = new Array(
25475             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25476             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25477         );
25478         
25479         for (var index = 0; index < word.length; ++index) {
25480             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25481                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25482                     characterSetChecks[nCharSet].fResult = true;
25483                     break;
25484                 }
25485             }
25486         }
25487
25488         var nCharSets = 0;
25489         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25490             if (characterSetChecks[nCharSet].fResult) {
25491                 ++nCharSets;
25492             }
25493         }
25494
25495         if (nCharSets < nb) {
25496             return false;
25497         }
25498         return true;
25499     },
25500     // private
25501     ClientSideStrongPassword: function (pwd)
25502     {
25503         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25504     },
25505     // private
25506     ClientSideMediumPassword: function (pwd)
25507     {
25508         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25509     },
25510     // private
25511     ClientSideWeakPassword: function (pwd)
25512     {
25513         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25514     }
25515           
25516 })//<script type="text/javascript">
25517
25518 /*
25519  * Based  Ext JS Library 1.1.1
25520  * Copyright(c) 2006-2007, Ext JS, LLC.
25521  * LGPL
25522  *
25523  */
25524  
25525 /**
25526  * @class Roo.HtmlEditorCore
25527  * @extends Roo.Component
25528  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25529  *
25530  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25531  */
25532
25533 Roo.HtmlEditorCore = function(config){
25534     
25535     
25536     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25537     
25538     
25539     this.addEvents({
25540         /**
25541          * @event initialize
25542          * Fires when the editor is fully initialized (including the iframe)
25543          * @param {Roo.HtmlEditorCore} this
25544          */
25545         initialize: true,
25546         /**
25547          * @event activate
25548          * Fires when the editor is first receives the focus. Any insertion must wait
25549          * until after this event.
25550          * @param {Roo.HtmlEditorCore} this
25551          */
25552         activate: true,
25553          /**
25554          * @event beforesync
25555          * Fires before the textarea is updated with content from the editor iframe. Return false
25556          * to cancel the sync.
25557          * @param {Roo.HtmlEditorCore} this
25558          * @param {String} html
25559          */
25560         beforesync: true,
25561          /**
25562          * @event beforepush
25563          * Fires before the iframe editor is updated with content from the textarea. Return false
25564          * to cancel the push.
25565          * @param {Roo.HtmlEditorCore} this
25566          * @param {String} html
25567          */
25568         beforepush: true,
25569          /**
25570          * @event sync
25571          * Fires when the textarea is updated with content from the editor iframe.
25572          * @param {Roo.HtmlEditorCore} this
25573          * @param {String} html
25574          */
25575         sync: true,
25576          /**
25577          * @event push
25578          * Fires when the iframe editor is updated with content from the textarea.
25579          * @param {Roo.HtmlEditorCore} this
25580          * @param {String} html
25581          */
25582         push: true,
25583         
25584         /**
25585          * @event editorevent
25586          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25587          * @param {Roo.HtmlEditorCore} this
25588          */
25589         editorevent: true
25590         
25591     });
25592     
25593     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25594     
25595     // defaults : white / black...
25596     this.applyBlacklists();
25597     
25598     
25599     
25600 };
25601
25602
25603 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25604
25605
25606      /**
25607      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25608      */
25609     
25610     owner : false,
25611     
25612      /**
25613      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25614      *                        Roo.resizable.
25615      */
25616     resizable : false,
25617      /**
25618      * @cfg {Number} height (in pixels)
25619      */   
25620     height: 300,
25621    /**
25622      * @cfg {Number} width (in pixels)
25623      */   
25624     width: 500,
25625     
25626     /**
25627      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25628      * 
25629      */
25630     stylesheets: false,
25631     
25632     /**
25633      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25634      */
25635     allowComments: false,
25636     // id of frame..
25637     frameId: false,
25638     
25639     // private properties
25640     validationEvent : false,
25641     deferHeight: true,
25642     initialized : false,
25643     activated : false,
25644     sourceEditMode : false,
25645     onFocus : Roo.emptyFn,
25646     iframePad:3,
25647     hideMode:'offsets',
25648     
25649     clearUp: true,
25650     
25651     // blacklist + whitelisted elements..
25652     black: false,
25653     white: false,
25654      
25655     bodyCls : '',
25656
25657     /**
25658      * Protected method that will not generally be called directly. It
25659      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25660      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25661      */
25662     getDocMarkup : function(){
25663         // body styles..
25664         var st = '';
25665         
25666         // inherit styels from page...?? 
25667         if (this.stylesheets === false) {
25668             
25669             Roo.get(document.head).select('style').each(function(node) {
25670                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25671             });
25672             
25673             Roo.get(document.head).select('link').each(function(node) { 
25674                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25675             });
25676             
25677         } else if (!this.stylesheets.length) {
25678                 // simple..
25679                 st = '<style type="text/css">' +
25680                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25681                    '</style>';
25682         } else {
25683             for (var i in this.stylesheets) { 
25684                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25685             }
25686             
25687         }
25688         
25689         st +=  '<style type="text/css">' +
25690             'IMG { cursor: pointer } ' +
25691         '</style>';
25692
25693         var cls = 'roo-htmleditor-body';
25694         
25695         if(this.bodyCls.length){
25696             cls += ' ' + this.bodyCls;
25697         }
25698         
25699         return '<html><head>' + st  +
25700             //<style type="text/css">' +
25701             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25702             //'</style>' +
25703             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25704     },
25705
25706     // private
25707     onRender : function(ct, position)
25708     {
25709         var _t = this;
25710         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25711         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25712         
25713         
25714         this.el.dom.style.border = '0 none';
25715         this.el.dom.setAttribute('tabIndex', -1);
25716         this.el.addClass('x-hidden hide');
25717         
25718         
25719         
25720         if(Roo.isIE){ // fix IE 1px bogus margin
25721             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25722         }
25723        
25724         
25725         this.frameId = Roo.id();
25726         
25727          
25728         
25729         var iframe = this.owner.wrap.createChild({
25730             tag: 'iframe',
25731             cls: 'form-control', // bootstrap..
25732             id: this.frameId,
25733             name: this.frameId,
25734             frameBorder : 'no',
25735             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25736         }, this.el
25737         );
25738         
25739         
25740         this.iframe = iframe.dom;
25741
25742          this.assignDocWin();
25743         
25744         this.doc.designMode = 'on';
25745        
25746         this.doc.open();
25747         this.doc.write(this.getDocMarkup());
25748         this.doc.close();
25749
25750         
25751         var task = { // must defer to wait for browser to be ready
25752             run : function(){
25753                 //console.log("run task?" + this.doc.readyState);
25754                 this.assignDocWin();
25755                 if(this.doc.body || this.doc.readyState == 'complete'){
25756                     try {
25757                         this.doc.designMode="on";
25758                     } catch (e) {
25759                         return;
25760                     }
25761                     Roo.TaskMgr.stop(task);
25762                     this.initEditor.defer(10, this);
25763                 }
25764             },
25765             interval : 10,
25766             duration: 10000,
25767             scope: this
25768         };
25769         Roo.TaskMgr.start(task);
25770
25771     },
25772
25773     // private
25774     onResize : function(w, h)
25775     {
25776          Roo.log('resize: ' +w + ',' + h );
25777         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25778         if(!this.iframe){
25779             return;
25780         }
25781         if(typeof w == 'number'){
25782             
25783             this.iframe.style.width = w + 'px';
25784         }
25785         if(typeof h == 'number'){
25786             
25787             this.iframe.style.height = h + 'px';
25788             if(this.doc){
25789                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25790             }
25791         }
25792         
25793     },
25794
25795     /**
25796      * Toggles the editor between standard and source edit mode.
25797      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25798      */
25799     toggleSourceEdit : function(sourceEditMode){
25800         
25801         this.sourceEditMode = sourceEditMode === true;
25802         
25803         if(this.sourceEditMode){
25804  
25805             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25806             
25807         }else{
25808             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25809             //this.iframe.className = '';
25810             this.deferFocus();
25811         }
25812         //this.setSize(this.owner.wrap.getSize());
25813         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25814     },
25815
25816     
25817   
25818
25819     /**
25820      * Protected method that will not generally be called directly. If you need/want
25821      * custom HTML cleanup, this is the method you should override.
25822      * @param {String} html The HTML to be cleaned
25823      * return {String} The cleaned HTML
25824      */
25825     cleanHtml : function(html){
25826         html = String(html);
25827         if(html.length > 5){
25828             if(Roo.isSafari){ // strip safari nonsense
25829                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25830             }
25831         }
25832         if(html == '&nbsp;'){
25833             html = '';
25834         }
25835         return html;
25836     },
25837
25838     /**
25839      * HTML Editor -> Textarea
25840      * Protected method that will not generally be called directly. Syncs the contents
25841      * of the editor iframe with the textarea.
25842      */
25843     syncValue : function(){
25844         if(this.initialized){
25845             var bd = (this.doc.body || this.doc.documentElement);
25846             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25847             var html = bd.innerHTML;
25848             if(Roo.isSafari){
25849                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25850                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25851                 if(m && m[1]){
25852                     html = '<div style="'+m[0]+'">' + html + '</div>';
25853                 }
25854             }
25855             html = this.cleanHtml(html);
25856             // fix up the special chars.. normaly like back quotes in word...
25857             // however we do not want to do this with chinese..
25858             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25859                 
25860                 var cc = match.charCodeAt();
25861
25862                 // Get the character value, handling surrogate pairs
25863                 if (match.length == 2) {
25864                     // It's a surrogate pair, calculate the Unicode code point
25865                     var high = match.charCodeAt(0) - 0xD800;
25866                     var low  = match.charCodeAt(1) - 0xDC00;
25867                     cc = (high * 0x400) + low + 0x10000;
25868                 }  else if (
25869                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25870                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25871                     (cc >= 0xf900 && cc < 0xfb00 )
25872                 ) {
25873                         return match;
25874                 }  
25875          
25876                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25877                 return "&#" + cc + ";";
25878                 
25879                 
25880             });
25881             
25882             
25883              
25884             if(this.owner.fireEvent('beforesync', this, html) !== false){
25885                 this.el.dom.value = html;
25886                 this.owner.fireEvent('sync', this, html);
25887             }
25888         }
25889     },
25890
25891     /**
25892      * Protected method that will not generally be called directly. Pushes the value of the textarea
25893      * into the iframe editor.
25894      */
25895     pushValue : function(){
25896         if(this.initialized){
25897             var v = this.el.dom.value.trim();
25898             
25899 //            if(v.length < 1){
25900 //                v = '&#160;';
25901 //            }
25902             
25903             if(this.owner.fireEvent('beforepush', this, v) !== false){
25904                 var d = (this.doc.body || this.doc.documentElement);
25905                 d.innerHTML = v;
25906                 this.cleanUpPaste();
25907                 this.el.dom.value = d.innerHTML;
25908                 this.owner.fireEvent('push', this, v);
25909             }
25910         }
25911     },
25912
25913     // private
25914     deferFocus : function(){
25915         this.focus.defer(10, this);
25916     },
25917
25918     // doc'ed in Field
25919     focus : function(){
25920         if(this.win && !this.sourceEditMode){
25921             this.win.focus();
25922         }else{
25923             this.el.focus();
25924         }
25925     },
25926     
25927     assignDocWin: function()
25928     {
25929         var iframe = this.iframe;
25930         
25931          if(Roo.isIE){
25932             this.doc = iframe.contentWindow.document;
25933             this.win = iframe.contentWindow;
25934         } else {
25935 //            if (!Roo.get(this.frameId)) {
25936 //                return;
25937 //            }
25938 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25939 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25940             
25941             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25942                 return;
25943             }
25944             
25945             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25946             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25947         }
25948     },
25949     
25950     // private
25951     initEditor : function(){
25952         //console.log("INIT EDITOR");
25953         this.assignDocWin();
25954         
25955         
25956         
25957         this.doc.designMode="on";
25958         this.doc.open();
25959         this.doc.write(this.getDocMarkup());
25960         this.doc.close();
25961         
25962         var dbody = (this.doc.body || this.doc.documentElement);
25963         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25964         // this copies styles from the containing element into thsi one..
25965         // not sure why we need all of this..
25966         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25967         
25968         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25969         //ss['background-attachment'] = 'fixed'; // w3c
25970         dbody.bgProperties = 'fixed'; // ie
25971         //Roo.DomHelper.applyStyles(dbody, ss);
25972         Roo.EventManager.on(this.doc, {
25973             //'mousedown': this.onEditorEvent,
25974             'mouseup': this.onEditorEvent,
25975             'dblclick': this.onEditorEvent,
25976             'click': this.onEditorEvent,
25977             'keyup': this.onEditorEvent,
25978             buffer:100,
25979             scope: this
25980         });
25981         if(Roo.isGecko){
25982             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25983         }
25984         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25985             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25986         }
25987         this.initialized = true;
25988
25989         this.owner.fireEvent('initialize', this);
25990         this.pushValue();
25991     },
25992
25993     // private
25994     onDestroy : function(){
25995         
25996         
25997         
25998         if(this.rendered){
25999             
26000             //for (var i =0; i < this.toolbars.length;i++) {
26001             //    // fixme - ask toolbars for heights?
26002             //    this.toolbars[i].onDestroy();
26003            // }
26004             
26005             //this.wrap.dom.innerHTML = '';
26006             //this.wrap.remove();
26007         }
26008     },
26009
26010     // private
26011     onFirstFocus : function(){
26012         
26013         this.assignDocWin();
26014         
26015         
26016         this.activated = true;
26017          
26018     
26019         if(Roo.isGecko){ // prevent silly gecko errors
26020             this.win.focus();
26021             var s = this.win.getSelection();
26022             if(!s.focusNode || s.focusNode.nodeType != 3){
26023                 var r = s.getRangeAt(0);
26024                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26025                 r.collapse(true);
26026                 this.deferFocus();
26027             }
26028             try{
26029                 this.execCmd('useCSS', true);
26030                 this.execCmd('styleWithCSS', false);
26031             }catch(e){}
26032         }
26033         this.owner.fireEvent('activate', this);
26034     },
26035
26036     // private
26037     adjustFont: function(btn){
26038         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26039         //if(Roo.isSafari){ // safari
26040         //    adjust *= 2;
26041        // }
26042         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26043         if(Roo.isSafari){ // safari
26044             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26045             v =  (v < 10) ? 10 : v;
26046             v =  (v > 48) ? 48 : v;
26047             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26048             
26049         }
26050         
26051         
26052         v = Math.max(1, v+adjust);
26053         
26054         this.execCmd('FontSize', v  );
26055     },
26056
26057     onEditorEvent : function(e)
26058     {
26059         this.owner.fireEvent('editorevent', this, e);
26060       //  this.updateToolbar();
26061         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26062     },
26063
26064     insertTag : function(tg)
26065     {
26066         // could be a bit smarter... -> wrap the current selected tRoo..
26067         if (tg.toLowerCase() == 'span' ||
26068             tg.toLowerCase() == 'code' ||
26069             tg.toLowerCase() == 'sup' ||
26070             tg.toLowerCase() == 'sub' 
26071             ) {
26072             
26073             range = this.createRange(this.getSelection());
26074             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26075             wrappingNode.appendChild(range.extractContents());
26076             range.insertNode(wrappingNode);
26077
26078             return;
26079             
26080             
26081             
26082         }
26083         this.execCmd("formatblock",   tg);
26084         
26085     },
26086     
26087     insertText : function(txt)
26088     {
26089         
26090         
26091         var range = this.createRange();
26092         range.deleteContents();
26093                //alert(Sender.getAttribute('label'));
26094                
26095         range.insertNode(this.doc.createTextNode(txt));
26096     } ,
26097     
26098      
26099
26100     /**
26101      * Executes a Midas editor command on the editor document and performs necessary focus and
26102      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26103      * @param {String} cmd The Midas command
26104      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26105      */
26106     relayCmd : function(cmd, value){
26107         this.win.focus();
26108         this.execCmd(cmd, value);
26109         this.owner.fireEvent('editorevent', this);
26110         //this.updateToolbar();
26111         this.owner.deferFocus();
26112     },
26113
26114     /**
26115      * Executes a Midas editor command directly on the editor document.
26116      * For visual commands, you should use {@link #relayCmd} instead.
26117      * <b>This should only be called after the editor is initialized.</b>
26118      * @param {String} cmd The Midas command
26119      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26120      */
26121     execCmd : function(cmd, value){
26122         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26123         this.syncValue();
26124     },
26125  
26126  
26127    
26128     /**
26129      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26130      * to insert tRoo.
26131      * @param {String} text | dom node.. 
26132      */
26133     insertAtCursor : function(text)
26134     {
26135         
26136         if(!this.activated){
26137             return;
26138         }
26139         /*
26140         if(Roo.isIE){
26141             this.win.focus();
26142             var r = this.doc.selection.createRange();
26143             if(r){
26144                 r.collapse(true);
26145                 r.pasteHTML(text);
26146                 this.syncValue();
26147                 this.deferFocus();
26148             
26149             }
26150             return;
26151         }
26152         */
26153         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26154             this.win.focus();
26155             
26156             
26157             // from jquery ui (MIT licenced)
26158             var range, node;
26159             var win = this.win;
26160             
26161             if (win.getSelection && win.getSelection().getRangeAt) {
26162                 range = win.getSelection().getRangeAt(0);
26163                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26164                 range.insertNode(node);
26165             } else if (win.document.selection && win.document.selection.createRange) {
26166                 // no firefox support
26167                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26168                 win.document.selection.createRange().pasteHTML(txt);
26169             } else {
26170                 // no firefox support
26171                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26172                 this.execCmd('InsertHTML', txt);
26173             } 
26174             
26175             this.syncValue();
26176             
26177             this.deferFocus();
26178         }
26179     },
26180  // private
26181     mozKeyPress : function(e){
26182         if(e.ctrlKey){
26183             var c = e.getCharCode(), cmd;
26184           
26185             if(c > 0){
26186                 c = String.fromCharCode(c).toLowerCase();
26187                 switch(c){
26188                     case 'b':
26189                         cmd = 'bold';
26190                         break;
26191                     case 'i':
26192                         cmd = 'italic';
26193                         break;
26194                     
26195                     case 'u':
26196                         cmd = 'underline';
26197                         break;
26198                     
26199                     case 'v':
26200                         this.cleanUpPaste.defer(100, this);
26201                         return;
26202                         
26203                 }
26204                 if(cmd){
26205                     this.win.focus();
26206                     this.execCmd(cmd);
26207                     this.deferFocus();
26208                     e.preventDefault();
26209                 }
26210                 
26211             }
26212         }
26213     },
26214
26215     // private
26216     fixKeys : function(){ // load time branching for fastest keydown performance
26217         if(Roo.isIE){
26218             return function(e){
26219                 var k = e.getKey(), r;
26220                 if(k == e.TAB){
26221                     e.stopEvent();
26222                     r = this.doc.selection.createRange();
26223                     if(r){
26224                         r.collapse(true);
26225                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26226                         this.deferFocus();
26227                     }
26228                     return;
26229                 }
26230                 
26231                 if(k == e.ENTER){
26232                     r = this.doc.selection.createRange();
26233                     if(r){
26234                         var target = r.parentElement();
26235                         if(!target || target.tagName.toLowerCase() != 'li'){
26236                             e.stopEvent();
26237                             r.pasteHTML('<br />');
26238                             r.collapse(false);
26239                             r.select();
26240                         }
26241                     }
26242                 }
26243                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26244                     this.cleanUpPaste.defer(100, this);
26245                     return;
26246                 }
26247                 
26248                 
26249             };
26250         }else if(Roo.isOpera){
26251             return function(e){
26252                 var k = e.getKey();
26253                 if(k == e.TAB){
26254                     e.stopEvent();
26255                     this.win.focus();
26256                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26257                     this.deferFocus();
26258                 }
26259                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26260                     this.cleanUpPaste.defer(100, this);
26261                     return;
26262                 }
26263                 
26264             };
26265         }else if(Roo.isSafari){
26266             return function(e){
26267                 var k = e.getKey();
26268                 
26269                 if(k == e.TAB){
26270                     e.stopEvent();
26271                     this.execCmd('InsertText','\t');
26272                     this.deferFocus();
26273                     return;
26274                 }
26275                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26276                     this.cleanUpPaste.defer(100, this);
26277                     return;
26278                 }
26279                 
26280              };
26281         }
26282     }(),
26283     
26284     getAllAncestors: function()
26285     {
26286         var p = this.getSelectedNode();
26287         var a = [];
26288         if (!p) {
26289             a.push(p); // push blank onto stack..
26290             p = this.getParentElement();
26291         }
26292         
26293         
26294         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26295             a.push(p);
26296             p = p.parentNode;
26297         }
26298         a.push(this.doc.body);
26299         return a;
26300     },
26301     lastSel : false,
26302     lastSelNode : false,
26303     
26304     
26305     getSelection : function() 
26306     {
26307         this.assignDocWin();
26308         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26309     },
26310     
26311     getSelectedNode: function() 
26312     {
26313         // this may only work on Gecko!!!
26314         
26315         // should we cache this!!!!
26316         
26317         
26318         
26319          
26320         var range = this.createRange(this.getSelection()).cloneRange();
26321         
26322         if (Roo.isIE) {
26323             var parent = range.parentElement();
26324             while (true) {
26325                 var testRange = range.duplicate();
26326                 testRange.moveToElementText(parent);
26327                 if (testRange.inRange(range)) {
26328                     break;
26329                 }
26330                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26331                     break;
26332                 }
26333                 parent = parent.parentElement;
26334             }
26335             return parent;
26336         }
26337         
26338         // is ancestor a text element.
26339         var ac =  range.commonAncestorContainer;
26340         if (ac.nodeType == 3) {
26341             ac = ac.parentNode;
26342         }
26343         
26344         var ar = ac.childNodes;
26345          
26346         var nodes = [];
26347         var other_nodes = [];
26348         var has_other_nodes = false;
26349         for (var i=0;i<ar.length;i++) {
26350             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26351                 continue;
26352             }
26353             // fullly contained node.
26354             
26355             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26356                 nodes.push(ar[i]);
26357                 continue;
26358             }
26359             
26360             // probably selected..
26361             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26362                 other_nodes.push(ar[i]);
26363                 continue;
26364             }
26365             // outer..
26366             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26367                 continue;
26368             }
26369             
26370             
26371             has_other_nodes = true;
26372         }
26373         if (!nodes.length && other_nodes.length) {
26374             nodes= other_nodes;
26375         }
26376         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26377             return false;
26378         }
26379         
26380         return nodes[0];
26381     },
26382     createRange: function(sel)
26383     {
26384         // this has strange effects when using with 
26385         // top toolbar - not sure if it's a great idea.
26386         //this.editor.contentWindow.focus();
26387         if (typeof sel != "undefined") {
26388             try {
26389                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26390             } catch(e) {
26391                 return this.doc.createRange();
26392             }
26393         } else {
26394             return this.doc.createRange();
26395         }
26396     },
26397     getParentElement: function()
26398     {
26399         
26400         this.assignDocWin();
26401         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26402         
26403         var range = this.createRange(sel);
26404          
26405         try {
26406             var p = range.commonAncestorContainer;
26407             while (p.nodeType == 3) { // text node
26408                 p = p.parentNode;
26409             }
26410             return p;
26411         } catch (e) {
26412             return null;
26413         }
26414     
26415     },
26416     /***
26417      *
26418      * Range intersection.. the hard stuff...
26419      *  '-1' = before
26420      *  '0' = hits..
26421      *  '1' = after.
26422      *         [ -- selected range --- ]
26423      *   [fail]                        [fail]
26424      *
26425      *    basically..
26426      *      if end is before start or  hits it. fail.
26427      *      if start is after end or hits it fail.
26428      *
26429      *   if either hits (but other is outside. - then it's not 
26430      *   
26431      *    
26432      **/
26433     
26434     
26435     // @see http://www.thismuchiknow.co.uk/?p=64.
26436     rangeIntersectsNode : function(range, node)
26437     {
26438         var nodeRange = node.ownerDocument.createRange();
26439         try {
26440             nodeRange.selectNode(node);
26441         } catch (e) {
26442             nodeRange.selectNodeContents(node);
26443         }
26444     
26445         var rangeStartRange = range.cloneRange();
26446         rangeStartRange.collapse(true);
26447     
26448         var rangeEndRange = range.cloneRange();
26449         rangeEndRange.collapse(false);
26450     
26451         var nodeStartRange = nodeRange.cloneRange();
26452         nodeStartRange.collapse(true);
26453     
26454         var nodeEndRange = nodeRange.cloneRange();
26455         nodeEndRange.collapse(false);
26456     
26457         return rangeStartRange.compareBoundaryPoints(
26458                  Range.START_TO_START, nodeEndRange) == -1 &&
26459                rangeEndRange.compareBoundaryPoints(
26460                  Range.START_TO_START, nodeStartRange) == 1;
26461         
26462          
26463     },
26464     rangeCompareNode : function(range, node)
26465     {
26466         var nodeRange = node.ownerDocument.createRange();
26467         try {
26468             nodeRange.selectNode(node);
26469         } catch (e) {
26470             nodeRange.selectNodeContents(node);
26471         }
26472         
26473         
26474         range.collapse(true);
26475     
26476         nodeRange.collapse(true);
26477      
26478         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26479         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26480          
26481         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26482         
26483         var nodeIsBefore   =  ss == 1;
26484         var nodeIsAfter    = ee == -1;
26485         
26486         if (nodeIsBefore && nodeIsAfter) {
26487             return 0; // outer
26488         }
26489         if (!nodeIsBefore && nodeIsAfter) {
26490             return 1; //right trailed.
26491         }
26492         
26493         if (nodeIsBefore && !nodeIsAfter) {
26494             return 2;  // left trailed.
26495         }
26496         // fully contined.
26497         return 3;
26498     },
26499
26500     // private? - in a new class?
26501     cleanUpPaste :  function()
26502     {
26503         // cleans up the whole document..
26504         Roo.log('cleanuppaste');
26505         
26506         this.cleanUpChildren(this.doc.body);
26507         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26508         if (clean != this.doc.body.innerHTML) {
26509             this.doc.body.innerHTML = clean;
26510         }
26511         
26512     },
26513     
26514     cleanWordChars : function(input) {// change the chars to hex code
26515         var he = Roo.HtmlEditorCore;
26516         
26517         var output = input;
26518         Roo.each(he.swapCodes, function(sw) { 
26519             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26520             
26521             output = output.replace(swapper, sw[1]);
26522         });
26523         
26524         return output;
26525     },
26526     
26527     
26528     cleanUpChildren : function (n)
26529     {
26530         if (!n.childNodes.length) {
26531             return;
26532         }
26533         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26534            this.cleanUpChild(n.childNodes[i]);
26535         }
26536     },
26537     
26538     
26539         
26540     
26541     cleanUpChild : function (node)
26542     {
26543         var ed = this;
26544         //console.log(node);
26545         if (node.nodeName == "#text") {
26546             // clean up silly Windows -- stuff?
26547             return; 
26548         }
26549         if (node.nodeName == "#comment") {
26550             if (!this.allowComments) {
26551                 node.parentNode.removeChild(node);
26552             }
26553             // clean up silly Windows -- stuff?
26554             return; 
26555         }
26556         var lcname = node.tagName.toLowerCase();
26557         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26558         // whitelist of tags..
26559         
26560         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26561             // remove node.
26562             node.parentNode.removeChild(node);
26563             return;
26564             
26565         }
26566         
26567         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26568         
26569         // spans with no attributes - just remove them..
26570         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26571             remove_keep_children = true;
26572         }
26573         
26574         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26575         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26576         
26577         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26578         //    remove_keep_children = true;
26579         //}
26580         
26581         if (remove_keep_children) {
26582             this.cleanUpChildren(node);
26583             // inserts everything just before this node...
26584             while (node.childNodes.length) {
26585                 var cn = node.childNodes[0];
26586                 node.removeChild(cn);
26587                 node.parentNode.insertBefore(cn, node);
26588             }
26589             node.parentNode.removeChild(node);
26590             return;
26591         }
26592         
26593         if (!node.attributes || !node.attributes.length) {
26594             
26595           
26596             
26597             
26598             this.cleanUpChildren(node);
26599             return;
26600         }
26601         
26602         function cleanAttr(n,v)
26603         {
26604             
26605             if (v.match(/^\./) || v.match(/^\//)) {
26606                 return;
26607             }
26608             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26609                 return;
26610             }
26611             if (v.match(/^#/)) {
26612                 return;
26613             }
26614             if (v.match(/^\{/)) { // allow template editing.
26615                 return;
26616             }
26617 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26618             node.removeAttribute(n);
26619             
26620         }
26621         
26622         var cwhite = this.cwhite;
26623         var cblack = this.cblack;
26624             
26625         function cleanStyle(n,v)
26626         {
26627             if (v.match(/expression/)) { //XSS?? should we even bother..
26628                 node.removeAttribute(n);
26629                 return;
26630             }
26631             
26632             var parts = v.split(/;/);
26633             var clean = [];
26634             
26635             Roo.each(parts, function(p) {
26636                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26637                 if (!p.length) {
26638                     return true;
26639                 }
26640                 var l = p.split(':').shift().replace(/\s+/g,'');
26641                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26642                 
26643                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26644 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26645                     //node.removeAttribute(n);
26646                     return true;
26647                 }
26648                 //Roo.log()
26649                 // only allow 'c whitelisted system attributes'
26650                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26651 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26652                     //node.removeAttribute(n);
26653                     return true;
26654                 }
26655                 
26656                 
26657                  
26658                 
26659                 clean.push(p);
26660                 return true;
26661             });
26662             if (clean.length) { 
26663                 node.setAttribute(n, clean.join(';'));
26664             } else {
26665                 node.removeAttribute(n);
26666             }
26667             
26668         }
26669         
26670         
26671         for (var i = node.attributes.length-1; i > -1 ; i--) {
26672             var a = node.attributes[i];
26673             //console.log(a);
26674             
26675             if (a.name.toLowerCase().substr(0,2)=='on')  {
26676                 node.removeAttribute(a.name);
26677                 continue;
26678             }
26679             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26680                 node.removeAttribute(a.name);
26681                 continue;
26682             }
26683             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26684                 cleanAttr(a.name,a.value); // fixme..
26685                 continue;
26686             }
26687             if (a.name == 'style') {
26688                 cleanStyle(a.name,a.value);
26689                 continue;
26690             }
26691             /// clean up MS crap..
26692             // tecnically this should be a list of valid class'es..
26693             
26694             
26695             if (a.name == 'class') {
26696                 if (a.value.match(/^Mso/)) {
26697                     node.removeAttribute('class');
26698                 }
26699                 
26700                 if (a.value.match(/^body$/)) {
26701                     node.removeAttribute('class');
26702                 }
26703                 continue;
26704             }
26705             
26706             // style cleanup!?
26707             // class cleanup?
26708             
26709         }
26710         
26711         
26712         this.cleanUpChildren(node);
26713         
26714         
26715     },
26716     
26717     /**
26718      * Clean up MS wordisms...
26719      */
26720     cleanWord : function(node)
26721     {
26722         if (!node) {
26723             this.cleanWord(this.doc.body);
26724             return;
26725         }
26726         
26727         if(
26728                 node.nodeName == 'SPAN' &&
26729                 !node.hasAttributes() &&
26730                 node.childNodes.length == 1 &&
26731                 node.firstChild.nodeName == "#text"  
26732         ) {
26733             var textNode = node.firstChild;
26734             node.removeChild(textNode);
26735             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26736                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26737             }
26738             node.parentNode.insertBefore(textNode, node);
26739             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26740                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26741             }
26742             node.parentNode.removeChild(node);
26743         }
26744         
26745         if (node.nodeName == "#text") {
26746             // clean up silly Windows -- stuff?
26747             return; 
26748         }
26749         if (node.nodeName == "#comment") {
26750             node.parentNode.removeChild(node);
26751             // clean up silly Windows -- stuff?
26752             return; 
26753         }
26754         
26755         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26756             node.parentNode.removeChild(node);
26757             return;
26758         }
26759         //Roo.log(node.tagName);
26760         // remove - but keep children..
26761         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26762             //Roo.log('-- removed');
26763             while (node.childNodes.length) {
26764                 var cn = node.childNodes[0];
26765                 node.removeChild(cn);
26766                 node.parentNode.insertBefore(cn, node);
26767                 // move node to parent - and clean it..
26768                 this.cleanWord(cn);
26769             }
26770             node.parentNode.removeChild(node);
26771             /// no need to iterate chidlren = it's got none..
26772             //this.iterateChildren(node, this.cleanWord);
26773             return;
26774         }
26775         // clean styles
26776         if (node.className.length) {
26777             
26778             var cn = node.className.split(/\W+/);
26779             var cna = [];
26780             Roo.each(cn, function(cls) {
26781                 if (cls.match(/Mso[a-zA-Z]+/)) {
26782                     return;
26783                 }
26784                 cna.push(cls);
26785             });
26786             node.className = cna.length ? cna.join(' ') : '';
26787             if (!cna.length) {
26788                 node.removeAttribute("class");
26789             }
26790         }
26791         
26792         if (node.hasAttribute("lang")) {
26793             node.removeAttribute("lang");
26794         }
26795         
26796         if (node.hasAttribute("style")) {
26797             
26798             var styles = node.getAttribute("style").split(";");
26799             var nstyle = [];
26800             Roo.each(styles, function(s) {
26801                 if (!s.match(/:/)) {
26802                     return;
26803                 }
26804                 var kv = s.split(":");
26805                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26806                     return;
26807                 }
26808                 // what ever is left... we allow.
26809                 nstyle.push(s);
26810             });
26811             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26812             if (!nstyle.length) {
26813                 node.removeAttribute('style');
26814             }
26815         }
26816         this.iterateChildren(node, this.cleanWord);
26817         
26818         
26819         
26820     },
26821     /**
26822      * iterateChildren of a Node, calling fn each time, using this as the scole..
26823      * @param {DomNode} node node to iterate children of.
26824      * @param {Function} fn method of this class to call on each item.
26825      */
26826     iterateChildren : function(node, fn)
26827     {
26828         if (!node.childNodes.length) {
26829                 return;
26830         }
26831         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26832            fn.call(this, node.childNodes[i])
26833         }
26834     },
26835     
26836     
26837     /**
26838      * cleanTableWidths.
26839      *
26840      * Quite often pasting from word etc.. results in tables with column and widths.
26841      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26842      *
26843      */
26844     cleanTableWidths : function(node)
26845     {
26846          
26847          
26848         if (!node) {
26849             this.cleanTableWidths(this.doc.body);
26850             return;
26851         }
26852         
26853         // ignore list...
26854         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26855             return; 
26856         }
26857         Roo.log(node.tagName);
26858         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26859             this.iterateChildren(node, this.cleanTableWidths);
26860             return;
26861         }
26862         if (node.hasAttribute('width')) {
26863             node.removeAttribute('width');
26864         }
26865         
26866          
26867         if (node.hasAttribute("style")) {
26868             // pretty basic...
26869             
26870             var styles = node.getAttribute("style").split(";");
26871             var nstyle = [];
26872             Roo.each(styles, function(s) {
26873                 if (!s.match(/:/)) {
26874                     return;
26875                 }
26876                 var kv = s.split(":");
26877                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26878                     return;
26879                 }
26880                 // what ever is left... we allow.
26881                 nstyle.push(s);
26882             });
26883             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26884             if (!nstyle.length) {
26885                 node.removeAttribute('style');
26886             }
26887         }
26888         
26889         this.iterateChildren(node, this.cleanTableWidths);
26890         
26891         
26892     },
26893     
26894     
26895     
26896     
26897     domToHTML : function(currentElement, depth, nopadtext) {
26898         
26899         depth = depth || 0;
26900         nopadtext = nopadtext || false;
26901     
26902         if (!currentElement) {
26903             return this.domToHTML(this.doc.body);
26904         }
26905         
26906         //Roo.log(currentElement);
26907         var j;
26908         var allText = false;
26909         var nodeName = currentElement.nodeName;
26910         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26911         
26912         if  (nodeName == '#text') {
26913             
26914             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26915         }
26916         
26917         
26918         var ret = '';
26919         if (nodeName != 'BODY') {
26920              
26921             var i = 0;
26922             // Prints the node tagName, such as <A>, <IMG>, etc
26923             if (tagName) {
26924                 var attr = [];
26925                 for(i = 0; i < currentElement.attributes.length;i++) {
26926                     // quoting?
26927                     var aname = currentElement.attributes.item(i).name;
26928                     if (!currentElement.attributes.item(i).value.length) {
26929                         continue;
26930                     }
26931                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26932                 }
26933                 
26934                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26935             } 
26936             else {
26937                 
26938                 // eack
26939             }
26940         } else {
26941             tagName = false;
26942         }
26943         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26944             return ret;
26945         }
26946         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26947             nopadtext = true;
26948         }
26949         
26950         
26951         // Traverse the tree
26952         i = 0;
26953         var currentElementChild = currentElement.childNodes.item(i);
26954         var allText = true;
26955         var innerHTML  = '';
26956         lastnode = '';
26957         while (currentElementChild) {
26958             // Formatting code (indent the tree so it looks nice on the screen)
26959             var nopad = nopadtext;
26960             if (lastnode == 'SPAN') {
26961                 nopad  = true;
26962             }
26963             // text
26964             if  (currentElementChild.nodeName == '#text') {
26965                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26966                 toadd = nopadtext ? toadd : toadd.trim();
26967                 if (!nopad && toadd.length > 80) {
26968                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26969                 }
26970                 innerHTML  += toadd;
26971                 
26972                 i++;
26973                 currentElementChild = currentElement.childNodes.item(i);
26974                 lastNode = '';
26975                 continue;
26976             }
26977             allText = false;
26978             
26979             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26980                 
26981             // Recursively traverse the tree structure of the child node
26982             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26983             lastnode = currentElementChild.nodeName;
26984             i++;
26985             currentElementChild=currentElement.childNodes.item(i);
26986         }
26987         
26988         ret += innerHTML;
26989         
26990         if (!allText) {
26991                 // The remaining code is mostly for formatting the tree
26992             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26993         }
26994         
26995         
26996         if (tagName) {
26997             ret+= "</"+tagName+">";
26998         }
26999         return ret;
27000         
27001     },
27002         
27003     applyBlacklists : function()
27004     {
27005         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27006         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27007         
27008         this.white = [];
27009         this.black = [];
27010         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27011             if (b.indexOf(tag) > -1) {
27012                 return;
27013             }
27014             this.white.push(tag);
27015             
27016         }, this);
27017         
27018         Roo.each(w, function(tag) {
27019             if (b.indexOf(tag) > -1) {
27020                 return;
27021             }
27022             if (this.white.indexOf(tag) > -1) {
27023                 return;
27024             }
27025             this.white.push(tag);
27026             
27027         }, this);
27028         
27029         
27030         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27031             if (w.indexOf(tag) > -1) {
27032                 return;
27033             }
27034             this.black.push(tag);
27035             
27036         }, this);
27037         
27038         Roo.each(b, function(tag) {
27039             if (w.indexOf(tag) > -1) {
27040                 return;
27041             }
27042             if (this.black.indexOf(tag) > -1) {
27043                 return;
27044             }
27045             this.black.push(tag);
27046             
27047         }, this);
27048         
27049         
27050         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27051         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27052         
27053         this.cwhite = [];
27054         this.cblack = [];
27055         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27056             if (b.indexOf(tag) > -1) {
27057                 return;
27058             }
27059             this.cwhite.push(tag);
27060             
27061         }, this);
27062         
27063         Roo.each(w, function(tag) {
27064             if (b.indexOf(tag) > -1) {
27065                 return;
27066             }
27067             if (this.cwhite.indexOf(tag) > -1) {
27068                 return;
27069             }
27070             this.cwhite.push(tag);
27071             
27072         }, this);
27073         
27074         
27075         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27076             if (w.indexOf(tag) > -1) {
27077                 return;
27078             }
27079             this.cblack.push(tag);
27080             
27081         }, this);
27082         
27083         Roo.each(b, function(tag) {
27084             if (w.indexOf(tag) > -1) {
27085                 return;
27086             }
27087             if (this.cblack.indexOf(tag) > -1) {
27088                 return;
27089             }
27090             this.cblack.push(tag);
27091             
27092         }, this);
27093     },
27094     
27095     setStylesheets : function(stylesheets)
27096     {
27097         if(typeof(stylesheets) == 'string'){
27098             Roo.get(this.iframe.contentDocument.head).createChild({
27099                 tag : 'link',
27100                 rel : 'stylesheet',
27101                 type : 'text/css',
27102                 href : stylesheets
27103             });
27104             
27105             return;
27106         }
27107         var _this = this;
27108      
27109         Roo.each(stylesheets, function(s) {
27110             if(!s.length){
27111                 return;
27112             }
27113             
27114             Roo.get(_this.iframe.contentDocument.head).createChild({
27115                 tag : 'link',
27116                 rel : 'stylesheet',
27117                 type : 'text/css',
27118                 href : s
27119             });
27120         });
27121
27122         
27123     },
27124     
27125     removeStylesheets : function()
27126     {
27127         var _this = this;
27128         
27129         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27130             s.remove();
27131         });
27132     },
27133     
27134     setStyle : function(style)
27135     {
27136         Roo.get(this.iframe.contentDocument.head).createChild({
27137             tag : 'style',
27138             type : 'text/css',
27139             html : style
27140         });
27141
27142         return;
27143     }
27144     
27145     // hide stuff that is not compatible
27146     /**
27147      * @event blur
27148      * @hide
27149      */
27150     /**
27151      * @event change
27152      * @hide
27153      */
27154     /**
27155      * @event focus
27156      * @hide
27157      */
27158     /**
27159      * @event specialkey
27160      * @hide
27161      */
27162     /**
27163      * @cfg {String} fieldClass @hide
27164      */
27165     /**
27166      * @cfg {String} focusClass @hide
27167      */
27168     /**
27169      * @cfg {String} autoCreate @hide
27170      */
27171     /**
27172      * @cfg {String} inputType @hide
27173      */
27174     /**
27175      * @cfg {String} invalidClass @hide
27176      */
27177     /**
27178      * @cfg {String} invalidText @hide
27179      */
27180     /**
27181      * @cfg {String} msgFx @hide
27182      */
27183     /**
27184      * @cfg {String} validateOnBlur @hide
27185      */
27186 });
27187
27188 Roo.HtmlEditorCore.white = [
27189         'area', 'br', 'img', 'input', 'hr', 'wbr',
27190         
27191        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27192        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27193        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27194        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27195        'table',   'ul',         'xmp', 
27196        
27197        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27198       'thead',   'tr', 
27199      
27200       'dir', 'menu', 'ol', 'ul', 'dl',
27201        
27202       'embed',  'object'
27203 ];
27204
27205
27206 Roo.HtmlEditorCore.black = [
27207     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27208         'applet', // 
27209         'base',   'basefont', 'bgsound', 'blink',  'body', 
27210         'frame',  'frameset', 'head',    'html',   'ilayer', 
27211         'iframe', 'layer',  'link',     'meta',    'object',   
27212         'script', 'style' ,'title',  'xml' // clean later..
27213 ];
27214 Roo.HtmlEditorCore.clean = [
27215     'script', 'style', 'title', 'xml'
27216 ];
27217 Roo.HtmlEditorCore.remove = [
27218     'font'
27219 ];
27220 // attributes..
27221
27222 Roo.HtmlEditorCore.ablack = [
27223     'on'
27224 ];
27225     
27226 Roo.HtmlEditorCore.aclean = [ 
27227     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27228 ];
27229
27230 // protocols..
27231 Roo.HtmlEditorCore.pwhite= [
27232         'http',  'https',  'mailto'
27233 ];
27234
27235 // white listed style attributes.
27236 Roo.HtmlEditorCore.cwhite= [
27237       //  'text-align', /// default is to allow most things..
27238       
27239          
27240 //        'font-size'//??
27241 ];
27242
27243 // black listed style attributes.
27244 Roo.HtmlEditorCore.cblack= [
27245       //  'font-size' -- this can be set by the project 
27246 ];
27247
27248
27249 Roo.HtmlEditorCore.swapCodes   =[ 
27250     [    8211, "&#8211;" ], 
27251     [    8212, "&#8212;" ], 
27252     [    8216,  "'" ],  
27253     [    8217, "'" ],  
27254     [    8220, '"' ],  
27255     [    8221, '"' ],  
27256     [    8226, "*" ],  
27257     [    8230, "..." ]
27258 ]; 
27259
27260     /*
27261  * - LGPL
27262  *
27263  * HtmlEditor
27264  * 
27265  */
27266
27267 /**
27268  * @class Roo.bootstrap.HtmlEditor
27269  * @extends Roo.bootstrap.TextArea
27270  * Bootstrap HtmlEditor class
27271
27272  * @constructor
27273  * Create a new HtmlEditor
27274  * @param {Object} config The config object
27275  */
27276
27277 Roo.bootstrap.HtmlEditor = function(config){
27278     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27279     if (!this.toolbars) {
27280         this.toolbars = [];
27281     }
27282     
27283     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27284     this.addEvents({
27285             /**
27286              * @event initialize
27287              * Fires when the editor is fully initialized (including the iframe)
27288              * @param {HtmlEditor} this
27289              */
27290             initialize: true,
27291             /**
27292              * @event activate
27293              * Fires when the editor is first receives the focus. Any insertion must wait
27294              * until after this event.
27295              * @param {HtmlEditor} this
27296              */
27297             activate: true,
27298              /**
27299              * @event beforesync
27300              * Fires before the textarea is updated with content from the editor iframe. Return false
27301              * to cancel the sync.
27302              * @param {HtmlEditor} this
27303              * @param {String} html
27304              */
27305             beforesync: true,
27306              /**
27307              * @event beforepush
27308              * Fires before the iframe editor is updated with content from the textarea. Return false
27309              * to cancel the push.
27310              * @param {HtmlEditor} this
27311              * @param {String} html
27312              */
27313             beforepush: true,
27314              /**
27315              * @event sync
27316              * Fires when the textarea is updated with content from the editor iframe.
27317              * @param {HtmlEditor} this
27318              * @param {String} html
27319              */
27320             sync: true,
27321              /**
27322              * @event push
27323              * Fires when the iframe editor is updated with content from the textarea.
27324              * @param {HtmlEditor} this
27325              * @param {String} html
27326              */
27327             push: true,
27328              /**
27329              * @event editmodechange
27330              * Fires when the editor switches edit modes
27331              * @param {HtmlEditor} this
27332              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27333              */
27334             editmodechange: true,
27335             /**
27336              * @event editorevent
27337              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27338              * @param {HtmlEditor} this
27339              */
27340             editorevent: true,
27341             /**
27342              * @event firstfocus
27343              * Fires when on first focus - needed by toolbars..
27344              * @param {HtmlEditor} this
27345              */
27346             firstfocus: true,
27347             /**
27348              * @event autosave
27349              * Auto save the htmlEditor value as a file into Events
27350              * @param {HtmlEditor} this
27351              */
27352             autosave: true,
27353             /**
27354              * @event savedpreview
27355              * preview the saved version of htmlEditor
27356              * @param {HtmlEditor} this
27357              */
27358             savedpreview: true
27359         });
27360 };
27361
27362
27363 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27364     
27365     
27366       /**
27367      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27368      */
27369     toolbars : false,
27370     
27371      /**
27372     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27373     */
27374     btns : [],
27375    
27376      /**
27377      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27378      *                        Roo.resizable.
27379      */
27380     resizable : false,
27381      /**
27382      * @cfg {Number} height (in pixels)
27383      */   
27384     height: 300,
27385    /**
27386      * @cfg {Number} width (in pixels)
27387      */   
27388     width: false,
27389     
27390     /**
27391      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27392      * 
27393      */
27394     stylesheets: false,
27395     
27396     // id of frame..
27397     frameId: false,
27398     
27399     // private properties
27400     validationEvent : false,
27401     deferHeight: true,
27402     initialized : false,
27403     activated : false,
27404     
27405     onFocus : Roo.emptyFn,
27406     iframePad:3,
27407     hideMode:'offsets',
27408     
27409     tbContainer : false,
27410     
27411     bodyCls : '',
27412     
27413     toolbarContainer :function() {
27414         return this.wrap.select('.x-html-editor-tb',true).first();
27415     },
27416
27417     /**
27418      * Protected method that will not generally be called directly. It
27419      * is called when the editor creates its toolbar. Override this method if you need to
27420      * add custom toolbar buttons.
27421      * @param {HtmlEditor} editor
27422      */
27423     createToolbar : function(){
27424         Roo.log('renewing');
27425         Roo.log("create toolbars");
27426         
27427         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27428         this.toolbars[0].render(this.toolbarContainer());
27429         
27430         return;
27431         
27432 //        if (!editor.toolbars || !editor.toolbars.length) {
27433 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27434 //        }
27435 //        
27436 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27437 //            editor.toolbars[i] = Roo.factory(
27438 //                    typeof(editor.toolbars[i]) == 'string' ?
27439 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27440 //                Roo.bootstrap.HtmlEditor);
27441 //            editor.toolbars[i].init(editor);
27442 //        }
27443     },
27444
27445      
27446     // private
27447     onRender : function(ct, position)
27448     {
27449        // Roo.log("Call onRender: " + this.xtype);
27450         var _t = this;
27451         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27452       
27453         this.wrap = this.inputEl().wrap({
27454             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27455         });
27456         
27457         this.editorcore.onRender(ct, position);
27458          
27459         if (this.resizable) {
27460             this.resizeEl = new Roo.Resizable(this.wrap, {
27461                 pinned : true,
27462                 wrap: true,
27463                 dynamic : true,
27464                 minHeight : this.height,
27465                 height: this.height,
27466                 handles : this.resizable,
27467                 width: this.width,
27468                 listeners : {
27469                     resize : function(r, w, h) {
27470                         _t.onResize(w,h); // -something
27471                     }
27472                 }
27473             });
27474             
27475         }
27476         this.createToolbar(this);
27477        
27478         
27479         if(!this.width && this.resizable){
27480             this.setSize(this.wrap.getSize());
27481         }
27482         if (this.resizeEl) {
27483             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27484             // should trigger onReize..
27485         }
27486         
27487     },
27488
27489     // private
27490     onResize : function(w, h)
27491     {
27492         Roo.log('resize: ' +w + ',' + h );
27493         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27494         var ew = false;
27495         var eh = false;
27496         
27497         if(this.inputEl() ){
27498             if(typeof w == 'number'){
27499                 var aw = w - this.wrap.getFrameWidth('lr');
27500                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27501                 ew = aw;
27502             }
27503             if(typeof h == 'number'){
27504                  var tbh = -11;  // fixme it needs to tool bar size!
27505                 for (var i =0; i < this.toolbars.length;i++) {
27506                     // fixme - ask toolbars for heights?
27507                     tbh += this.toolbars[i].el.getHeight();
27508                     //if (this.toolbars[i].footer) {
27509                     //    tbh += this.toolbars[i].footer.el.getHeight();
27510                     //}
27511                 }
27512               
27513                 
27514                 
27515                 
27516                 
27517                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27518                 ah -= 5; // knock a few pixes off for look..
27519                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27520                 var eh = ah;
27521             }
27522         }
27523         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27524         this.editorcore.onResize(ew,eh);
27525         
27526     },
27527
27528     /**
27529      * Toggles the editor between standard and source edit mode.
27530      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27531      */
27532     toggleSourceEdit : function(sourceEditMode)
27533     {
27534         this.editorcore.toggleSourceEdit(sourceEditMode);
27535         
27536         if(this.editorcore.sourceEditMode){
27537             Roo.log('editor - showing textarea');
27538             
27539 //            Roo.log('in');
27540 //            Roo.log(this.syncValue());
27541             this.syncValue();
27542             this.inputEl().removeClass(['hide', 'x-hidden']);
27543             this.inputEl().dom.removeAttribute('tabIndex');
27544             this.inputEl().focus();
27545         }else{
27546             Roo.log('editor - hiding textarea');
27547 //            Roo.log('out')
27548 //            Roo.log(this.pushValue()); 
27549             this.pushValue();
27550             
27551             this.inputEl().addClass(['hide', 'x-hidden']);
27552             this.inputEl().dom.setAttribute('tabIndex', -1);
27553             //this.deferFocus();
27554         }
27555          
27556         if(this.resizable){
27557             this.setSize(this.wrap.getSize());
27558         }
27559         
27560         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27561     },
27562  
27563     // private (for BoxComponent)
27564     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27565
27566     // private (for BoxComponent)
27567     getResizeEl : function(){
27568         return this.wrap;
27569     },
27570
27571     // private (for BoxComponent)
27572     getPositionEl : function(){
27573         return this.wrap;
27574     },
27575
27576     // private
27577     initEvents : function(){
27578         this.originalValue = this.getValue();
27579     },
27580
27581 //    /**
27582 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27583 //     * @method
27584 //     */
27585 //    markInvalid : Roo.emptyFn,
27586 //    /**
27587 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27588 //     * @method
27589 //     */
27590 //    clearInvalid : Roo.emptyFn,
27591
27592     setValue : function(v){
27593         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27594         this.editorcore.pushValue();
27595     },
27596
27597      
27598     // private
27599     deferFocus : function(){
27600         this.focus.defer(10, this);
27601     },
27602
27603     // doc'ed in Field
27604     focus : function(){
27605         this.editorcore.focus();
27606         
27607     },
27608       
27609
27610     // private
27611     onDestroy : function(){
27612         
27613         
27614         
27615         if(this.rendered){
27616             
27617             for (var i =0; i < this.toolbars.length;i++) {
27618                 // fixme - ask toolbars for heights?
27619                 this.toolbars[i].onDestroy();
27620             }
27621             
27622             this.wrap.dom.innerHTML = '';
27623             this.wrap.remove();
27624         }
27625     },
27626
27627     // private
27628     onFirstFocus : function(){
27629         //Roo.log("onFirstFocus");
27630         this.editorcore.onFirstFocus();
27631          for (var i =0; i < this.toolbars.length;i++) {
27632             this.toolbars[i].onFirstFocus();
27633         }
27634         
27635     },
27636     
27637     // private
27638     syncValue : function()
27639     {   
27640         this.editorcore.syncValue();
27641     },
27642     
27643     pushValue : function()
27644     {   
27645         this.editorcore.pushValue();
27646     }
27647      
27648     
27649     // hide stuff that is not compatible
27650     /**
27651      * @event blur
27652      * @hide
27653      */
27654     /**
27655      * @event change
27656      * @hide
27657      */
27658     /**
27659      * @event focus
27660      * @hide
27661      */
27662     /**
27663      * @event specialkey
27664      * @hide
27665      */
27666     /**
27667      * @cfg {String} fieldClass @hide
27668      */
27669     /**
27670      * @cfg {String} focusClass @hide
27671      */
27672     /**
27673      * @cfg {String} autoCreate @hide
27674      */
27675     /**
27676      * @cfg {String} inputType @hide
27677      */
27678      
27679     /**
27680      * @cfg {String} invalidText @hide
27681      */
27682     /**
27683      * @cfg {String} msgFx @hide
27684      */
27685     /**
27686      * @cfg {String} validateOnBlur @hide
27687      */
27688 });
27689  
27690     
27691    
27692    
27693    
27694       
27695 Roo.namespace('Roo.bootstrap.htmleditor');
27696 /**
27697  * @class Roo.bootstrap.HtmlEditorToolbar1
27698  * Basic Toolbar
27699  * 
27700  * @example
27701  * Usage:
27702  *
27703  new Roo.bootstrap.HtmlEditor({
27704     ....
27705     toolbars : [
27706         new Roo.bootstrap.HtmlEditorToolbar1({
27707             disable : { fonts: 1 , format: 1, ..., ... , ...],
27708             btns : [ .... ]
27709         })
27710     }
27711      
27712  * 
27713  * @cfg {Object} disable List of elements to disable..
27714  * @cfg {Array} btns List of additional buttons.
27715  * 
27716  * 
27717  * NEEDS Extra CSS? 
27718  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27719  */
27720  
27721 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27722 {
27723     
27724     Roo.apply(this, config);
27725     
27726     // default disabled, based on 'good practice'..
27727     this.disable = this.disable || {};
27728     Roo.applyIf(this.disable, {
27729         fontSize : true,
27730         colors : true,
27731         specialElements : true
27732     });
27733     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27734     
27735     this.editor = config.editor;
27736     this.editorcore = config.editor.editorcore;
27737     
27738     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27739     
27740     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27741     // dont call parent... till later.
27742 }
27743 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27744      
27745     bar : true,
27746     
27747     editor : false,
27748     editorcore : false,
27749     
27750     
27751     formats : [
27752         "p" ,  
27753         "h1","h2","h3","h4","h5","h6", 
27754         "pre", "code", 
27755         "abbr", "acronym", "address", "cite", "samp", "var",
27756         'div','span'
27757     ],
27758     
27759     onRender : function(ct, position)
27760     {
27761        // Roo.log("Call onRender: " + this.xtype);
27762         
27763        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27764        Roo.log(this.el);
27765        this.el.dom.style.marginBottom = '0';
27766        var _this = this;
27767        var editorcore = this.editorcore;
27768        var editor= this.editor;
27769        
27770        var children = [];
27771        var btn = function(id,cmd , toggle, handler, html){
27772        
27773             var  event = toggle ? 'toggle' : 'click';
27774        
27775             var a = {
27776                 size : 'sm',
27777                 xtype: 'Button',
27778                 xns: Roo.bootstrap,
27779                 //glyphicon : id,
27780                 fa: id,
27781                 cmd : id || cmd,
27782                 enableToggle:toggle !== false,
27783                 html : html || '',
27784                 pressed : toggle ? false : null,
27785                 listeners : {}
27786             };
27787             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27788                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27789             };
27790             children.push(a);
27791             return a;
27792        }
27793        
27794     //    var cb_box = function...
27795         
27796         var style = {
27797                 xtype: 'Button',
27798                 size : 'sm',
27799                 xns: Roo.bootstrap,
27800                 fa : 'font',
27801                 //html : 'submit'
27802                 menu : {
27803                     xtype: 'Menu',
27804                     xns: Roo.bootstrap,
27805                     items:  []
27806                 }
27807         };
27808         Roo.each(this.formats, function(f) {
27809             style.menu.items.push({
27810                 xtype :'MenuItem',
27811                 xns: Roo.bootstrap,
27812                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27813                 tagname : f,
27814                 listeners : {
27815                     click : function()
27816                     {
27817                         editorcore.insertTag(this.tagname);
27818                         editor.focus();
27819                     }
27820                 }
27821                 
27822             });
27823         });
27824         children.push(style);   
27825         
27826         btn('bold',false,true);
27827         btn('italic',false,true);
27828         btn('align-left', 'justifyleft',true);
27829         btn('align-center', 'justifycenter',true);
27830         btn('align-right' , 'justifyright',true);
27831         btn('link', false, false, function(btn) {
27832             //Roo.log("create link?");
27833             var url = prompt(this.createLinkText, this.defaultLinkValue);
27834             if(url && url != 'http:/'+'/'){
27835                 this.editorcore.relayCmd('createlink', url);
27836             }
27837         }),
27838         btn('list','insertunorderedlist',true);
27839         btn('pencil', false,true, function(btn){
27840                 Roo.log(this);
27841                 this.toggleSourceEdit(btn.pressed);
27842         });
27843         
27844         if (this.editor.btns.length > 0) {
27845             for (var i = 0; i<this.editor.btns.length; i++) {
27846                 children.push(this.editor.btns[i]);
27847             }
27848         }
27849         
27850         /*
27851         var cog = {
27852                 xtype: 'Button',
27853                 size : 'sm',
27854                 xns: Roo.bootstrap,
27855                 glyphicon : 'cog',
27856                 //html : 'submit'
27857                 menu : {
27858                     xtype: 'Menu',
27859                     xns: Roo.bootstrap,
27860                     items:  []
27861                 }
27862         };
27863         
27864         cog.menu.items.push({
27865             xtype :'MenuItem',
27866             xns: Roo.bootstrap,
27867             html : Clean styles,
27868             tagname : f,
27869             listeners : {
27870                 click : function()
27871                 {
27872                     editorcore.insertTag(this.tagname);
27873                     editor.focus();
27874                 }
27875             }
27876             
27877         });
27878        */
27879         
27880          
27881        this.xtype = 'NavSimplebar';
27882         
27883         for(var i=0;i< children.length;i++) {
27884             
27885             this.buttons.add(this.addxtypeChild(children[i]));
27886             
27887         }
27888         
27889         editor.on('editorevent', this.updateToolbar, this);
27890     },
27891     onBtnClick : function(id)
27892     {
27893        this.editorcore.relayCmd(id);
27894        this.editorcore.focus();
27895     },
27896     
27897     /**
27898      * Protected method that will not generally be called directly. It triggers
27899      * a toolbar update by reading the markup state of the current selection in the editor.
27900      */
27901     updateToolbar: function(){
27902
27903         if(!this.editorcore.activated){
27904             this.editor.onFirstFocus(); // is this neeed?
27905             return;
27906         }
27907
27908         var btns = this.buttons; 
27909         var doc = this.editorcore.doc;
27910         btns.get('bold').setActive(doc.queryCommandState('bold'));
27911         btns.get('italic').setActive(doc.queryCommandState('italic'));
27912         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27913         
27914         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27915         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27916         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27917         
27918         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27919         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27920          /*
27921         
27922         var ans = this.editorcore.getAllAncestors();
27923         if (this.formatCombo) {
27924             
27925             
27926             var store = this.formatCombo.store;
27927             this.formatCombo.setValue("");
27928             for (var i =0; i < ans.length;i++) {
27929                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27930                     // select it..
27931                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27932                     break;
27933                 }
27934             }
27935         }
27936         
27937         
27938         
27939         // hides menus... - so this cant be on a menu...
27940         Roo.bootstrap.MenuMgr.hideAll();
27941         */
27942         Roo.bootstrap.MenuMgr.hideAll();
27943         //this.editorsyncValue();
27944     },
27945     onFirstFocus: function() {
27946         this.buttons.each(function(item){
27947            item.enable();
27948         });
27949     },
27950     toggleSourceEdit : function(sourceEditMode){
27951         
27952           
27953         if(sourceEditMode){
27954             Roo.log("disabling buttons");
27955            this.buttons.each( function(item){
27956                 if(item.cmd != 'pencil'){
27957                     item.disable();
27958                 }
27959             });
27960           
27961         }else{
27962             Roo.log("enabling buttons");
27963             if(this.editorcore.initialized){
27964                 this.buttons.each( function(item){
27965                     item.enable();
27966                 });
27967             }
27968             
27969         }
27970         Roo.log("calling toggole on editor");
27971         // tell the editor that it's been pressed..
27972         this.editor.toggleSourceEdit(sourceEditMode);
27973        
27974     }
27975 });
27976
27977
27978
27979
27980  
27981 /*
27982  * - LGPL
27983  */
27984
27985 /**
27986  * @class Roo.bootstrap.Markdown
27987  * @extends Roo.bootstrap.TextArea
27988  * Bootstrap Showdown editable area
27989  * @cfg {string} content
27990  * 
27991  * @constructor
27992  * Create a new Showdown
27993  */
27994
27995 Roo.bootstrap.Markdown = function(config){
27996     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27997    
27998 };
27999
28000 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28001     
28002     editing :false,
28003     
28004     initEvents : function()
28005     {
28006         
28007         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28008         this.markdownEl = this.el.createChild({
28009             cls : 'roo-markdown-area'
28010         });
28011         this.inputEl().addClass('d-none');
28012         if (this.getValue() == '') {
28013             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28014             
28015         } else {
28016             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28017         }
28018         this.markdownEl.on('click', this.toggleTextEdit, this);
28019         this.on('blur', this.toggleTextEdit, this);
28020         this.on('specialkey', this.resizeTextArea, this);
28021     },
28022     
28023     toggleTextEdit : function()
28024     {
28025         var sh = this.markdownEl.getHeight();
28026         this.inputEl().addClass('d-none');
28027         this.markdownEl.addClass('d-none');
28028         if (!this.editing) {
28029             // show editor?
28030             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28031             this.inputEl().removeClass('d-none');
28032             this.inputEl().focus();
28033             this.editing = true;
28034             return;
28035         }
28036         // show showdown...
28037         this.updateMarkdown();
28038         this.markdownEl.removeClass('d-none');
28039         this.editing = false;
28040         return;
28041     },
28042     updateMarkdown : function()
28043     {
28044         if (this.getValue() == '') {
28045             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28046             return;
28047         }
28048  
28049         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28050     },
28051     
28052     resizeTextArea: function () {
28053         
28054         var sh = 100;
28055         Roo.log([sh, this.getValue().split("\n").length * 30]);
28056         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28057     },
28058     setValue : function(val)
28059     {
28060         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28061         if (!this.editing) {
28062             this.updateMarkdown();
28063         }
28064         
28065     },
28066     focus : function()
28067     {
28068         if (!this.editing) {
28069             this.toggleTextEdit();
28070         }
28071         
28072     }
28073
28074
28075 });/*
28076  * Based on:
28077  * Ext JS Library 1.1.1
28078  * Copyright(c) 2006-2007, Ext JS, LLC.
28079  *
28080  * Originally Released Under LGPL - original licence link has changed is not relivant.
28081  *
28082  * Fork - LGPL
28083  * <script type="text/javascript">
28084  */
28085  
28086 /**
28087  * @class Roo.bootstrap.PagingToolbar
28088  * @extends Roo.bootstrap.NavSimplebar
28089  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28090  * @constructor
28091  * Create a new PagingToolbar
28092  * @param {Object} config The config object
28093  * @param {Roo.data.Store} store
28094  */
28095 Roo.bootstrap.PagingToolbar = function(config)
28096 {
28097     // old args format still supported... - xtype is prefered..
28098         // created from xtype...
28099     
28100     this.ds = config.dataSource;
28101     
28102     if (config.store && !this.ds) {
28103         this.store= Roo.factory(config.store, Roo.data);
28104         this.ds = this.store;
28105         this.ds.xmodule = this.xmodule || false;
28106     }
28107     
28108     this.toolbarItems = [];
28109     if (config.items) {
28110         this.toolbarItems = config.items;
28111     }
28112     
28113     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28114     
28115     this.cursor = 0;
28116     
28117     if (this.ds) { 
28118         this.bind(this.ds);
28119     }
28120     
28121     if (Roo.bootstrap.version == 4) {
28122         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28123     } else {
28124         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28125     }
28126     
28127 };
28128
28129 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28130     /**
28131      * @cfg {Roo.data.Store} dataSource
28132      * The underlying data store providing the paged data
28133      */
28134     /**
28135      * @cfg {String/HTMLElement/Element} container
28136      * container The id or element that will contain the toolbar
28137      */
28138     /**
28139      * @cfg {Boolean} displayInfo
28140      * True to display the displayMsg (defaults to false)
28141      */
28142     /**
28143      * @cfg {Number} pageSize
28144      * The number of records to display per page (defaults to 20)
28145      */
28146     pageSize: 20,
28147     /**
28148      * @cfg {String} displayMsg
28149      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28150      */
28151     displayMsg : 'Displaying {0} - {1} of {2}',
28152     /**
28153      * @cfg {String} emptyMsg
28154      * The message to display when no records are found (defaults to "No data to display")
28155      */
28156     emptyMsg : 'No data to display',
28157     /**
28158      * Customizable piece of the default paging text (defaults to "Page")
28159      * @type String
28160      */
28161     beforePageText : "Page",
28162     /**
28163      * Customizable piece of the default paging text (defaults to "of %0")
28164      * @type String
28165      */
28166     afterPageText : "of {0}",
28167     /**
28168      * Customizable piece of the default paging text (defaults to "First Page")
28169      * @type String
28170      */
28171     firstText : "First Page",
28172     /**
28173      * Customizable piece of the default paging text (defaults to "Previous Page")
28174      * @type String
28175      */
28176     prevText : "Previous Page",
28177     /**
28178      * Customizable piece of the default paging text (defaults to "Next Page")
28179      * @type String
28180      */
28181     nextText : "Next Page",
28182     /**
28183      * Customizable piece of the default paging text (defaults to "Last Page")
28184      * @type String
28185      */
28186     lastText : "Last Page",
28187     /**
28188      * Customizable piece of the default paging text (defaults to "Refresh")
28189      * @type String
28190      */
28191     refreshText : "Refresh",
28192
28193     buttons : false,
28194     // private
28195     onRender : function(ct, position) 
28196     {
28197         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28198         this.navgroup.parentId = this.id;
28199         this.navgroup.onRender(this.el, null);
28200         // add the buttons to the navgroup
28201         
28202         if(this.displayInfo){
28203             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28204             this.displayEl = this.el.select('.x-paging-info', true).first();
28205 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28206 //            this.displayEl = navel.el.select('span',true).first();
28207         }
28208         
28209         var _this = this;
28210         
28211         if(this.buttons){
28212             Roo.each(_this.buttons, function(e){ // this might need to use render????
28213                Roo.factory(e).render(_this.el);
28214             });
28215         }
28216             
28217         Roo.each(_this.toolbarItems, function(e) {
28218             _this.navgroup.addItem(e);
28219         });
28220         
28221         
28222         this.first = this.navgroup.addItem({
28223             tooltip: this.firstText,
28224             cls: "prev btn-outline-secondary",
28225             html : ' <i class="fa fa-step-backward"></i>',
28226             disabled: true,
28227             preventDefault: true,
28228             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28229         });
28230         
28231         this.prev =  this.navgroup.addItem({
28232             tooltip: this.prevText,
28233             cls: "prev btn-outline-secondary",
28234             html : ' <i class="fa fa-backward"></i>',
28235             disabled: true,
28236             preventDefault: true,
28237             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28238         });
28239     //this.addSeparator();
28240         
28241         
28242         var field = this.navgroup.addItem( {
28243             tagtype : 'span',
28244             cls : 'x-paging-position  btn-outline-secondary',
28245              disabled: true,
28246             html : this.beforePageText  +
28247                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28248                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28249          } ); //?? escaped?
28250         
28251         this.field = field.el.select('input', true).first();
28252         this.field.on("keydown", this.onPagingKeydown, this);
28253         this.field.on("focus", function(){this.dom.select();});
28254     
28255     
28256         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28257         //this.field.setHeight(18);
28258         //this.addSeparator();
28259         this.next = this.navgroup.addItem({
28260             tooltip: this.nextText,
28261             cls: "next btn-outline-secondary",
28262             html : ' <i class="fa fa-forward"></i>',
28263             disabled: true,
28264             preventDefault: true,
28265             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28266         });
28267         this.last = this.navgroup.addItem({
28268             tooltip: this.lastText,
28269             html : ' <i class="fa fa-step-forward"></i>',
28270             cls: "next btn-outline-secondary",
28271             disabled: true,
28272             preventDefault: true,
28273             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28274         });
28275     //this.addSeparator();
28276         this.loading = this.navgroup.addItem({
28277             tooltip: this.refreshText,
28278             cls: "btn-outline-secondary",
28279             html : ' <i class="fa fa-refresh"></i>',
28280             preventDefault: true,
28281             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28282         });
28283         
28284     },
28285
28286     // private
28287     updateInfo : function(){
28288         if(this.displayEl){
28289             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28290             var msg = count == 0 ?
28291                 this.emptyMsg :
28292                 String.format(
28293                     this.displayMsg,
28294                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28295                 );
28296             this.displayEl.update(msg);
28297         }
28298     },
28299
28300     // private
28301     onLoad : function(ds, r, o)
28302     {
28303         this.cursor = o.params && o.params.start ? o.params.start : 0;
28304         
28305         var d = this.getPageData(),
28306             ap = d.activePage,
28307             ps = d.pages;
28308         
28309         
28310         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28311         this.field.dom.value = ap;
28312         this.first.setDisabled(ap == 1);
28313         this.prev.setDisabled(ap == 1);
28314         this.next.setDisabled(ap == ps);
28315         this.last.setDisabled(ap == ps);
28316         this.loading.enable();
28317         this.updateInfo();
28318     },
28319
28320     // private
28321     getPageData : function(){
28322         var total = this.ds.getTotalCount();
28323         return {
28324             total : total,
28325             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28326             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28327         };
28328     },
28329
28330     // private
28331     onLoadError : function(){
28332         this.loading.enable();
28333     },
28334
28335     // private
28336     onPagingKeydown : function(e){
28337         var k = e.getKey();
28338         var d = this.getPageData();
28339         if(k == e.RETURN){
28340             var v = this.field.dom.value, pageNum;
28341             if(!v || isNaN(pageNum = parseInt(v, 10))){
28342                 this.field.dom.value = d.activePage;
28343                 return;
28344             }
28345             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28346             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28347             e.stopEvent();
28348         }
28349         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))
28350         {
28351           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28352           this.field.dom.value = pageNum;
28353           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28354           e.stopEvent();
28355         }
28356         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28357         {
28358           var v = this.field.dom.value, pageNum; 
28359           var increment = (e.shiftKey) ? 10 : 1;
28360           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28361                 increment *= -1;
28362           }
28363           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28364             this.field.dom.value = d.activePage;
28365             return;
28366           }
28367           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28368           {
28369             this.field.dom.value = parseInt(v, 10) + increment;
28370             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28371             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28372           }
28373           e.stopEvent();
28374         }
28375     },
28376
28377     // private
28378     beforeLoad : function(){
28379         if(this.loading){
28380             this.loading.disable();
28381         }
28382     },
28383
28384     // private
28385     onClick : function(which){
28386         
28387         var ds = this.ds;
28388         if (!ds) {
28389             return;
28390         }
28391         
28392         switch(which){
28393             case "first":
28394                 ds.load({params:{start: 0, limit: this.pageSize}});
28395             break;
28396             case "prev":
28397                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28398             break;
28399             case "next":
28400                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28401             break;
28402             case "last":
28403                 var total = ds.getTotalCount();
28404                 var extra = total % this.pageSize;
28405                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28406                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28407             break;
28408             case "refresh":
28409                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28410             break;
28411         }
28412     },
28413
28414     /**
28415      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28416      * @param {Roo.data.Store} store The data store to unbind
28417      */
28418     unbind : function(ds){
28419         ds.un("beforeload", this.beforeLoad, this);
28420         ds.un("load", this.onLoad, this);
28421         ds.un("loadexception", this.onLoadError, this);
28422         ds.un("remove", this.updateInfo, this);
28423         ds.un("add", this.updateInfo, this);
28424         this.ds = undefined;
28425     },
28426
28427     /**
28428      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28429      * @param {Roo.data.Store} store The data store to bind
28430      */
28431     bind : function(ds){
28432         ds.on("beforeload", this.beforeLoad, this);
28433         ds.on("load", this.onLoad, this);
28434         ds.on("loadexception", this.onLoadError, this);
28435         ds.on("remove", this.updateInfo, this);
28436         ds.on("add", this.updateInfo, this);
28437         this.ds = ds;
28438     }
28439 });/*
28440  * - LGPL
28441  *
28442  * element
28443  * 
28444  */
28445
28446 /**
28447  * @class Roo.bootstrap.MessageBar
28448  * @extends Roo.bootstrap.Component
28449  * Bootstrap MessageBar class
28450  * @cfg {String} html contents of the MessageBar
28451  * @cfg {String} weight (info | success | warning | danger) default info
28452  * @cfg {String} beforeClass insert the bar before the given class
28453  * @cfg {Boolean} closable (true | false) default false
28454  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28455  * 
28456  * @constructor
28457  * Create a new Element
28458  * @param {Object} config The config object
28459  */
28460
28461 Roo.bootstrap.MessageBar = function(config){
28462     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28463 };
28464
28465 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28466     
28467     html: '',
28468     weight: 'info',
28469     closable: false,
28470     fixed: false,
28471     beforeClass: 'bootstrap-sticky-wrap',
28472     
28473     getAutoCreate : function(){
28474         
28475         var cfg = {
28476             tag: 'div',
28477             cls: 'alert alert-dismissable alert-' + this.weight,
28478             cn: [
28479                 {
28480                     tag: 'span',
28481                     cls: 'message',
28482                     html: this.html || ''
28483                 }
28484             ]
28485         };
28486         
28487         if(this.fixed){
28488             cfg.cls += ' alert-messages-fixed';
28489         }
28490         
28491         if(this.closable){
28492             cfg.cn.push({
28493                 tag: 'button',
28494                 cls: 'close',
28495                 html: 'x'
28496             });
28497         }
28498         
28499         return cfg;
28500     },
28501     
28502     onRender : function(ct, position)
28503     {
28504         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28505         
28506         if(!this.el){
28507             var cfg = Roo.apply({},  this.getAutoCreate());
28508             cfg.id = Roo.id();
28509             
28510             if (this.cls) {
28511                 cfg.cls += ' ' + this.cls;
28512             }
28513             if (this.style) {
28514                 cfg.style = this.style;
28515             }
28516             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28517             
28518             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28519         }
28520         
28521         this.el.select('>button.close').on('click', this.hide, this);
28522         
28523     },
28524     
28525     show : function()
28526     {
28527         if (!this.rendered) {
28528             this.render();
28529         }
28530         
28531         this.el.show();
28532         
28533         this.fireEvent('show', this);
28534         
28535     },
28536     
28537     hide : function()
28538     {
28539         if (!this.rendered) {
28540             this.render();
28541         }
28542         
28543         this.el.hide();
28544         
28545         this.fireEvent('hide', this);
28546     },
28547     
28548     update : function()
28549     {
28550 //        var e = this.el.dom.firstChild;
28551 //        
28552 //        if(this.closable){
28553 //            e = e.nextSibling;
28554 //        }
28555 //        
28556 //        e.data = this.html || '';
28557
28558         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28559     }
28560    
28561 });
28562
28563  
28564
28565      /*
28566  * - LGPL
28567  *
28568  * Graph
28569  * 
28570  */
28571
28572
28573 /**
28574  * @class Roo.bootstrap.Graph
28575  * @extends Roo.bootstrap.Component
28576  * Bootstrap Graph class
28577 > Prameters
28578  -sm {number} sm 4
28579  -md {number} md 5
28580  @cfg {String} graphtype  bar | vbar | pie
28581  @cfg {number} g_x coodinator | centre x (pie)
28582  @cfg {number} g_y coodinator | centre y (pie)
28583  @cfg {number} g_r radius (pie)
28584  @cfg {number} g_height height of the chart (respected by all elements in the set)
28585  @cfg {number} g_width width of the chart (respected by all elements in the set)
28586  @cfg {Object} title The title of the chart
28587     
28588  -{Array}  values
28589  -opts (object) options for the chart 
28590      o {
28591      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28592      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28593      o vgutter (number)
28594      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.
28595      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28596      o to
28597      o stretch (boolean)
28598      o }
28599  -opts (object) options for the pie
28600      o{
28601      o cut
28602      o startAngle (number)
28603      o endAngle (number)
28604      } 
28605  *
28606  * @constructor
28607  * Create a new Input
28608  * @param {Object} config The config object
28609  */
28610
28611 Roo.bootstrap.Graph = function(config){
28612     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28613     
28614     this.addEvents({
28615         // img events
28616         /**
28617          * @event click
28618          * The img click event for the img.
28619          * @param {Roo.EventObject} e
28620          */
28621         "click" : true
28622     });
28623 };
28624
28625 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28626     
28627     sm: 4,
28628     md: 5,
28629     graphtype: 'bar',
28630     g_height: 250,
28631     g_width: 400,
28632     g_x: 50,
28633     g_y: 50,
28634     g_r: 30,
28635     opts:{
28636         //g_colors: this.colors,
28637         g_type: 'soft',
28638         g_gutter: '20%'
28639
28640     },
28641     title : false,
28642
28643     getAutoCreate : function(){
28644         
28645         var cfg = {
28646             tag: 'div',
28647             html : null
28648         };
28649         
28650         
28651         return  cfg;
28652     },
28653
28654     onRender : function(ct,position){
28655         
28656         
28657         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28658         
28659         if (typeof(Raphael) == 'undefined') {
28660             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28661             return;
28662         }
28663         
28664         this.raphael = Raphael(this.el.dom);
28665         
28666                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28667                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28668                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28669                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28670                 /*
28671                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28672                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28673                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28674                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28675                 
28676                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28677                 r.barchart(330, 10, 300, 220, data1);
28678                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28679                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28680                 */
28681                 
28682                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28683                 // r.barchart(30, 30, 560, 250,  xdata, {
28684                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28685                 //     axis : "0 0 1 1",
28686                 //     axisxlabels :  xdata
28687                 //     //yvalues : cols,
28688                    
28689                 // });
28690 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28691 //        
28692 //        this.load(null,xdata,{
28693 //                axis : "0 0 1 1",
28694 //                axisxlabels :  xdata
28695 //                });
28696
28697     },
28698
28699     load : function(graphtype,xdata,opts)
28700     {
28701         this.raphael.clear();
28702         if(!graphtype) {
28703             graphtype = this.graphtype;
28704         }
28705         if(!opts){
28706             opts = this.opts;
28707         }
28708         var r = this.raphael,
28709             fin = function () {
28710                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28711             },
28712             fout = function () {
28713                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28714             },
28715             pfin = function() {
28716                 this.sector.stop();
28717                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28718
28719                 if (this.label) {
28720                     this.label[0].stop();
28721                     this.label[0].attr({ r: 7.5 });
28722                     this.label[1].attr({ "font-weight": 800 });
28723                 }
28724             },
28725             pfout = function() {
28726                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28727
28728                 if (this.label) {
28729                     this.label[0].animate({ r: 5 }, 500, "bounce");
28730                     this.label[1].attr({ "font-weight": 400 });
28731                 }
28732             };
28733
28734         switch(graphtype){
28735             case 'bar':
28736                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28737                 break;
28738             case 'hbar':
28739                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28740                 break;
28741             case 'pie':
28742 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28743 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28744 //            
28745                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28746                 
28747                 break;
28748
28749         }
28750         
28751         if(this.title){
28752             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28753         }
28754         
28755     },
28756     
28757     setTitle: function(o)
28758     {
28759         this.title = o;
28760     },
28761     
28762     initEvents: function() {
28763         
28764         if(!this.href){
28765             this.el.on('click', this.onClick, this);
28766         }
28767     },
28768     
28769     onClick : function(e)
28770     {
28771         Roo.log('img onclick');
28772         this.fireEvent('click', this, e);
28773     }
28774    
28775 });
28776
28777  
28778 /*
28779  * - LGPL
28780  *
28781  * numberBox
28782  * 
28783  */
28784 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28785
28786 /**
28787  * @class Roo.bootstrap.dash.NumberBox
28788  * @extends Roo.bootstrap.Component
28789  * Bootstrap NumberBox class
28790  * @cfg {String} headline Box headline
28791  * @cfg {String} content Box content
28792  * @cfg {String} icon Box icon
28793  * @cfg {String} footer Footer text
28794  * @cfg {String} fhref Footer href
28795  * 
28796  * @constructor
28797  * Create a new NumberBox
28798  * @param {Object} config The config object
28799  */
28800
28801
28802 Roo.bootstrap.dash.NumberBox = function(config){
28803     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28804     
28805 };
28806
28807 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28808     
28809     headline : '',
28810     content : '',
28811     icon : '',
28812     footer : '',
28813     fhref : '',
28814     ficon : '',
28815     
28816     getAutoCreate : function(){
28817         
28818         var cfg = {
28819             tag : 'div',
28820             cls : 'small-box ',
28821             cn : [
28822                 {
28823                     tag : 'div',
28824                     cls : 'inner',
28825                     cn :[
28826                         {
28827                             tag : 'h3',
28828                             cls : 'roo-headline',
28829                             html : this.headline
28830                         },
28831                         {
28832                             tag : 'p',
28833                             cls : 'roo-content',
28834                             html : this.content
28835                         }
28836                     ]
28837                 }
28838             ]
28839         };
28840         
28841         if(this.icon){
28842             cfg.cn.push({
28843                 tag : 'div',
28844                 cls : 'icon',
28845                 cn :[
28846                     {
28847                         tag : 'i',
28848                         cls : 'ion ' + this.icon
28849                     }
28850                 ]
28851             });
28852         }
28853         
28854         if(this.footer){
28855             var footer = {
28856                 tag : 'a',
28857                 cls : 'small-box-footer',
28858                 href : this.fhref || '#',
28859                 html : this.footer
28860             };
28861             
28862             cfg.cn.push(footer);
28863             
28864         }
28865         
28866         return  cfg;
28867     },
28868
28869     onRender : function(ct,position){
28870         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28871
28872
28873        
28874                 
28875     },
28876
28877     setHeadline: function (value)
28878     {
28879         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28880     },
28881     
28882     setFooter: function (value, href)
28883     {
28884         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28885         
28886         if(href){
28887             this.el.select('a.small-box-footer',true).first().attr('href', href);
28888         }
28889         
28890     },
28891
28892     setContent: function (value)
28893     {
28894         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28895     },
28896
28897     initEvents: function() 
28898     {   
28899         
28900     }
28901     
28902 });
28903
28904  
28905 /*
28906  * - LGPL
28907  *
28908  * TabBox
28909  * 
28910  */
28911 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28912
28913 /**
28914  * @class Roo.bootstrap.dash.TabBox
28915  * @extends Roo.bootstrap.Component
28916  * Bootstrap TabBox class
28917  * @cfg {String} title Title of the TabBox
28918  * @cfg {String} icon Icon of the TabBox
28919  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28920  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28921  * 
28922  * @constructor
28923  * Create a new TabBox
28924  * @param {Object} config The config object
28925  */
28926
28927
28928 Roo.bootstrap.dash.TabBox = function(config){
28929     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28930     this.addEvents({
28931         // raw events
28932         /**
28933          * @event addpane
28934          * When a pane is added
28935          * @param {Roo.bootstrap.dash.TabPane} pane
28936          */
28937         "addpane" : true,
28938         /**
28939          * @event activatepane
28940          * When a pane is activated
28941          * @param {Roo.bootstrap.dash.TabPane} pane
28942          */
28943         "activatepane" : true
28944         
28945          
28946     });
28947     
28948     this.panes = [];
28949 };
28950
28951 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28952
28953     title : '',
28954     icon : false,
28955     showtabs : true,
28956     tabScrollable : false,
28957     
28958     getChildContainer : function()
28959     {
28960         return this.el.select('.tab-content', true).first();
28961     },
28962     
28963     getAutoCreate : function(){
28964         
28965         var header = {
28966             tag: 'li',
28967             cls: 'pull-left header',
28968             html: this.title,
28969             cn : []
28970         };
28971         
28972         if(this.icon){
28973             header.cn.push({
28974                 tag: 'i',
28975                 cls: 'fa ' + this.icon
28976             });
28977         }
28978         
28979         var h = {
28980             tag: 'ul',
28981             cls: 'nav nav-tabs pull-right',
28982             cn: [
28983                 header
28984             ]
28985         };
28986         
28987         if(this.tabScrollable){
28988             h = {
28989                 tag: 'div',
28990                 cls: 'tab-header',
28991                 cn: [
28992                     {
28993                         tag: 'ul',
28994                         cls: 'nav nav-tabs pull-right',
28995                         cn: [
28996                             header
28997                         ]
28998                     }
28999                 ]
29000             };
29001         }
29002         
29003         var cfg = {
29004             tag: 'div',
29005             cls: 'nav-tabs-custom',
29006             cn: [
29007                 h,
29008                 {
29009                     tag: 'div',
29010                     cls: 'tab-content no-padding',
29011                     cn: []
29012                 }
29013             ]
29014         };
29015
29016         return  cfg;
29017     },
29018     initEvents : function()
29019     {
29020         //Roo.log('add add pane handler');
29021         this.on('addpane', this.onAddPane, this);
29022     },
29023      /**
29024      * Updates the box title
29025      * @param {String} html to set the title to.
29026      */
29027     setTitle : function(value)
29028     {
29029         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29030     },
29031     onAddPane : function(pane)
29032     {
29033         this.panes.push(pane);
29034         //Roo.log('addpane');
29035         //Roo.log(pane);
29036         // tabs are rendere left to right..
29037         if(!this.showtabs){
29038             return;
29039         }
29040         
29041         var ctr = this.el.select('.nav-tabs', true).first();
29042          
29043          
29044         var existing = ctr.select('.nav-tab',true);
29045         var qty = existing.getCount();;
29046         
29047         
29048         var tab = ctr.createChild({
29049             tag : 'li',
29050             cls : 'nav-tab' + (qty ? '' : ' active'),
29051             cn : [
29052                 {
29053                     tag : 'a',
29054                     href:'#',
29055                     html : pane.title
29056                 }
29057             ]
29058         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29059         pane.tab = tab;
29060         
29061         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29062         if (!qty) {
29063             pane.el.addClass('active');
29064         }
29065         
29066                 
29067     },
29068     onTabClick : function(ev,un,ob,pane)
29069     {
29070         //Roo.log('tab - prev default');
29071         ev.preventDefault();
29072         
29073         
29074         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29075         pane.tab.addClass('active');
29076         //Roo.log(pane.title);
29077         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29078         // technically we should have a deactivate event.. but maybe add later.
29079         // and it should not de-activate the selected tab...
29080         this.fireEvent('activatepane', pane);
29081         pane.el.addClass('active');
29082         pane.fireEvent('activate');
29083         
29084         
29085     },
29086     
29087     getActivePane : function()
29088     {
29089         var r = false;
29090         Roo.each(this.panes, function(p) {
29091             if(p.el.hasClass('active')){
29092                 r = p;
29093                 return false;
29094             }
29095             
29096             return;
29097         });
29098         
29099         return r;
29100     }
29101     
29102     
29103 });
29104
29105  
29106 /*
29107  * - LGPL
29108  *
29109  * Tab pane
29110  * 
29111  */
29112 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29113 /**
29114  * @class Roo.bootstrap.TabPane
29115  * @extends Roo.bootstrap.Component
29116  * Bootstrap TabPane class
29117  * @cfg {Boolean} active (false | true) Default false
29118  * @cfg {String} title title of panel
29119
29120  * 
29121  * @constructor
29122  * Create a new TabPane
29123  * @param {Object} config The config object
29124  */
29125
29126 Roo.bootstrap.dash.TabPane = function(config){
29127     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29128     
29129     this.addEvents({
29130         // raw events
29131         /**
29132          * @event activate
29133          * When a pane is activated
29134          * @param {Roo.bootstrap.dash.TabPane} pane
29135          */
29136         "activate" : true
29137          
29138     });
29139 };
29140
29141 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29142     
29143     active : false,
29144     title : '',
29145     
29146     // the tabBox that this is attached to.
29147     tab : false,
29148      
29149     getAutoCreate : function() 
29150     {
29151         var cfg = {
29152             tag: 'div',
29153             cls: 'tab-pane'
29154         };
29155         
29156         if(this.active){
29157             cfg.cls += ' active';
29158         }
29159         
29160         return cfg;
29161     },
29162     initEvents  : function()
29163     {
29164         //Roo.log('trigger add pane handler');
29165         this.parent().fireEvent('addpane', this)
29166     },
29167     
29168      /**
29169      * Updates the tab title 
29170      * @param {String} html to set the title to.
29171      */
29172     setTitle: function(str)
29173     {
29174         if (!this.tab) {
29175             return;
29176         }
29177         this.title = str;
29178         this.tab.select('a', true).first().dom.innerHTML = str;
29179         
29180     }
29181     
29182     
29183     
29184 });
29185
29186  
29187
29188
29189  /*
29190  * - LGPL
29191  *
29192  * menu
29193  * 
29194  */
29195 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29196
29197 /**
29198  * @class Roo.bootstrap.menu.Menu
29199  * @extends Roo.bootstrap.Component
29200  * Bootstrap Menu class - container for Menu
29201  * @cfg {String} html Text of the menu
29202  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29203  * @cfg {String} icon Font awesome icon
29204  * @cfg {String} pos Menu align to (top | bottom) default bottom
29205  * 
29206  * 
29207  * @constructor
29208  * Create a new Menu
29209  * @param {Object} config The config object
29210  */
29211
29212
29213 Roo.bootstrap.menu.Menu = function(config){
29214     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29215     
29216     this.addEvents({
29217         /**
29218          * @event beforeshow
29219          * Fires before this menu is displayed
29220          * @param {Roo.bootstrap.menu.Menu} this
29221          */
29222         beforeshow : true,
29223         /**
29224          * @event beforehide
29225          * Fires before this menu is hidden
29226          * @param {Roo.bootstrap.menu.Menu} this
29227          */
29228         beforehide : true,
29229         /**
29230          * @event show
29231          * Fires after this menu is displayed
29232          * @param {Roo.bootstrap.menu.Menu} this
29233          */
29234         show : true,
29235         /**
29236          * @event hide
29237          * Fires after this menu is hidden
29238          * @param {Roo.bootstrap.menu.Menu} this
29239          */
29240         hide : true,
29241         /**
29242          * @event click
29243          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29244          * @param {Roo.bootstrap.menu.Menu} this
29245          * @param {Roo.EventObject} e
29246          */
29247         click : true
29248     });
29249     
29250 };
29251
29252 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29253     
29254     submenu : false,
29255     html : '',
29256     weight : 'default',
29257     icon : false,
29258     pos : 'bottom',
29259     
29260     
29261     getChildContainer : function() {
29262         if(this.isSubMenu){
29263             return this.el;
29264         }
29265         
29266         return this.el.select('ul.dropdown-menu', true).first();  
29267     },
29268     
29269     getAutoCreate : function()
29270     {
29271         var text = [
29272             {
29273                 tag : 'span',
29274                 cls : 'roo-menu-text',
29275                 html : this.html
29276             }
29277         ];
29278         
29279         if(this.icon){
29280             text.unshift({
29281                 tag : 'i',
29282                 cls : 'fa ' + this.icon
29283             })
29284         }
29285         
29286         
29287         var cfg = {
29288             tag : 'div',
29289             cls : 'btn-group',
29290             cn : [
29291                 {
29292                     tag : 'button',
29293                     cls : 'dropdown-button btn btn-' + this.weight,
29294                     cn : text
29295                 },
29296                 {
29297                     tag : 'button',
29298                     cls : 'dropdown-toggle btn btn-' + this.weight,
29299                     cn : [
29300                         {
29301                             tag : 'span',
29302                             cls : 'caret'
29303                         }
29304                     ]
29305                 },
29306                 {
29307                     tag : 'ul',
29308                     cls : 'dropdown-menu'
29309                 }
29310             ]
29311             
29312         };
29313         
29314         if(this.pos == 'top'){
29315             cfg.cls += ' dropup';
29316         }
29317         
29318         if(this.isSubMenu){
29319             cfg = {
29320                 tag : 'ul',
29321                 cls : 'dropdown-menu'
29322             }
29323         }
29324         
29325         return cfg;
29326     },
29327     
29328     onRender : function(ct, position)
29329     {
29330         this.isSubMenu = ct.hasClass('dropdown-submenu');
29331         
29332         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29333     },
29334     
29335     initEvents : function() 
29336     {
29337         if(this.isSubMenu){
29338             return;
29339         }
29340         
29341         this.hidden = true;
29342         
29343         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29344         this.triggerEl.on('click', this.onTriggerPress, this);
29345         
29346         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29347         this.buttonEl.on('click', this.onClick, this);
29348         
29349     },
29350     
29351     list : function()
29352     {
29353         if(this.isSubMenu){
29354             return this.el;
29355         }
29356         
29357         return this.el.select('ul.dropdown-menu', true).first();
29358     },
29359     
29360     onClick : function(e)
29361     {
29362         this.fireEvent("click", this, e);
29363     },
29364     
29365     onTriggerPress  : function(e)
29366     {   
29367         if (this.isVisible()) {
29368             this.hide();
29369         } else {
29370             this.show();
29371         }
29372     },
29373     
29374     isVisible : function(){
29375         return !this.hidden;
29376     },
29377     
29378     show : function()
29379     {
29380         this.fireEvent("beforeshow", this);
29381         
29382         this.hidden = false;
29383         this.el.addClass('open');
29384         
29385         Roo.get(document).on("mouseup", this.onMouseUp, this);
29386         
29387         this.fireEvent("show", this);
29388         
29389         
29390     },
29391     
29392     hide : function()
29393     {
29394         this.fireEvent("beforehide", this);
29395         
29396         this.hidden = true;
29397         this.el.removeClass('open');
29398         
29399         Roo.get(document).un("mouseup", this.onMouseUp);
29400         
29401         this.fireEvent("hide", this);
29402     },
29403     
29404     onMouseUp : function()
29405     {
29406         this.hide();
29407     }
29408     
29409 });
29410
29411  
29412  /*
29413  * - LGPL
29414  *
29415  * menu item
29416  * 
29417  */
29418 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29419
29420 /**
29421  * @class Roo.bootstrap.menu.Item
29422  * @extends Roo.bootstrap.Component
29423  * Bootstrap MenuItem class
29424  * @cfg {Boolean} submenu (true | false) default false
29425  * @cfg {String} html text of the item
29426  * @cfg {String} href the link
29427  * @cfg {Boolean} disable (true | false) default false
29428  * @cfg {Boolean} preventDefault (true | false) default true
29429  * @cfg {String} icon Font awesome icon
29430  * @cfg {String} pos Submenu align to (left | right) default right 
29431  * 
29432  * 
29433  * @constructor
29434  * Create a new Item
29435  * @param {Object} config The config object
29436  */
29437
29438
29439 Roo.bootstrap.menu.Item = function(config){
29440     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29441     this.addEvents({
29442         /**
29443          * @event mouseover
29444          * Fires when the mouse is hovering over this menu
29445          * @param {Roo.bootstrap.menu.Item} this
29446          * @param {Roo.EventObject} e
29447          */
29448         mouseover : true,
29449         /**
29450          * @event mouseout
29451          * Fires when the mouse exits this menu
29452          * @param {Roo.bootstrap.menu.Item} this
29453          * @param {Roo.EventObject} e
29454          */
29455         mouseout : true,
29456         // raw events
29457         /**
29458          * @event click
29459          * The raw click event for the entire grid.
29460          * @param {Roo.EventObject} e
29461          */
29462         click : true
29463     });
29464 };
29465
29466 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29467     
29468     submenu : false,
29469     href : '',
29470     html : '',
29471     preventDefault: true,
29472     disable : false,
29473     icon : false,
29474     pos : 'right',
29475     
29476     getAutoCreate : function()
29477     {
29478         var text = [
29479             {
29480                 tag : 'span',
29481                 cls : 'roo-menu-item-text',
29482                 html : this.html
29483             }
29484         ];
29485         
29486         if(this.icon){
29487             text.unshift({
29488                 tag : 'i',
29489                 cls : 'fa ' + this.icon
29490             })
29491         }
29492         
29493         var cfg = {
29494             tag : 'li',
29495             cn : [
29496                 {
29497                     tag : 'a',
29498                     href : this.href || '#',
29499                     cn : text
29500                 }
29501             ]
29502         };
29503         
29504         if(this.disable){
29505             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29506         }
29507         
29508         if(this.submenu){
29509             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29510             
29511             if(this.pos == 'left'){
29512                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29513             }
29514         }
29515         
29516         return cfg;
29517     },
29518     
29519     initEvents : function() 
29520     {
29521         this.el.on('mouseover', this.onMouseOver, this);
29522         this.el.on('mouseout', this.onMouseOut, this);
29523         
29524         this.el.select('a', true).first().on('click', this.onClick, this);
29525         
29526     },
29527     
29528     onClick : function(e)
29529     {
29530         if(this.preventDefault){
29531             e.preventDefault();
29532         }
29533         
29534         this.fireEvent("click", this, e);
29535     },
29536     
29537     onMouseOver : function(e)
29538     {
29539         if(this.submenu && this.pos == 'left'){
29540             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29541         }
29542         
29543         this.fireEvent("mouseover", this, e);
29544     },
29545     
29546     onMouseOut : function(e)
29547     {
29548         this.fireEvent("mouseout", this, e);
29549     }
29550 });
29551
29552  
29553
29554  /*
29555  * - LGPL
29556  *
29557  * menu separator
29558  * 
29559  */
29560 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29561
29562 /**
29563  * @class Roo.bootstrap.menu.Separator
29564  * @extends Roo.bootstrap.Component
29565  * Bootstrap Separator class
29566  * 
29567  * @constructor
29568  * Create a new Separator
29569  * @param {Object} config The config object
29570  */
29571
29572
29573 Roo.bootstrap.menu.Separator = function(config){
29574     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29575 };
29576
29577 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29578     
29579     getAutoCreate : function(){
29580         var cfg = {
29581             tag : 'li',
29582             cls: 'dropdown-divider divider'
29583         };
29584         
29585         return cfg;
29586     }
29587    
29588 });
29589
29590  
29591
29592  /*
29593  * - LGPL
29594  *
29595  * Tooltip
29596  * 
29597  */
29598
29599 /**
29600  * @class Roo.bootstrap.Tooltip
29601  * Bootstrap Tooltip class
29602  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29603  * to determine which dom element triggers the tooltip.
29604  * 
29605  * It needs to add support for additional attributes like tooltip-position
29606  * 
29607  * @constructor
29608  * Create a new Toolti
29609  * @param {Object} config The config object
29610  */
29611
29612 Roo.bootstrap.Tooltip = function(config){
29613     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29614     
29615     this.alignment = Roo.bootstrap.Tooltip.alignment;
29616     
29617     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29618         this.alignment = config.alignment;
29619     }
29620     
29621 };
29622
29623 Roo.apply(Roo.bootstrap.Tooltip, {
29624     /**
29625      * @function init initialize tooltip monitoring.
29626      * @static
29627      */
29628     currentEl : false,
29629     currentTip : false,
29630     currentRegion : false,
29631     
29632     //  init : delay?
29633     
29634     init : function()
29635     {
29636         Roo.get(document).on('mouseover', this.enter ,this);
29637         Roo.get(document).on('mouseout', this.leave, this);
29638          
29639         
29640         this.currentTip = new Roo.bootstrap.Tooltip();
29641     },
29642     
29643     enter : function(ev)
29644     {
29645         var dom = ev.getTarget();
29646         
29647         //Roo.log(['enter',dom]);
29648         var el = Roo.fly(dom);
29649         if (this.currentEl) {
29650             //Roo.log(dom);
29651             //Roo.log(this.currentEl);
29652             //Roo.log(this.currentEl.contains(dom));
29653             if (this.currentEl == el) {
29654                 return;
29655             }
29656             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29657                 return;
29658             }
29659
29660         }
29661         
29662         if (this.currentTip.el) {
29663             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29664         }    
29665         //Roo.log(ev);
29666         
29667         if(!el || el.dom == document){
29668             return;
29669         }
29670         
29671         var bindEl = el; 
29672         var pel = false;
29673         if (!el.attr('tooltip')) {
29674             pel = el.findParent("[tooltip]");
29675             if (pel) {
29676                 bindEl = Roo.get(pel);
29677             }
29678         }
29679         
29680        
29681         
29682         // you can not look for children, as if el is the body.. then everythign is the child..
29683         if (!pel && !el.attr('tooltip')) { //
29684             if (!el.select("[tooltip]").elements.length) {
29685                 return;
29686             }
29687             // is the mouse over this child...?
29688             bindEl = el.select("[tooltip]").first();
29689             var xy = ev.getXY();
29690             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29691                 //Roo.log("not in region.");
29692                 return;
29693             }
29694             //Roo.log("child element over..");
29695             
29696         }
29697         this.currentEl = el;
29698         this.currentTip.bind(bindEl);
29699         this.currentRegion = Roo.lib.Region.getRegion(dom);
29700         this.currentTip.enter();
29701         
29702     },
29703     leave : function(ev)
29704     {
29705         var dom = ev.getTarget();
29706         //Roo.log(['leave',dom]);
29707         if (!this.currentEl) {
29708             return;
29709         }
29710         
29711         
29712         if (dom != this.currentEl.dom) {
29713             return;
29714         }
29715         var xy = ev.getXY();
29716         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29717             return;
29718         }
29719         // only activate leave if mouse cursor is outside... bounding box..
29720         
29721         
29722         
29723         
29724         if (this.currentTip) {
29725             this.currentTip.leave();
29726         }
29727         //Roo.log('clear currentEl');
29728         this.currentEl = false;
29729         
29730         
29731     },
29732     alignment : {
29733         'left' : ['r-l', [-2,0], 'right'],
29734         'right' : ['l-r', [2,0], 'left'],
29735         'bottom' : ['t-b', [0,2], 'top'],
29736         'top' : [ 'b-t', [0,-2], 'bottom']
29737     }
29738     
29739 });
29740
29741
29742 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29743     
29744     
29745     bindEl : false,
29746     
29747     delay : null, // can be { show : 300 , hide: 500}
29748     
29749     timeout : null,
29750     
29751     hoverState : null, //???
29752     
29753     placement : 'bottom', 
29754     
29755     alignment : false,
29756     
29757     getAutoCreate : function(){
29758     
29759         var cfg = {
29760            cls : 'tooltip',   
29761            role : 'tooltip',
29762            cn : [
29763                 {
29764                     cls : 'tooltip-arrow arrow'
29765                 },
29766                 {
29767                     cls : 'tooltip-inner'
29768                 }
29769            ]
29770         };
29771         
29772         return cfg;
29773     },
29774     bind : function(el)
29775     {
29776         this.bindEl = el;
29777     },
29778     
29779     initEvents : function()
29780     {
29781         this.arrowEl = this.el.select('.arrow', true).first();
29782         this.innerEl = this.el.select('.tooltip-inner', true).first();
29783     },
29784     
29785     enter : function () {
29786        
29787         if (this.timeout != null) {
29788             clearTimeout(this.timeout);
29789         }
29790         
29791         this.hoverState = 'in';
29792          //Roo.log("enter - show");
29793         if (!this.delay || !this.delay.show) {
29794             this.show();
29795             return;
29796         }
29797         var _t = this;
29798         this.timeout = setTimeout(function () {
29799             if (_t.hoverState == 'in') {
29800                 _t.show();
29801             }
29802         }, this.delay.show);
29803     },
29804     leave : function()
29805     {
29806         clearTimeout(this.timeout);
29807     
29808         this.hoverState = 'out';
29809          if (!this.delay || !this.delay.hide) {
29810             this.hide();
29811             return;
29812         }
29813        
29814         var _t = this;
29815         this.timeout = setTimeout(function () {
29816             //Roo.log("leave - timeout");
29817             
29818             if (_t.hoverState == 'out') {
29819                 _t.hide();
29820                 Roo.bootstrap.Tooltip.currentEl = false;
29821             }
29822         }, delay);
29823     },
29824     
29825     show : function (msg)
29826     {
29827         if (!this.el) {
29828             this.render(document.body);
29829         }
29830         // set content.
29831         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29832         
29833         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29834         
29835         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29836         
29837         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29838                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29839         
29840         var placement = typeof this.placement == 'function' ?
29841             this.placement.call(this, this.el, on_el) :
29842             this.placement;
29843             
29844         var autoToken = /\s?auto?\s?/i;
29845         var autoPlace = autoToken.test(placement);
29846         if (autoPlace) {
29847             placement = placement.replace(autoToken, '') || 'top';
29848         }
29849         
29850         //this.el.detach()
29851         //this.el.setXY([0,0]);
29852         this.el.show();
29853         //this.el.dom.style.display='block';
29854         
29855         //this.el.appendTo(on_el);
29856         
29857         var p = this.getPosition();
29858         var box = this.el.getBox();
29859         
29860         if (autoPlace) {
29861             // fixme..
29862         }
29863         
29864         var align = this.alignment[placement];
29865         
29866         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29867         
29868         if(placement == 'top' || placement == 'bottom'){
29869             if(xy[0] < 0){
29870                 placement = 'right';
29871             }
29872             
29873             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29874                 placement = 'left';
29875             }
29876             
29877             var scroll = Roo.select('body', true).first().getScroll();
29878             
29879             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29880                 placement = 'top';
29881             }
29882             
29883             align = this.alignment[placement];
29884             
29885             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29886             
29887         }
29888         
29889         var elems = document.getElementsByTagName('div');
29890         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29891         for (var i = 0; i < elems.length; i++) {
29892           var zindex = Number.parseInt(
29893                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29894                 10
29895           );
29896           if (zindex > highest) {
29897             highest = zindex;
29898           }
29899         }
29900         
29901         
29902         
29903         this.el.dom.style.zIndex = highest;
29904         
29905         this.el.alignTo(this.bindEl, align[0],align[1]);
29906         //var arrow = this.el.select('.arrow',true).first();
29907         //arrow.set(align[2], 
29908         
29909         this.el.addClass(placement);
29910         this.el.addClass("bs-tooltip-"+ placement);
29911         
29912         this.el.addClass('in fade show');
29913         
29914         this.hoverState = null;
29915         
29916         if (this.el.hasClass('fade')) {
29917             // fade it?
29918         }
29919         
29920         
29921         
29922         
29923         
29924     },
29925     hide : function()
29926     {
29927          
29928         if (!this.el) {
29929             return;
29930         }
29931         //this.el.setXY([0,0]);
29932         this.el.removeClass(['show', 'in']);
29933         //this.el.hide();
29934         
29935     }
29936     
29937 });
29938  
29939
29940  /*
29941  * - LGPL
29942  *
29943  * Location Picker
29944  * 
29945  */
29946
29947 /**
29948  * @class Roo.bootstrap.LocationPicker
29949  * @extends Roo.bootstrap.Component
29950  * Bootstrap LocationPicker class
29951  * @cfg {Number} latitude Position when init default 0
29952  * @cfg {Number} longitude Position when init default 0
29953  * @cfg {Number} zoom default 15
29954  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29955  * @cfg {Boolean} mapTypeControl default false
29956  * @cfg {Boolean} disableDoubleClickZoom default false
29957  * @cfg {Boolean} scrollwheel default true
29958  * @cfg {Boolean} streetViewControl default false
29959  * @cfg {Number} radius default 0
29960  * @cfg {String} locationName
29961  * @cfg {Boolean} draggable default true
29962  * @cfg {Boolean} enableAutocomplete default false
29963  * @cfg {Boolean} enableReverseGeocode default true
29964  * @cfg {String} markerTitle
29965  * 
29966  * @constructor
29967  * Create a new LocationPicker
29968  * @param {Object} config The config object
29969  */
29970
29971
29972 Roo.bootstrap.LocationPicker = function(config){
29973     
29974     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29975     
29976     this.addEvents({
29977         /**
29978          * @event initial
29979          * Fires when the picker initialized.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          * @param {Google Location} location
29982          */
29983         initial : true,
29984         /**
29985          * @event positionchanged
29986          * Fires when the picker position changed.
29987          * @param {Roo.bootstrap.LocationPicker} this
29988          * @param {Google Location} location
29989          */
29990         positionchanged : true,
29991         /**
29992          * @event resize
29993          * Fires when the map resize.
29994          * @param {Roo.bootstrap.LocationPicker} this
29995          */
29996         resize : true,
29997         /**
29998          * @event show
29999          * Fires when the map show.
30000          * @param {Roo.bootstrap.LocationPicker} this
30001          */
30002         show : true,
30003         /**
30004          * @event hide
30005          * Fires when the map hide.
30006          * @param {Roo.bootstrap.LocationPicker} this
30007          */
30008         hide : true,
30009         /**
30010          * @event mapClick
30011          * Fires when click the map.
30012          * @param {Roo.bootstrap.LocationPicker} this
30013          * @param {Map event} e
30014          */
30015         mapClick : true,
30016         /**
30017          * @event mapRightClick
30018          * Fires when right click the map.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Map event} e
30021          */
30022         mapRightClick : true,
30023         /**
30024          * @event markerClick
30025          * Fires when click the marker.
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Map event} e
30028          */
30029         markerClick : true,
30030         /**
30031          * @event markerRightClick
30032          * Fires when right click the marker.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          * @param {Map event} e
30035          */
30036         markerRightClick : true,
30037         /**
30038          * @event OverlayViewDraw
30039          * Fires when OverlayView Draw
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          */
30042         OverlayViewDraw : true,
30043         /**
30044          * @event OverlayViewOnAdd
30045          * Fires when OverlayView Draw
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          */
30048         OverlayViewOnAdd : true,
30049         /**
30050          * @event OverlayViewOnRemove
30051          * Fires when OverlayView Draw
30052          * @param {Roo.bootstrap.LocationPicker} this
30053          */
30054         OverlayViewOnRemove : true,
30055         /**
30056          * @event OverlayViewShow
30057          * Fires when OverlayView Draw
30058          * @param {Roo.bootstrap.LocationPicker} this
30059          * @param {Pixel} cpx
30060          */
30061         OverlayViewShow : true,
30062         /**
30063          * @event OverlayViewHide
30064          * Fires when OverlayView Draw
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          */
30067         OverlayViewHide : true,
30068         /**
30069          * @event loadexception
30070          * Fires when load google lib failed.
30071          * @param {Roo.bootstrap.LocationPicker} this
30072          */
30073         loadexception : true
30074     });
30075         
30076 };
30077
30078 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30079     
30080     gMapContext: false,
30081     
30082     latitude: 0,
30083     longitude: 0,
30084     zoom: 15,
30085     mapTypeId: false,
30086     mapTypeControl: false,
30087     disableDoubleClickZoom: false,
30088     scrollwheel: true,
30089     streetViewControl: false,
30090     radius: 0,
30091     locationName: '',
30092     draggable: true,
30093     enableAutocomplete: false,
30094     enableReverseGeocode: true,
30095     markerTitle: '',
30096     
30097     getAutoCreate: function()
30098     {
30099
30100         var cfg = {
30101             tag: 'div',
30102             cls: 'roo-location-picker'
30103         };
30104         
30105         return cfg
30106     },
30107     
30108     initEvents: function(ct, position)
30109     {       
30110         if(!this.el.getWidth() || this.isApplied()){
30111             return;
30112         }
30113         
30114         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30115         
30116         this.initial();
30117     },
30118     
30119     initial: function()
30120     {
30121         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30122             this.fireEvent('loadexception', this);
30123             return;
30124         }
30125         
30126         if(!this.mapTypeId){
30127             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30128         }
30129         
30130         this.gMapContext = this.GMapContext();
30131         
30132         this.initOverlayView();
30133         
30134         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30135         
30136         var _this = this;
30137                 
30138         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30139             _this.setPosition(_this.gMapContext.marker.position);
30140         });
30141         
30142         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30143             _this.fireEvent('mapClick', this, event);
30144             
30145         });
30146
30147         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30148             _this.fireEvent('mapRightClick', this, event);
30149             
30150         });
30151         
30152         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30153             _this.fireEvent('markerClick', this, event);
30154             
30155         });
30156
30157         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30158             _this.fireEvent('markerRightClick', this, event);
30159             
30160         });
30161         
30162         this.setPosition(this.gMapContext.location);
30163         
30164         this.fireEvent('initial', this, this.gMapContext.location);
30165     },
30166     
30167     initOverlayView: function()
30168     {
30169         var _this = this;
30170         
30171         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30172             
30173             draw: function()
30174             {
30175                 _this.fireEvent('OverlayViewDraw', _this);
30176             },
30177             
30178             onAdd: function()
30179             {
30180                 _this.fireEvent('OverlayViewOnAdd', _this);
30181             },
30182             
30183             onRemove: function()
30184             {
30185                 _this.fireEvent('OverlayViewOnRemove', _this);
30186             },
30187             
30188             show: function(cpx)
30189             {
30190                 _this.fireEvent('OverlayViewShow', _this, cpx);
30191             },
30192             
30193             hide: function()
30194             {
30195                 _this.fireEvent('OverlayViewHide', _this);
30196             }
30197             
30198         });
30199     },
30200     
30201     fromLatLngToContainerPixel: function(event)
30202     {
30203         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30204     },
30205     
30206     isApplied: function() 
30207     {
30208         return this.getGmapContext() == false ? false : true;
30209     },
30210     
30211     getGmapContext: function() 
30212     {
30213         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30214     },
30215     
30216     GMapContext: function() 
30217     {
30218         var position = new google.maps.LatLng(this.latitude, this.longitude);
30219         
30220         var _map = new google.maps.Map(this.el.dom, {
30221             center: position,
30222             zoom: this.zoom,
30223             mapTypeId: this.mapTypeId,
30224             mapTypeControl: this.mapTypeControl,
30225             disableDoubleClickZoom: this.disableDoubleClickZoom,
30226             scrollwheel: this.scrollwheel,
30227             streetViewControl: this.streetViewControl,
30228             locationName: this.locationName,
30229             draggable: this.draggable,
30230             enableAutocomplete: this.enableAutocomplete,
30231             enableReverseGeocode: this.enableReverseGeocode
30232         });
30233         
30234         var _marker = new google.maps.Marker({
30235             position: position,
30236             map: _map,
30237             title: this.markerTitle,
30238             draggable: this.draggable
30239         });
30240         
30241         return {
30242             map: _map,
30243             marker: _marker,
30244             circle: null,
30245             location: position,
30246             radius: this.radius,
30247             locationName: this.locationName,
30248             addressComponents: {
30249                 formatted_address: null,
30250                 addressLine1: null,
30251                 addressLine2: null,
30252                 streetName: null,
30253                 streetNumber: null,
30254                 city: null,
30255                 district: null,
30256                 state: null,
30257                 stateOrProvince: null
30258             },
30259             settings: this,
30260             domContainer: this.el.dom,
30261             geodecoder: new google.maps.Geocoder()
30262         };
30263     },
30264     
30265     drawCircle: function(center, radius, options) 
30266     {
30267         if (this.gMapContext.circle != null) {
30268             this.gMapContext.circle.setMap(null);
30269         }
30270         if (radius > 0) {
30271             radius *= 1;
30272             options = Roo.apply({}, options, {
30273                 strokeColor: "#0000FF",
30274                 strokeOpacity: .35,
30275                 strokeWeight: 2,
30276                 fillColor: "#0000FF",
30277                 fillOpacity: .2
30278             });
30279             
30280             options.map = this.gMapContext.map;
30281             options.radius = radius;
30282             options.center = center;
30283             this.gMapContext.circle = new google.maps.Circle(options);
30284             return this.gMapContext.circle;
30285         }
30286         
30287         return null;
30288     },
30289     
30290     setPosition: function(location) 
30291     {
30292         this.gMapContext.location = location;
30293         this.gMapContext.marker.setPosition(location);
30294         this.gMapContext.map.panTo(location);
30295         this.drawCircle(location, this.gMapContext.radius, {});
30296         
30297         var _this = this;
30298         
30299         if (this.gMapContext.settings.enableReverseGeocode) {
30300             this.gMapContext.geodecoder.geocode({
30301                 latLng: this.gMapContext.location
30302             }, function(results, status) {
30303                 
30304                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30305                     _this.gMapContext.locationName = results[0].formatted_address;
30306                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30307                     
30308                     _this.fireEvent('positionchanged', this, location);
30309                 }
30310             });
30311             
30312             return;
30313         }
30314         
30315         this.fireEvent('positionchanged', this, location);
30316     },
30317     
30318     resize: function()
30319     {
30320         google.maps.event.trigger(this.gMapContext.map, "resize");
30321         
30322         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30323         
30324         this.fireEvent('resize', this);
30325     },
30326     
30327     setPositionByLatLng: function(latitude, longitude)
30328     {
30329         this.setPosition(new google.maps.LatLng(latitude, longitude));
30330     },
30331     
30332     getCurrentPosition: function() 
30333     {
30334         return {
30335             latitude: this.gMapContext.location.lat(),
30336             longitude: this.gMapContext.location.lng()
30337         };
30338     },
30339     
30340     getAddressName: function() 
30341     {
30342         return this.gMapContext.locationName;
30343     },
30344     
30345     getAddressComponents: function() 
30346     {
30347         return this.gMapContext.addressComponents;
30348     },
30349     
30350     address_component_from_google_geocode: function(address_components) 
30351     {
30352         var result = {};
30353         
30354         for (var i = 0; i < address_components.length; i++) {
30355             var component = address_components[i];
30356             if (component.types.indexOf("postal_code") >= 0) {
30357                 result.postalCode = component.short_name;
30358             } else if (component.types.indexOf("street_number") >= 0) {
30359                 result.streetNumber = component.short_name;
30360             } else if (component.types.indexOf("route") >= 0) {
30361                 result.streetName = component.short_name;
30362             } else if (component.types.indexOf("neighborhood") >= 0) {
30363                 result.city = component.short_name;
30364             } else if (component.types.indexOf("locality") >= 0) {
30365                 result.city = component.short_name;
30366             } else if (component.types.indexOf("sublocality") >= 0) {
30367                 result.district = component.short_name;
30368             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30369                 result.stateOrProvince = component.short_name;
30370             } else if (component.types.indexOf("country") >= 0) {
30371                 result.country = component.short_name;
30372             }
30373         }
30374         
30375         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30376         result.addressLine2 = "";
30377         return result;
30378     },
30379     
30380     setZoomLevel: function(zoom)
30381     {
30382         this.gMapContext.map.setZoom(zoom);
30383     },
30384     
30385     show: function()
30386     {
30387         if(!this.el){
30388             return;
30389         }
30390         
30391         this.el.show();
30392         
30393         this.resize();
30394         
30395         this.fireEvent('show', this);
30396     },
30397     
30398     hide: function()
30399     {
30400         if(!this.el){
30401             return;
30402         }
30403         
30404         this.el.hide();
30405         
30406         this.fireEvent('hide', this);
30407     }
30408     
30409 });
30410
30411 Roo.apply(Roo.bootstrap.LocationPicker, {
30412     
30413     OverlayView : function(map, options)
30414     {
30415         options = options || {};
30416         
30417         this.setMap(map);
30418     }
30419     
30420     
30421 });/**
30422  * @class Roo.bootstrap.Alert
30423  * @extends Roo.bootstrap.Component
30424  * Bootstrap Alert class - shows an alert area box
30425  * eg
30426  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30427   Enter a valid email address
30428 </div>
30429  * @licence LGPL
30430  * @cfg {String} title The title of alert
30431  * @cfg {String} html The content of alert
30432  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30433  * @cfg {String} fa font-awesomeicon
30434  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30435  * @cfg {Boolean} close true to show a x closer
30436  * 
30437  * 
30438  * @constructor
30439  * Create a new alert
30440  * @param {Object} config The config object
30441  */
30442
30443
30444 Roo.bootstrap.Alert = function(config){
30445     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30446     
30447 };
30448
30449 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30450     
30451     title: '',
30452     html: '',
30453     weight: false,
30454     fa: false,
30455     faicon: false, // BC
30456     close : false,
30457     
30458     
30459     getAutoCreate : function()
30460     {
30461         
30462         var cfg = {
30463             tag : 'div',
30464             cls : 'alert',
30465             cn : [
30466                 {
30467                     tag: 'button',
30468                     type :  "button",
30469                     cls: "close",
30470                     html : '×',
30471                     style : this.close ? '' : 'display:none'
30472                 },
30473                 {
30474                     tag : 'i',
30475                     cls : 'roo-alert-icon'
30476                     
30477                 },
30478                 {
30479                     tag : 'b',
30480                     cls : 'roo-alert-title',
30481                     html : this.title
30482                 },
30483                 {
30484                     tag : 'span',
30485                     cls : 'roo-alert-text',
30486                     html : this.html
30487                 }
30488             ]
30489         };
30490         
30491         if(this.faicon){
30492             cfg.cn[0].cls += ' fa ' + this.faicon;
30493         }
30494         if(this.fa){
30495             cfg.cn[0].cls += ' fa ' + this.fa;
30496         }
30497         
30498         if(this.weight){
30499             cfg.cls += ' alert-' + this.weight;
30500         }
30501         
30502         return cfg;
30503     },
30504     
30505     initEvents: function() 
30506     {
30507         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30508         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30509         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30510         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30511         if (this.seconds > 0) {
30512             this.hide.defer(this.seconds, this);
30513         }
30514     },
30515     /**
30516      * Set the Title Message HTML
30517      * @param {String} html
30518      */
30519     setTitle : function(str)
30520     {
30521         this.titleEl.dom.innerHTML = str;
30522     },
30523      
30524      /**
30525      * Set the Body Message HTML
30526      * @param {String} html
30527      */
30528     setHtml : function(str)
30529     {
30530         this.htmlEl.dom.innerHTML = str;
30531     },
30532     /**
30533      * Set the Weight of the alert
30534      * @param {String} (success|info|warning|danger) weight
30535      */
30536     
30537     setWeight : function(weight)
30538     {
30539         if(this.weight){
30540             this.el.removeClass('alert-' + this.weight);
30541         }
30542         
30543         this.weight = weight;
30544         
30545         this.el.addClass('alert-' + this.weight);
30546     },
30547       /**
30548      * Set the Icon of the alert
30549      * @param {String} see fontawsome names (name without the 'fa-' bit)
30550      */
30551     setIcon : function(icon)
30552     {
30553         if(this.faicon){
30554             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30555         }
30556         
30557         this.faicon = icon;
30558         
30559         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30560     },
30561     /**
30562      * Hide the Alert
30563      */
30564     hide: function() 
30565     {
30566         this.el.hide();   
30567     },
30568     /**
30569      * Show the Alert
30570      */
30571     show: function() 
30572     {  
30573         this.el.show();   
30574     }
30575     
30576 });
30577
30578  
30579 /*
30580 * Licence: LGPL
30581 */
30582
30583 /**
30584  * @class Roo.bootstrap.UploadCropbox
30585  * @extends Roo.bootstrap.Component
30586  * Bootstrap UploadCropbox class
30587  * @cfg {String} emptyText show when image has been loaded
30588  * @cfg {String} rotateNotify show when image too small to rotate
30589  * @cfg {Number} errorTimeout default 3000
30590  * @cfg {Number} minWidth default 300
30591  * @cfg {Number} minHeight default 300
30592  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30593  * @cfg {Boolean} isDocument (true|false) default false
30594  * @cfg {String} url action url
30595  * @cfg {String} paramName default 'imageUpload'
30596  * @cfg {String} method default POST
30597  * @cfg {Boolean} loadMask (true|false) default true
30598  * @cfg {Boolean} loadingText default 'Loading...'
30599  * 
30600  * @constructor
30601  * Create a new UploadCropbox
30602  * @param {Object} config The config object
30603  */
30604
30605 Roo.bootstrap.UploadCropbox = function(config){
30606     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30607     
30608     this.addEvents({
30609         /**
30610          * @event beforeselectfile
30611          * Fire before select file
30612          * @param {Roo.bootstrap.UploadCropbox} this
30613          */
30614         "beforeselectfile" : true,
30615         /**
30616          * @event initial
30617          * Fire after initEvent
30618          * @param {Roo.bootstrap.UploadCropbox} this
30619          */
30620         "initial" : true,
30621         /**
30622          * @event crop
30623          * Fire after initEvent
30624          * @param {Roo.bootstrap.UploadCropbox} this
30625          * @param {String} data
30626          */
30627         "crop" : true,
30628         /**
30629          * @event prepare
30630          * Fire when preparing the file data
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          * @param {Object} file
30633          */
30634         "prepare" : true,
30635         /**
30636          * @event exception
30637          * Fire when get exception
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          * @param {XMLHttpRequest} xhr
30640          */
30641         "exception" : true,
30642         /**
30643          * @event beforeloadcanvas
30644          * Fire before load the canvas
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          * @param {String} src
30647          */
30648         "beforeloadcanvas" : true,
30649         /**
30650          * @event trash
30651          * Fire when trash image
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          */
30654         "trash" : true,
30655         /**
30656          * @event download
30657          * Fire when download the image
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          */
30660         "download" : true,
30661         /**
30662          * @event footerbuttonclick
30663          * Fire when footerbuttonclick
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          * @param {String} type
30666          */
30667         "footerbuttonclick" : true,
30668         /**
30669          * @event resize
30670          * Fire when resize
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          */
30673         "resize" : true,
30674         /**
30675          * @event rotate
30676          * Fire when rotate the image
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {String} pos
30679          */
30680         "rotate" : true,
30681         /**
30682          * @event inspect
30683          * Fire when inspect the file
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {Object} file
30686          */
30687         "inspect" : true,
30688         /**
30689          * @event upload
30690          * Fire when xhr upload the file
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          * @param {Object} data
30693          */
30694         "upload" : true,
30695         /**
30696          * @event arrange
30697          * Fire when arrange the file data
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          * @param {Object} formData
30700          */
30701         "arrange" : true
30702     });
30703     
30704     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30705 };
30706
30707 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30708     
30709     emptyText : 'Click to upload image',
30710     rotateNotify : 'Image is too small to rotate',
30711     errorTimeout : 3000,
30712     scale : 0,
30713     baseScale : 1,
30714     rotate : 0,
30715     dragable : false,
30716     pinching : false,
30717     mouseX : 0,
30718     mouseY : 0,
30719     cropData : false,
30720     minWidth : 300,
30721     minHeight : 300,
30722     file : false,
30723     exif : {},
30724     baseRotate : 1,
30725     cropType : 'image/jpeg',
30726     buttons : false,
30727     canvasLoaded : false,
30728     isDocument : false,
30729     method : 'POST',
30730     paramName : 'imageUpload',
30731     loadMask : true,
30732     loadingText : 'Loading...',
30733     maskEl : false,
30734     
30735     getAutoCreate : function()
30736     {
30737         var cfg = {
30738             tag : 'div',
30739             cls : 'roo-upload-cropbox',
30740             cn : [
30741                 {
30742                     tag : 'input',
30743                     cls : 'roo-upload-cropbox-selector',
30744                     type : 'file'
30745                 },
30746                 {
30747                     tag : 'div',
30748                     cls : 'roo-upload-cropbox-body',
30749                     style : 'cursor:pointer',
30750                     cn : [
30751                         {
30752                             tag : 'div',
30753                             cls : 'roo-upload-cropbox-preview'
30754                         },
30755                         {
30756                             tag : 'div',
30757                             cls : 'roo-upload-cropbox-thumb'
30758                         },
30759                         {
30760                             tag : 'div',
30761                             cls : 'roo-upload-cropbox-empty-notify',
30762                             html : this.emptyText
30763                         },
30764                         {
30765                             tag : 'div',
30766                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30767                             html : this.rotateNotify
30768                         }
30769                     ]
30770                 },
30771                 {
30772                     tag : 'div',
30773                     cls : 'roo-upload-cropbox-footer',
30774                     cn : {
30775                         tag : 'div',
30776                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30777                         cn : []
30778                     }
30779                 }
30780             ]
30781         };
30782         
30783         return cfg;
30784     },
30785     
30786     onRender : function(ct, position)
30787     {
30788         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30789         
30790         if (this.buttons.length) {
30791             
30792             Roo.each(this.buttons, function(bb) {
30793                 
30794                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30795                 
30796                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30797                 
30798             }, this);
30799         }
30800         
30801         if(this.loadMask){
30802             this.maskEl = this.el;
30803         }
30804     },
30805     
30806     initEvents : function()
30807     {
30808         this.urlAPI = (window.createObjectURL && window) || 
30809                                 (window.URL && URL.revokeObjectURL && URL) || 
30810                                 (window.webkitURL && webkitURL);
30811                         
30812         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30813         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30814         
30815         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30816         this.selectorEl.hide();
30817         
30818         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30819         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820         
30821         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30822         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823         this.thumbEl.hide();
30824         
30825         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30826         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30827         
30828         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30829         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30830         this.errorEl.hide();
30831         
30832         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30833         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30834         this.footerEl.hide();
30835         
30836         this.setThumbBoxSize();
30837         
30838         this.bind();
30839         
30840         this.resize();
30841         
30842         this.fireEvent('initial', this);
30843     },
30844
30845     bind : function()
30846     {
30847         var _this = this;
30848         
30849         window.addEventListener("resize", function() { _this.resize(); } );
30850         
30851         this.bodyEl.on('click', this.beforeSelectFile, this);
30852         
30853         if(Roo.isTouch){
30854             this.bodyEl.on('touchstart', this.onTouchStart, this);
30855             this.bodyEl.on('touchmove', this.onTouchMove, this);
30856             this.bodyEl.on('touchend', this.onTouchEnd, this);
30857         }
30858         
30859         if(!Roo.isTouch){
30860             this.bodyEl.on('mousedown', this.onMouseDown, this);
30861             this.bodyEl.on('mousemove', this.onMouseMove, this);
30862             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30863             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30864             Roo.get(document).on('mouseup', this.onMouseUp, this);
30865         }
30866         
30867         this.selectorEl.on('change', this.onFileSelected, this);
30868     },
30869     
30870     reset : function()
30871     {    
30872         this.scale = 0;
30873         this.baseScale = 1;
30874         this.rotate = 0;
30875         this.baseRotate = 1;
30876         this.dragable = false;
30877         this.pinching = false;
30878         this.mouseX = 0;
30879         this.mouseY = 0;
30880         this.cropData = false;
30881         this.notifyEl.dom.innerHTML = this.emptyText;
30882         
30883         this.selectorEl.dom.value = '';
30884         
30885     },
30886     
30887     resize : function()
30888     {
30889         if(this.fireEvent('resize', this) != false){
30890             this.setThumbBoxPosition();
30891             this.setCanvasPosition();
30892         }
30893     },
30894     
30895     onFooterButtonClick : function(e, el, o, type)
30896     {
30897         switch (type) {
30898             case 'rotate-left' :
30899                 this.onRotateLeft(e);
30900                 break;
30901             case 'rotate-right' :
30902                 this.onRotateRight(e);
30903                 break;
30904             case 'picture' :
30905                 this.beforeSelectFile(e);
30906                 break;
30907             case 'trash' :
30908                 this.trash(e);
30909                 break;
30910             case 'crop' :
30911                 this.crop(e);
30912                 break;
30913             case 'download' :
30914                 this.download(e);
30915                 break;
30916             default :
30917                 break;
30918         }
30919         
30920         this.fireEvent('footerbuttonclick', this, type);
30921     },
30922     
30923     beforeSelectFile : function(e)
30924     {
30925         e.preventDefault();
30926         
30927         if(this.fireEvent('beforeselectfile', this) != false){
30928             this.selectorEl.dom.click();
30929         }
30930     },
30931     
30932     onFileSelected : function(e)
30933     {
30934         e.preventDefault();
30935         
30936         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30937             return;
30938         }
30939         
30940         var file = this.selectorEl.dom.files[0];
30941         
30942         if(this.fireEvent('inspect', this, file) != false){
30943             this.prepare(file);
30944         }
30945         
30946     },
30947     
30948     trash : function(e)
30949     {
30950         this.fireEvent('trash', this);
30951     },
30952     
30953     download : function(e)
30954     {
30955         this.fireEvent('download', this);
30956     },
30957     
30958     loadCanvas : function(src)
30959     {   
30960         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30961             
30962             this.reset();
30963             
30964             this.imageEl = document.createElement('img');
30965             
30966             var _this = this;
30967             
30968             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30969             
30970             this.imageEl.src = src;
30971         }
30972     },
30973     
30974     onLoadCanvas : function()
30975     {   
30976         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30977         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30978         
30979         this.bodyEl.un('click', this.beforeSelectFile, this);
30980         
30981         this.notifyEl.hide();
30982         this.thumbEl.show();
30983         this.footerEl.show();
30984         
30985         this.baseRotateLevel();
30986         
30987         if(this.isDocument){
30988             this.setThumbBoxSize();
30989         }
30990         
30991         this.setThumbBoxPosition();
30992         
30993         this.baseScaleLevel();
30994         
30995         this.draw();
30996         
30997         this.resize();
30998         
30999         this.canvasLoaded = true;
31000         
31001         if(this.loadMask){
31002             this.maskEl.unmask();
31003         }
31004         
31005     },
31006     
31007     setCanvasPosition : function()
31008     {   
31009         if(!this.canvasEl){
31010             return;
31011         }
31012         
31013         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31014         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31015         
31016         this.previewEl.setLeft(pw);
31017         this.previewEl.setTop(ph);
31018         
31019     },
31020     
31021     onMouseDown : function(e)
31022     {   
31023         e.stopEvent();
31024         
31025         this.dragable = true;
31026         this.pinching = false;
31027         
31028         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31029             this.dragable = false;
31030             return;
31031         }
31032         
31033         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31034         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31035         
31036     },
31037     
31038     onMouseMove : function(e)
31039     {   
31040         e.stopEvent();
31041         
31042         if(!this.canvasLoaded){
31043             return;
31044         }
31045         
31046         if (!this.dragable){
31047             return;
31048         }
31049         
31050         var minX = Math.ceil(this.thumbEl.getLeft(true));
31051         var minY = Math.ceil(this.thumbEl.getTop(true));
31052         
31053         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31054         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31055         
31056         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31057         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31058         
31059         x = x - this.mouseX;
31060         y = y - this.mouseY;
31061         
31062         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31063         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31064         
31065         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31066         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31067         
31068         this.previewEl.setLeft(bgX);
31069         this.previewEl.setTop(bgY);
31070         
31071         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31072         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31073     },
31074     
31075     onMouseUp : function(e)
31076     {   
31077         e.stopEvent();
31078         
31079         this.dragable = false;
31080     },
31081     
31082     onMouseWheel : function(e)
31083     {   
31084         e.stopEvent();
31085         
31086         this.startScale = this.scale;
31087         
31088         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31089         
31090         if(!this.zoomable()){
31091             this.scale = this.startScale;
31092             return;
31093         }
31094         
31095         this.draw();
31096         
31097         return;
31098     },
31099     
31100     zoomable : function()
31101     {
31102         var minScale = this.thumbEl.getWidth() / this.minWidth;
31103         
31104         if(this.minWidth < this.minHeight){
31105             minScale = this.thumbEl.getHeight() / this.minHeight;
31106         }
31107         
31108         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31109         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31110         
31111         if(
31112                 this.isDocument &&
31113                 (this.rotate == 0 || this.rotate == 180) && 
31114                 (
31115                     width > this.imageEl.OriginWidth || 
31116                     height > this.imageEl.OriginHeight ||
31117                     (width < this.minWidth && height < this.minHeight)
31118                 )
31119         ){
31120             return false;
31121         }
31122         
31123         if(
31124                 this.isDocument &&
31125                 (this.rotate == 90 || this.rotate == 270) && 
31126                 (
31127                     width > this.imageEl.OriginWidth || 
31128                     height > this.imageEl.OriginHeight ||
31129                     (width < this.minHeight && height < this.minWidth)
31130                 )
31131         ){
31132             return false;
31133         }
31134         
31135         if(
31136                 !this.isDocument &&
31137                 (this.rotate == 0 || this.rotate == 180) && 
31138                 (
31139                     width < this.minWidth || 
31140                     width > this.imageEl.OriginWidth || 
31141                     height < this.minHeight || 
31142                     height > this.imageEl.OriginHeight
31143                 )
31144         ){
31145             return false;
31146         }
31147         
31148         if(
31149                 !this.isDocument &&
31150                 (this.rotate == 90 || this.rotate == 270) && 
31151                 (
31152                     width < this.minHeight || 
31153                     width > this.imageEl.OriginWidth || 
31154                     height < this.minWidth || 
31155                     height > this.imageEl.OriginHeight
31156                 )
31157         ){
31158             return false;
31159         }
31160         
31161         return true;
31162         
31163     },
31164     
31165     onRotateLeft : function(e)
31166     {   
31167         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31168             
31169             var minScale = this.thumbEl.getWidth() / this.minWidth;
31170             
31171             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31172             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31173             
31174             this.startScale = this.scale;
31175             
31176             while (this.getScaleLevel() < minScale){
31177             
31178                 this.scale = this.scale + 1;
31179                 
31180                 if(!this.zoomable()){
31181                     break;
31182                 }
31183                 
31184                 if(
31185                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31186                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31187                 ){
31188                     continue;
31189                 }
31190                 
31191                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31192
31193                 this.draw();
31194                 
31195                 return;
31196             }
31197             
31198             this.scale = this.startScale;
31199             
31200             this.onRotateFail();
31201             
31202             return false;
31203         }
31204         
31205         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31206
31207         if(this.isDocument){
31208             this.setThumbBoxSize();
31209             this.setThumbBoxPosition();
31210             this.setCanvasPosition();
31211         }
31212         
31213         this.draw();
31214         
31215         this.fireEvent('rotate', this, 'left');
31216         
31217     },
31218     
31219     onRotateRight : function(e)
31220     {
31221         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31222             
31223             var minScale = this.thumbEl.getWidth() / this.minWidth;
31224         
31225             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31226             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31227             
31228             this.startScale = this.scale;
31229             
31230             while (this.getScaleLevel() < minScale){
31231             
31232                 this.scale = this.scale + 1;
31233                 
31234                 if(!this.zoomable()){
31235                     break;
31236                 }
31237                 
31238                 if(
31239                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31240                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31241                 ){
31242                     continue;
31243                 }
31244                 
31245                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31246
31247                 this.draw();
31248                 
31249                 return;
31250             }
31251             
31252             this.scale = this.startScale;
31253             
31254             this.onRotateFail();
31255             
31256             return false;
31257         }
31258         
31259         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31260
31261         if(this.isDocument){
31262             this.setThumbBoxSize();
31263             this.setThumbBoxPosition();
31264             this.setCanvasPosition();
31265         }
31266         
31267         this.draw();
31268         
31269         this.fireEvent('rotate', this, 'right');
31270     },
31271     
31272     onRotateFail : function()
31273     {
31274         this.errorEl.show(true);
31275         
31276         var _this = this;
31277         
31278         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31279     },
31280     
31281     draw : function()
31282     {
31283         this.previewEl.dom.innerHTML = '';
31284         
31285         var canvasEl = document.createElement("canvas");
31286         
31287         var contextEl = canvasEl.getContext("2d");
31288         
31289         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31290         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31291         var center = this.imageEl.OriginWidth / 2;
31292         
31293         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31294             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31295             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31296             center = this.imageEl.OriginHeight / 2;
31297         }
31298         
31299         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31300         
31301         contextEl.translate(center, center);
31302         contextEl.rotate(this.rotate * Math.PI / 180);
31303
31304         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31305         
31306         this.canvasEl = document.createElement("canvas");
31307         
31308         this.contextEl = this.canvasEl.getContext("2d");
31309         
31310         switch (this.rotate) {
31311             case 0 :
31312                 
31313                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31314                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31315                 
31316                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31317                 
31318                 break;
31319             case 90 : 
31320                 
31321                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31322                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31323                 
31324                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31325                     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);
31326                     break;
31327                 }
31328                 
31329                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31330                 
31331                 break;
31332             case 180 :
31333                 
31334                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31335                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31336                 
31337                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31338                     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);
31339                     break;
31340                 }
31341                 
31342                 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);
31343                 
31344                 break;
31345             case 270 :
31346                 
31347                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31348                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31349         
31350                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31351                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31352                     break;
31353                 }
31354                 
31355                 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);
31356                 
31357                 break;
31358             default : 
31359                 break;
31360         }
31361         
31362         this.previewEl.appendChild(this.canvasEl);
31363         
31364         this.setCanvasPosition();
31365     },
31366     
31367     crop : function()
31368     {
31369         if(!this.canvasLoaded){
31370             return;
31371         }
31372         
31373         var imageCanvas = document.createElement("canvas");
31374         
31375         var imageContext = imageCanvas.getContext("2d");
31376         
31377         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31378         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31379         
31380         var center = imageCanvas.width / 2;
31381         
31382         imageContext.translate(center, center);
31383         
31384         imageContext.rotate(this.rotate * Math.PI / 180);
31385         
31386         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31387         
31388         var canvas = document.createElement("canvas");
31389         
31390         var context = canvas.getContext("2d");
31391                 
31392         canvas.width = this.minWidth;
31393         canvas.height = this.minHeight;
31394
31395         switch (this.rotate) {
31396             case 0 :
31397                 
31398                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31399                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31400                 
31401                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31402                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31403                 
31404                 var targetWidth = this.minWidth - 2 * x;
31405                 var targetHeight = this.minHeight - 2 * y;
31406                 
31407                 var scale = 1;
31408                 
31409                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31410                     scale = targetWidth / width;
31411                 }
31412                 
31413                 if(x > 0 && y == 0){
31414                     scale = targetHeight / height;
31415                 }
31416                 
31417                 if(x > 0 && y > 0){
31418                     scale = targetWidth / width;
31419                     
31420                     if(width < height){
31421                         scale = targetHeight / height;
31422                     }
31423                 }
31424                 
31425                 context.scale(scale, scale);
31426                 
31427                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31428                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31429
31430                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31431                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31432
31433                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31434                 
31435                 break;
31436             case 90 : 
31437                 
31438                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31439                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31440                 
31441                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31442                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31443                 
31444                 var targetWidth = this.minWidth - 2 * x;
31445                 var targetHeight = this.minHeight - 2 * y;
31446                 
31447                 var scale = 1;
31448                 
31449                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31450                     scale = targetWidth / width;
31451                 }
31452                 
31453                 if(x > 0 && y == 0){
31454                     scale = targetHeight / height;
31455                 }
31456                 
31457                 if(x > 0 && y > 0){
31458                     scale = targetWidth / width;
31459                     
31460                     if(width < height){
31461                         scale = targetHeight / height;
31462                     }
31463                 }
31464                 
31465                 context.scale(scale, scale);
31466                 
31467                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31468                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31469
31470                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31471                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31472                 
31473                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31474                 
31475                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31476                 
31477                 break;
31478             case 180 :
31479                 
31480                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31481                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31482                 
31483                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31484                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31485                 
31486                 var targetWidth = this.minWidth - 2 * x;
31487                 var targetHeight = this.minHeight - 2 * y;
31488                 
31489                 var scale = 1;
31490                 
31491                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31492                     scale = targetWidth / width;
31493                 }
31494                 
31495                 if(x > 0 && y == 0){
31496                     scale = targetHeight / height;
31497                 }
31498                 
31499                 if(x > 0 && y > 0){
31500                     scale = targetWidth / width;
31501                     
31502                     if(width < height){
31503                         scale = targetHeight / height;
31504                     }
31505                 }
31506                 
31507                 context.scale(scale, scale);
31508                 
31509                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31510                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31511
31512                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31513                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31514
31515                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31516                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31517                 
31518                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31519                 
31520                 break;
31521             case 270 :
31522                 
31523                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31524                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31525                 
31526                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31527                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31528                 
31529                 var targetWidth = this.minWidth - 2 * x;
31530                 var targetHeight = this.minHeight - 2 * y;
31531                 
31532                 var scale = 1;
31533                 
31534                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31535                     scale = targetWidth / width;
31536                 }
31537                 
31538                 if(x > 0 && y == 0){
31539                     scale = targetHeight / height;
31540                 }
31541                 
31542                 if(x > 0 && y > 0){
31543                     scale = targetWidth / width;
31544                     
31545                     if(width < height){
31546                         scale = targetHeight / height;
31547                     }
31548                 }
31549                 
31550                 context.scale(scale, scale);
31551                 
31552                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31553                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31554
31555                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31556                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31557                 
31558                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31559                 
31560                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31561                 
31562                 break;
31563             default : 
31564                 break;
31565         }
31566         
31567         this.cropData = canvas.toDataURL(this.cropType);
31568         
31569         if(this.fireEvent('crop', this, this.cropData) !== false){
31570             this.process(this.file, this.cropData);
31571         }
31572         
31573         return;
31574         
31575     },
31576     
31577     setThumbBoxSize : function()
31578     {
31579         var width, height;
31580         
31581         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31582             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31583             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31584             
31585             this.minWidth = width;
31586             this.minHeight = height;
31587             
31588             if(this.rotate == 90 || this.rotate == 270){
31589                 this.minWidth = height;
31590                 this.minHeight = width;
31591             }
31592         }
31593         
31594         height = 300;
31595         width = Math.ceil(this.minWidth * height / this.minHeight);
31596         
31597         if(this.minWidth > this.minHeight){
31598             width = 300;
31599             height = Math.ceil(this.minHeight * width / this.minWidth);
31600         }
31601         
31602         this.thumbEl.setStyle({
31603             width : width + 'px',
31604             height : height + 'px'
31605         });
31606
31607         return;
31608             
31609     },
31610     
31611     setThumbBoxPosition : function()
31612     {
31613         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31614         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31615         
31616         this.thumbEl.setLeft(x);
31617         this.thumbEl.setTop(y);
31618         
31619     },
31620     
31621     baseRotateLevel : function()
31622     {
31623         this.baseRotate = 1;
31624         
31625         if(
31626                 typeof(this.exif) != 'undefined' &&
31627                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31628                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31629         ){
31630             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31631         }
31632         
31633         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31634         
31635     },
31636     
31637     baseScaleLevel : function()
31638     {
31639         var width, height;
31640         
31641         if(this.isDocument){
31642             
31643             if(this.baseRotate == 6 || this.baseRotate == 8){
31644             
31645                 height = this.thumbEl.getHeight();
31646                 this.baseScale = height / this.imageEl.OriginWidth;
31647
31648                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31649                     width = this.thumbEl.getWidth();
31650                     this.baseScale = width / this.imageEl.OriginHeight;
31651                 }
31652
31653                 return;
31654             }
31655
31656             height = this.thumbEl.getHeight();
31657             this.baseScale = height / this.imageEl.OriginHeight;
31658
31659             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31660                 width = this.thumbEl.getWidth();
31661                 this.baseScale = width / this.imageEl.OriginWidth;
31662             }
31663
31664             return;
31665         }
31666         
31667         if(this.baseRotate == 6 || this.baseRotate == 8){
31668             
31669             width = this.thumbEl.getHeight();
31670             this.baseScale = width / this.imageEl.OriginHeight;
31671             
31672             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31673                 height = this.thumbEl.getWidth();
31674                 this.baseScale = height / this.imageEl.OriginHeight;
31675             }
31676             
31677             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31678                 height = this.thumbEl.getWidth();
31679                 this.baseScale = height / this.imageEl.OriginHeight;
31680                 
31681                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31682                     width = this.thumbEl.getHeight();
31683                     this.baseScale = width / this.imageEl.OriginWidth;
31684                 }
31685             }
31686             
31687             return;
31688         }
31689         
31690         width = this.thumbEl.getWidth();
31691         this.baseScale = width / this.imageEl.OriginWidth;
31692         
31693         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31694             height = this.thumbEl.getHeight();
31695             this.baseScale = height / this.imageEl.OriginHeight;
31696         }
31697         
31698         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31699             
31700             height = this.thumbEl.getHeight();
31701             this.baseScale = height / this.imageEl.OriginHeight;
31702             
31703             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31704                 width = this.thumbEl.getWidth();
31705                 this.baseScale = width / this.imageEl.OriginWidth;
31706             }
31707             
31708         }
31709         
31710         return;
31711     },
31712     
31713     getScaleLevel : function()
31714     {
31715         return this.baseScale * Math.pow(1.1, this.scale);
31716     },
31717     
31718     onTouchStart : function(e)
31719     {
31720         if(!this.canvasLoaded){
31721             this.beforeSelectFile(e);
31722             return;
31723         }
31724         
31725         var touches = e.browserEvent.touches;
31726         
31727         if(!touches){
31728             return;
31729         }
31730         
31731         if(touches.length == 1){
31732             this.onMouseDown(e);
31733             return;
31734         }
31735         
31736         if(touches.length != 2){
31737             return;
31738         }
31739         
31740         var coords = [];
31741         
31742         for(var i = 0, finger; finger = touches[i]; i++){
31743             coords.push(finger.pageX, finger.pageY);
31744         }
31745         
31746         var x = Math.pow(coords[0] - coords[2], 2);
31747         var y = Math.pow(coords[1] - coords[3], 2);
31748         
31749         this.startDistance = Math.sqrt(x + y);
31750         
31751         this.startScale = this.scale;
31752         
31753         this.pinching = true;
31754         this.dragable = false;
31755         
31756     },
31757     
31758     onTouchMove : function(e)
31759     {
31760         if(!this.pinching && !this.dragable){
31761             return;
31762         }
31763         
31764         var touches = e.browserEvent.touches;
31765         
31766         if(!touches){
31767             return;
31768         }
31769         
31770         if(this.dragable){
31771             this.onMouseMove(e);
31772             return;
31773         }
31774         
31775         var coords = [];
31776         
31777         for(var i = 0, finger; finger = touches[i]; i++){
31778             coords.push(finger.pageX, finger.pageY);
31779         }
31780         
31781         var x = Math.pow(coords[0] - coords[2], 2);
31782         var y = Math.pow(coords[1] - coords[3], 2);
31783         
31784         this.endDistance = Math.sqrt(x + y);
31785         
31786         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31787         
31788         if(!this.zoomable()){
31789             this.scale = this.startScale;
31790             return;
31791         }
31792         
31793         this.draw();
31794         
31795     },
31796     
31797     onTouchEnd : function(e)
31798     {
31799         this.pinching = false;
31800         this.dragable = false;
31801         
31802     },
31803     
31804     process : function(file, crop)
31805     {
31806         if(this.loadMask){
31807             this.maskEl.mask(this.loadingText);
31808         }
31809         
31810         this.xhr = new XMLHttpRequest();
31811         
31812         file.xhr = this.xhr;
31813
31814         this.xhr.open(this.method, this.url, true);
31815         
31816         var headers = {
31817             "Accept": "application/json",
31818             "Cache-Control": "no-cache",
31819             "X-Requested-With": "XMLHttpRequest"
31820         };
31821         
31822         for (var headerName in headers) {
31823             var headerValue = headers[headerName];
31824             if (headerValue) {
31825                 this.xhr.setRequestHeader(headerName, headerValue);
31826             }
31827         }
31828         
31829         var _this = this;
31830         
31831         this.xhr.onload = function()
31832         {
31833             _this.xhrOnLoad(_this.xhr);
31834         }
31835         
31836         this.xhr.onerror = function()
31837         {
31838             _this.xhrOnError(_this.xhr);
31839         }
31840         
31841         var formData = new FormData();
31842
31843         formData.append('returnHTML', 'NO');
31844         
31845         if(crop){
31846             formData.append('crop', crop);
31847         }
31848         
31849         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31850             formData.append(this.paramName, file, file.name);
31851         }
31852         
31853         if(typeof(file.filename) != 'undefined'){
31854             formData.append('filename', file.filename);
31855         }
31856         
31857         if(typeof(file.mimetype) != 'undefined'){
31858             formData.append('mimetype', file.mimetype);
31859         }
31860         
31861         if(this.fireEvent('arrange', this, formData) != false){
31862             this.xhr.send(formData);
31863         };
31864     },
31865     
31866     xhrOnLoad : function(xhr)
31867     {
31868         if(this.loadMask){
31869             this.maskEl.unmask();
31870         }
31871         
31872         if (xhr.readyState !== 4) {
31873             this.fireEvent('exception', this, xhr);
31874             return;
31875         }
31876
31877         var response = Roo.decode(xhr.responseText);
31878         
31879         if(!response.success){
31880             this.fireEvent('exception', this, xhr);
31881             return;
31882         }
31883         
31884         var response = Roo.decode(xhr.responseText);
31885         
31886         this.fireEvent('upload', this, response);
31887         
31888     },
31889     
31890     xhrOnError : function()
31891     {
31892         if(this.loadMask){
31893             this.maskEl.unmask();
31894         }
31895         
31896         Roo.log('xhr on error');
31897         
31898         var response = Roo.decode(xhr.responseText);
31899           
31900         Roo.log(response);
31901         
31902     },
31903     
31904     prepare : function(file)
31905     {   
31906         if(this.loadMask){
31907             this.maskEl.mask(this.loadingText);
31908         }
31909         
31910         this.file = false;
31911         this.exif = {};
31912         
31913         if(typeof(file) === 'string'){
31914             this.loadCanvas(file);
31915             return;
31916         }
31917         
31918         if(!file || !this.urlAPI){
31919             return;
31920         }
31921         
31922         this.file = file;
31923         this.cropType = file.type;
31924         
31925         var _this = this;
31926         
31927         if(this.fireEvent('prepare', this, this.file) != false){
31928             
31929             var reader = new FileReader();
31930             
31931             reader.onload = function (e) {
31932                 if (e.target.error) {
31933                     Roo.log(e.target.error);
31934                     return;
31935                 }
31936                 
31937                 var buffer = e.target.result,
31938                     dataView = new DataView(buffer),
31939                     offset = 2,
31940                     maxOffset = dataView.byteLength - 4,
31941                     markerBytes,
31942                     markerLength;
31943                 
31944                 if (dataView.getUint16(0) === 0xffd8) {
31945                     while (offset < maxOffset) {
31946                         markerBytes = dataView.getUint16(offset);
31947                         
31948                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31949                             markerLength = dataView.getUint16(offset + 2) + 2;
31950                             if (offset + markerLength > dataView.byteLength) {
31951                                 Roo.log('Invalid meta data: Invalid segment size.');
31952                                 break;
31953                             }
31954                             
31955                             if(markerBytes == 0xffe1){
31956                                 _this.parseExifData(
31957                                     dataView,
31958                                     offset,
31959                                     markerLength
31960                                 );
31961                             }
31962                             
31963                             offset += markerLength;
31964                             
31965                             continue;
31966                         }
31967                         
31968                         break;
31969                     }
31970                     
31971                 }
31972                 
31973                 var url = _this.urlAPI.createObjectURL(_this.file);
31974                 
31975                 _this.loadCanvas(url);
31976                 
31977                 return;
31978             }
31979             
31980             reader.readAsArrayBuffer(this.file);
31981             
31982         }
31983         
31984     },
31985     
31986     parseExifData : function(dataView, offset, length)
31987     {
31988         var tiffOffset = offset + 10,
31989             littleEndian,
31990             dirOffset;
31991     
31992         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31993             // No Exif data, might be XMP data instead
31994             return;
31995         }
31996         
31997         // Check for the ASCII code for "Exif" (0x45786966):
31998         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31999             // No Exif data, might be XMP data instead
32000             return;
32001         }
32002         if (tiffOffset + 8 > dataView.byteLength) {
32003             Roo.log('Invalid Exif data: Invalid segment size.');
32004             return;
32005         }
32006         // Check for the two null bytes:
32007         if (dataView.getUint16(offset + 8) !== 0x0000) {
32008             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32009             return;
32010         }
32011         // Check the byte alignment:
32012         switch (dataView.getUint16(tiffOffset)) {
32013         case 0x4949:
32014             littleEndian = true;
32015             break;
32016         case 0x4D4D:
32017             littleEndian = false;
32018             break;
32019         default:
32020             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32021             return;
32022         }
32023         // Check for the TIFF tag marker (0x002A):
32024         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32025             Roo.log('Invalid Exif data: Missing TIFF marker.');
32026             return;
32027         }
32028         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32029         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32030         
32031         this.parseExifTags(
32032             dataView,
32033             tiffOffset,
32034             tiffOffset + dirOffset,
32035             littleEndian
32036         );
32037     },
32038     
32039     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32040     {
32041         var tagsNumber,
32042             dirEndOffset,
32043             i;
32044         if (dirOffset + 6 > dataView.byteLength) {
32045             Roo.log('Invalid Exif data: Invalid directory offset.');
32046             return;
32047         }
32048         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32049         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32050         if (dirEndOffset + 4 > dataView.byteLength) {
32051             Roo.log('Invalid Exif data: Invalid directory size.');
32052             return;
32053         }
32054         for (i = 0; i < tagsNumber; i += 1) {
32055             this.parseExifTag(
32056                 dataView,
32057                 tiffOffset,
32058                 dirOffset + 2 + 12 * i, // tag offset
32059                 littleEndian
32060             );
32061         }
32062         // Return the offset to the next directory:
32063         return dataView.getUint32(dirEndOffset, littleEndian);
32064     },
32065     
32066     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32067     {
32068         var tag = dataView.getUint16(offset, littleEndian);
32069         
32070         this.exif[tag] = this.getExifValue(
32071             dataView,
32072             tiffOffset,
32073             offset,
32074             dataView.getUint16(offset + 2, littleEndian), // tag type
32075             dataView.getUint32(offset + 4, littleEndian), // tag length
32076             littleEndian
32077         );
32078     },
32079     
32080     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32081     {
32082         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32083             tagSize,
32084             dataOffset,
32085             values,
32086             i,
32087             str,
32088             c;
32089     
32090         if (!tagType) {
32091             Roo.log('Invalid Exif data: Invalid tag type.');
32092             return;
32093         }
32094         
32095         tagSize = tagType.size * length;
32096         // Determine if the value is contained in the dataOffset bytes,
32097         // or if the value at the dataOffset is a pointer to the actual data:
32098         dataOffset = tagSize > 4 ?
32099                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32100         if (dataOffset + tagSize > dataView.byteLength) {
32101             Roo.log('Invalid Exif data: Invalid data offset.');
32102             return;
32103         }
32104         if (length === 1) {
32105             return tagType.getValue(dataView, dataOffset, littleEndian);
32106         }
32107         values = [];
32108         for (i = 0; i < length; i += 1) {
32109             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32110         }
32111         
32112         if (tagType.ascii) {
32113             str = '';
32114             // Concatenate the chars:
32115             for (i = 0; i < values.length; i += 1) {
32116                 c = values[i];
32117                 // Ignore the terminating NULL byte(s):
32118                 if (c === '\u0000') {
32119                     break;
32120                 }
32121                 str += c;
32122             }
32123             return str;
32124         }
32125         return values;
32126     }
32127     
32128 });
32129
32130 Roo.apply(Roo.bootstrap.UploadCropbox, {
32131     tags : {
32132         'Orientation': 0x0112
32133     },
32134     
32135     Orientation: {
32136             1: 0, //'top-left',
32137 //            2: 'top-right',
32138             3: 180, //'bottom-right',
32139 //            4: 'bottom-left',
32140 //            5: 'left-top',
32141             6: 90, //'right-top',
32142 //            7: 'right-bottom',
32143             8: 270 //'left-bottom'
32144     },
32145     
32146     exifTagTypes : {
32147         // byte, 8-bit unsigned int:
32148         1: {
32149             getValue: function (dataView, dataOffset) {
32150                 return dataView.getUint8(dataOffset);
32151             },
32152             size: 1
32153         },
32154         // ascii, 8-bit byte:
32155         2: {
32156             getValue: function (dataView, dataOffset) {
32157                 return String.fromCharCode(dataView.getUint8(dataOffset));
32158             },
32159             size: 1,
32160             ascii: true
32161         },
32162         // short, 16 bit int:
32163         3: {
32164             getValue: function (dataView, dataOffset, littleEndian) {
32165                 return dataView.getUint16(dataOffset, littleEndian);
32166             },
32167             size: 2
32168         },
32169         // long, 32 bit int:
32170         4: {
32171             getValue: function (dataView, dataOffset, littleEndian) {
32172                 return dataView.getUint32(dataOffset, littleEndian);
32173             },
32174             size: 4
32175         },
32176         // rational = two long values, first is numerator, second is denominator:
32177         5: {
32178             getValue: function (dataView, dataOffset, littleEndian) {
32179                 return dataView.getUint32(dataOffset, littleEndian) /
32180                     dataView.getUint32(dataOffset + 4, littleEndian);
32181             },
32182             size: 8
32183         },
32184         // slong, 32 bit signed int:
32185         9: {
32186             getValue: function (dataView, dataOffset, littleEndian) {
32187                 return dataView.getInt32(dataOffset, littleEndian);
32188             },
32189             size: 4
32190         },
32191         // srational, two slongs, first is numerator, second is denominator:
32192         10: {
32193             getValue: function (dataView, dataOffset, littleEndian) {
32194                 return dataView.getInt32(dataOffset, littleEndian) /
32195                     dataView.getInt32(dataOffset + 4, littleEndian);
32196             },
32197             size: 8
32198         }
32199     },
32200     
32201     footer : {
32202         STANDARD : [
32203             {
32204                 tag : 'div',
32205                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32206                 action : 'rotate-left',
32207                 cn : [
32208                     {
32209                         tag : 'button',
32210                         cls : 'btn btn-default',
32211                         html : '<i class="fa fa-undo"></i>'
32212                     }
32213                 ]
32214             },
32215             {
32216                 tag : 'div',
32217                 cls : 'btn-group roo-upload-cropbox-picture',
32218                 action : 'picture',
32219                 cn : [
32220                     {
32221                         tag : 'button',
32222                         cls : 'btn btn-default',
32223                         html : '<i class="fa fa-picture-o"></i>'
32224                     }
32225                 ]
32226             },
32227             {
32228                 tag : 'div',
32229                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32230                 action : 'rotate-right',
32231                 cn : [
32232                     {
32233                         tag : 'button',
32234                         cls : 'btn btn-default',
32235                         html : '<i class="fa fa-repeat"></i>'
32236                     }
32237                 ]
32238             }
32239         ],
32240         DOCUMENT : [
32241             {
32242                 tag : 'div',
32243                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32244                 action : 'rotate-left',
32245                 cn : [
32246                     {
32247                         tag : 'button',
32248                         cls : 'btn btn-default',
32249                         html : '<i class="fa fa-undo"></i>'
32250                     }
32251                 ]
32252             },
32253             {
32254                 tag : 'div',
32255                 cls : 'btn-group roo-upload-cropbox-download',
32256                 action : 'download',
32257                 cn : [
32258                     {
32259                         tag : 'button',
32260                         cls : 'btn btn-default',
32261                         html : '<i class="fa fa-download"></i>'
32262                     }
32263                 ]
32264             },
32265             {
32266                 tag : 'div',
32267                 cls : 'btn-group roo-upload-cropbox-crop',
32268                 action : 'crop',
32269                 cn : [
32270                     {
32271                         tag : 'button',
32272                         cls : 'btn btn-default',
32273                         html : '<i class="fa fa-crop"></i>'
32274                     }
32275                 ]
32276             },
32277             {
32278                 tag : 'div',
32279                 cls : 'btn-group roo-upload-cropbox-trash',
32280                 action : 'trash',
32281                 cn : [
32282                     {
32283                         tag : 'button',
32284                         cls : 'btn btn-default',
32285                         html : '<i class="fa fa-trash"></i>'
32286                     }
32287                 ]
32288             },
32289             {
32290                 tag : 'div',
32291                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32292                 action : 'rotate-right',
32293                 cn : [
32294                     {
32295                         tag : 'button',
32296                         cls : 'btn btn-default',
32297                         html : '<i class="fa fa-repeat"></i>'
32298                     }
32299                 ]
32300             }
32301         ],
32302         ROTATOR : [
32303             {
32304                 tag : 'div',
32305                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32306                 action : 'rotate-left',
32307                 cn : [
32308                     {
32309                         tag : 'button',
32310                         cls : 'btn btn-default',
32311                         html : '<i class="fa fa-undo"></i>'
32312                     }
32313                 ]
32314             },
32315             {
32316                 tag : 'div',
32317                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32318                 action : 'rotate-right',
32319                 cn : [
32320                     {
32321                         tag : 'button',
32322                         cls : 'btn btn-default',
32323                         html : '<i class="fa fa-repeat"></i>'
32324                     }
32325                 ]
32326             }
32327         ]
32328     }
32329 });
32330
32331 /*
32332 * Licence: LGPL
32333 */
32334
32335 /**
32336  * @class Roo.bootstrap.DocumentManager
32337  * @extends Roo.bootstrap.Component
32338  * Bootstrap DocumentManager class
32339  * @cfg {String} paramName default 'imageUpload'
32340  * @cfg {String} toolTipName default 'filename'
32341  * @cfg {String} method default POST
32342  * @cfg {String} url action url
32343  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32344  * @cfg {Boolean} multiple multiple upload default true
32345  * @cfg {Number} thumbSize default 300
32346  * @cfg {String} fieldLabel
32347  * @cfg {Number} labelWidth default 4
32348  * @cfg {String} labelAlign (left|top) default left
32349  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32350 * @cfg {Number} labellg set the width of label (1-12)
32351  * @cfg {Number} labelmd set the width of label (1-12)
32352  * @cfg {Number} labelsm set the width of label (1-12)
32353  * @cfg {Number} labelxs set the width of label (1-12)
32354  * 
32355  * @constructor
32356  * Create a new DocumentManager
32357  * @param {Object} config The config object
32358  */
32359
32360 Roo.bootstrap.DocumentManager = function(config){
32361     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32362     
32363     this.files = [];
32364     this.delegates = [];
32365     
32366     this.addEvents({
32367         /**
32368          * @event initial
32369          * Fire when initial the DocumentManager
32370          * @param {Roo.bootstrap.DocumentManager} this
32371          */
32372         "initial" : true,
32373         /**
32374          * @event inspect
32375          * inspect selected file
32376          * @param {Roo.bootstrap.DocumentManager} this
32377          * @param {File} file
32378          */
32379         "inspect" : true,
32380         /**
32381          * @event exception
32382          * Fire when xhr load exception
32383          * @param {Roo.bootstrap.DocumentManager} this
32384          * @param {XMLHttpRequest} xhr
32385          */
32386         "exception" : true,
32387         /**
32388          * @event afterupload
32389          * Fire when xhr load exception
32390          * @param {Roo.bootstrap.DocumentManager} this
32391          * @param {XMLHttpRequest} xhr
32392          */
32393         "afterupload" : true,
32394         /**
32395          * @event prepare
32396          * prepare the form data
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          * @param {Object} formData
32399          */
32400         "prepare" : true,
32401         /**
32402          * @event remove
32403          * Fire when remove the file
32404          * @param {Roo.bootstrap.DocumentManager} this
32405          * @param {Object} file
32406          */
32407         "remove" : true,
32408         /**
32409          * @event refresh
32410          * Fire after refresh the file
32411          * @param {Roo.bootstrap.DocumentManager} this
32412          */
32413         "refresh" : true,
32414         /**
32415          * @event click
32416          * Fire after click the image
32417          * @param {Roo.bootstrap.DocumentManager} this
32418          * @param {Object} file
32419          */
32420         "click" : true,
32421         /**
32422          * @event edit
32423          * Fire when upload a image and editable set to true
32424          * @param {Roo.bootstrap.DocumentManager} this
32425          * @param {Object} file
32426          */
32427         "edit" : true,
32428         /**
32429          * @event beforeselectfile
32430          * Fire before select file
32431          * @param {Roo.bootstrap.DocumentManager} this
32432          */
32433         "beforeselectfile" : true,
32434         /**
32435          * @event process
32436          * Fire before process file
32437          * @param {Roo.bootstrap.DocumentManager} this
32438          * @param {Object} file
32439          */
32440         "process" : true,
32441         /**
32442          * @event previewrendered
32443          * Fire when preview rendered
32444          * @param {Roo.bootstrap.DocumentManager} this
32445          * @param {Object} file
32446          */
32447         "previewrendered" : true,
32448         /**
32449          */
32450         "previewResize" : true
32451         
32452     });
32453 };
32454
32455 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32456     
32457     boxes : 0,
32458     inputName : '',
32459     thumbSize : 300,
32460     multiple : true,
32461     files : false,
32462     method : 'POST',
32463     url : '',
32464     paramName : 'imageUpload',
32465     toolTipName : 'filename',
32466     fieldLabel : '',
32467     labelWidth : 4,
32468     labelAlign : 'left',
32469     editable : true,
32470     delegates : false,
32471     xhr : false, 
32472     
32473     labellg : 0,
32474     labelmd : 0,
32475     labelsm : 0,
32476     labelxs : 0,
32477     
32478     getAutoCreate : function()
32479     {   
32480         var managerWidget = {
32481             tag : 'div',
32482             cls : 'roo-document-manager',
32483             cn : [
32484                 {
32485                     tag : 'input',
32486                     cls : 'roo-document-manager-selector',
32487                     type : 'file'
32488                 },
32489                 {
32490                     tag : 'div',
32491                     cls : 'roo-document-manager-uploader',
32492                     cn : [
32493                         {
32494                             tag : 'div',
32495                             cls : 'roo-document-manager-upload-btn',
32496                             html : '<i class="fa fa-plus"></i>'
32497                         }
32498                     ]
32499                     
32500                 }
32501             ]
32502         };
32503         
32504         var content = [
32505             {
32506                 tag : 'div',
32507                 cls : 'column col-md-12',
32508                 cn : managerWidget
32509             }
32510         ];
32511         
32512         if(this.fieldLabel.length){
32513             
32514             content = [
32515                 {
32516                     tag : 'div',
32517                     cls : 'column col-md-12',
32518                     html : this.fieldLabel
32519                 },
32520                 {
32521                     tag : 'div',
32522                     cls : 'column col-md-12',
32523                     cn : managerWidget
32524                 }
32525             ];
32526
32527             if(this.labelAlign == 'left'){
32528                 content = [
32529                     {
32530                         tag : 'div',
32531                         cls : 'column',
32532                         html : this.fieldLabel
32533                     },
32534                     {
32535                         tag : 'div',
32536                         cls : 'column',
32537                         cn : managerWidget
32538                     }
32539                 ];
32540                 
32541                 if(this.labelWidth > 12){
32542                     content[0].style = "width: " + this.labelWidth + 'px';
32543                 }
32544
32545                 if(this.labelWidth < 13 && this.labelmd == 0){
32546                     this.labelmd = this.labelWidth;
32547                 }
32548
32549                 if(this.labellg > 0){
32550                     content[0].cls += ' col-lg-' + this.labellg;
32551                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32552                 }
32553
32554                 if(this.labelmd > 0){
32555                     content[0].cls += ' col-md-' + this.labelmd;
32556                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32557                 }
32558
32559                 if(this.labelsm > 0){
32560                     content[0].cls += ' col-sm-' + this.labelsm;
32561                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32562                 }
32563
32564                 if(this.labelxs > 0){
32565                     content[0].cls += ' col-xs-' + this.labelxs;
32566                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32567                 }
32568                 
32569             }
32570         }
32571         
32572         var cfg = {
32573             tag : 'div',
32574             cls : 'row clearfix',
32575             cn : content
32576         };
32577         
32578         return cfg;
32579         
32580     },
32581     
32582     initEvents : function()
32583     {
32584         this.managerEl = this.el.select('.roo-document-manager', true).first();
32585         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32586         
32587         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32588         this.selectorEl.hide();
32589         
32590         if(this.multiple){
32591             this.selectorEl.attr('multiple', 'multiple');
32592         }
32593         
32594         this.selectorEl.on('change', this.onFileSelected, this);
32595         
32596         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32597         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32598         
32599         this.uploader.on('click', this.onUploaderClick, this);
32600         
32601         this.renderProgressDialog();
32602         
32603         var _this = this;
32604         
32605         window.addEventListener("resize", function() { _this.refresh(); } );
32606         
32607         this.fireEvent('initial', this);
32608     },
32609     
32610     renderProgressDialog : function()
32611     {
32612         var _this = this;
32613         
32614         this.progressDialog = new Roo.bootstrap.Modal({
32615             cls : 'roo-document-manager-progress-dialog',
32616             allow_close : false,
32617             animate : false,
32618             title : '',
32619             buttons : [
32620                 {
32621                     name  :'cancel',
32622                     weight : 'danger',
32623                     html : 'Cancel'
32624                 }
32625             ], 
32626             listeners : { 
32627                 btnclick : function() {
32628                     _this.uploadCancel();
32629                     this.hide();
32630                 }
32631             }
32632         });
32633          
32634         this.progressDialog.render(Roo.get(document.body));
32635          
32636         this.progress = new Roo.bootstrap.Progress({
32637             cls : 'roo-document-manager-progress',
32638             active : true,
32639             striped : true
32640         });
32641         
32642         this.progress.render(this.progressDialog.getChildContainer());
32643         
32644         this.progressBar = new Roo.bootstrap.ProgressBar({
32645             cls : 'roo-document-manager-progress-bar',
32646             aria_valuenow : 0,
32647             aria_valuemin : 0,
32648             aria_valuemax : 12,
32649             panel : 'success'
32650         });
32651         
32652         this.progressBar.render(this.progress.getChildContainer());
32653     },
32654     
32655     onUploaderClick : function(e)
32656     {
32657         e.preventDefault();
32658      
32659         if(this.fireEvent('beforeselectfile', this) != false){
32660             this.selectorEl.dom.click();
32661         }
32662         
32663     },
32664     
32665     onFileSelected : function(e)
32666     {
32667         e.preventDefault();
32668         
32669         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32670             return;
32671         }
32672         
32673         Roo.each(this.selectorEl.dom.files, function(file){
32674             if(this.fireEvent('inspect', this, file) != false){
32675                 this.files.push(file);
32676             }
32677         }, this);
32678         
32679         this.queue();
32680         
32681     },
32682     
32683     queue : function()
32684     {
32685         this.selectorEl.dom.value = '';
32686         
32687         if(!this.files || !this.files.length){
32688             return;
32689         }
32690         
32691         if(this.boxes > 0 && this.files.length > this.boxes){
32692             this.files = this.files.slice(0, this.boxes);
32693         }
32694         
32695         this.uploader.show();
32696         
32697         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32698             this.uploader.hide();
32699         }
32700         
32701         var _this = this;
32702         
32703         var files = [];
32704         
32705         var docs = [];
32706         
32707         Roo.each(this.files, function(file){
32708             
32709             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32710                 var f = this.renderPreview(file);
32711                 files.push(f);
32712                 return;
32713             }
32714             
32715             if(file.type.indexOf('image') != -1){
32716                 this.delegates.push(
32717                     (function(){
32718                         _this.process(file);
32719                     }).createDelegate(this)
32720                 );
32721         
32722                 return;
32723             }
32724             
32725             docs.push(
32726                 (function(){
32727                     _this.process(file);
32728                 }).createDelegate(this)
32729             );
32730             
32731         }, this);
32732         
32733         this.files = files;
32734         
32735         this.delegates = this.delegates.concat(docs);
32736         
32737         if(!this.delegates.length){
32738             this.refresh();
32739             return;
32740         }
32741         
32742         this.progressBar.aria_valuemax = this.delegates.length;
32743         
32744         this.arrange();
32745         
32746         return;
32747     },
32748     
32749     arrange : function()
32750     {
32751         if(!this.delegates.length){
32752             this.progressDialog.hide();
32753             this.refresh();
32754             return;
32755         }
32756         
32757         var delegate = this.delegates.shift();
32758         
32759         this.progressDialog.show();
32760         
32761         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32762         
32763         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32764         
32765         delegate();
32766     },
32767     
32768     refresh : function()
32769     {
32770         this.uploader.show();
32771         
32772         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32773             this.uploader.hide();
32774         }
32775         
32776         Roo.isTouch ? this.closable(false) : this.closable(true);
32777         
32778         this.fireEvent('refresh', this);
32779     },
32780     
32781     onRemove : function(e, el, o)
32782     {
32783         e.preventDefault();
32784         
32785         this.fireEvent('remove', this, o);
32786         
32787     },
32788     
32789     remove : function(o)
32790     {
32791         var files = [];
32792         
32793         Roo.each(this.files, function(file){
32794             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32795                 files.push(file);
32796                 return;
32797             }
32798
32799             o.target.remove();
32800
32801         }, this);
32802         
32803         this.files = files;
32804         
32805         this.refresh();
32806     },
32807     
32808     clear : function()
32809     {
32810         Roo.each(this.files, function(file){
32811             if(!file.target){
32812                 return;
32813             }
32814             
32815             file.target.remove();
32816
32817         }, this);
32818         
32819         this.files = [];
32820         
32821         this.refresh();
32822     },
32823     
32824     onClick : function(e, el, o)
32825     {
32826         e.preventDefault();
32827         
32828         this.fireEvent('click', this, o);
32829         
32830     },
32831     
32832     closable : function(closable)
32833     {
32834         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32835             
32836             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32837             
32838             if(closable){
32839                 el.show();
32840                 return;
32841             }
32842             
32843             el.hide();
32844             
32845         }, this);
32846     },
32847     
32848     xhrOnLoad : function(xhr)
32849     {
32850         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32851             el.remove();
32852         }, this);
32853         
32854         if (xhr.readyState !== 4) {
32855             this.arrange();
32856             this.fireEvent('exception', this, xhr);
32857             return;
32858         }
32859
32860         var response = Roo.decode(xhr.responseText);
32861         
32862         if(!response.success){
32863             this.arrange();
32864             this.fireEvent('exception', this, xhr);
32865             return;
32866         }
32867         
32868         var file = this.renderPreview(response.data);
32869         
32870         this.files.push(file);
32871         
32872         this.arrange();
32873         
32874         this.fireEvent('afterupload', this, xhr);
32875         
32876     },
32877     
32878     xhrOnError : function(xhr)
32879     {
32880         Roo.log('xhr on error');
32881         
32882         var response = Roo.decode(xhr.responseText);
32883           
32884         Roo.log(response);
32885         
32886         this.arrange();
32887     },
32888     
32889     process : function(file)
32890     {
32891         if(this.fireEvent('process', this, file) !== false){
32892             if(this.editable && file.type.indexOf('image') != -1){
32893                 this.fireEvent('edit', this, file);
32894                 return;
32895             }
32896
32897             this.uploadStart(file, false);
32898
32899             return;
32900         }
32901         
32902     },
32903     
32904     uploadStart : function(file, crop)
32905     {
32906         this.xhr = new XMLHttpRequest();
32907         
32908         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32909             this.arrange();
32910             return;
32911         }
32912         
32913         file.xhr = this.xhr;
32914             
32915         this.managerEl.createChild({
32916             tag : 'div',
32917             cls : 'roo-document-manager-loading',
32918             cn : [
32919                 {
32920                     tag : 'div',
32921                     tooltip : file.name,
32922                     cls : 'roo-document-manager-thumb',
32923                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32924                 }
32925             ]
32926
32927         });
32928
32929         this.xhr.open(this.method, this.url, true);
32930         
32931         var headers = {
32932             "Accept": "application/json",
32933             "Cache-Control": "no-cache",
32934             "X-Requested-With": "XMLHttpRequest"
32935         };
32936         
32937         for (var headerName in headers) {
32938             var headerValue = headers[headerName];
32939             if (headerValue) {
32940                 this.xhr.setRequestHeader(headerName, headerValue);
32941             }
32942         }
32943         
32944         var _this = this;
32945         
32946         this.xhr.onload = function()
32947         {
32948             _this.xhrOnLoad(_this.xhr);
32949         }
32950         
32951         this.xhr.onerror = function()
32952         {
32953             _this.xhrOnError(_this.xhr);
32954         }
32955         
32956         var formData = new FormData();
32957
32958         formData.append('returnHTML', 'NO');
32959         
32960         if(crop){
32961             formData.append('crop', crop);
32962         }
32963         
32964         formData.append(this.paramName, file, file.name);
32965         
32966         var options = {
32967             file : file, 
32968             manually : false
32969         };
32970         
32971         if(this.fireEvent('prepare', this, formData, options) != false){
32972             
32973             if(options.manually){
32974                 return;
32975             }
32976             
32977             this.xhr.send(formData);
32978             return;
32979         };
32980         
32981         this.uploadCancel();
32982     },
32983     
32984     uploadCancel : function()
32985     {
32986         if (this.xhr) {
32987             this.xhr.abort();
32988         }
32989         
32990         this.delegates = [];
32991         
32992         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32993             el.remove();
32994         }, this);
32995         
32996         this.arrange();
32997     },
32998     
32999     renderPreview : function(file)
33000     {
33001         if(typeof(file.target) != 'undefined' && file.target){
33002             return file;
33003         }
33004         
33005         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33006         
33007         var previewEl = this.managerEl.createChild({
33008             tag : 'div',
33009             cls : 'roo-document-manager-preview',
33010             cn : [
33011                 {
33012                     tag : 'div',
33013                     tooltip : file[this.toolTipName],
33014                     cls : 'roo-document-manager-thumb',
33015                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33016                 },
33017                 {
33018                     tag : 'button',
33019                     cls : 'close',
33020                     html : '<i class="fa fa-times-circle"></i>'
33021                 }
33022             ]
33023         });
33024
33025         var close = previewEl.select('button.close', true).first();
33026
33027         close.on('click', this.onRemove, this, file);
33028
33029         file.target = previewEl;
33030
33031         var image = previewEl.select('img', true).first();
33032         
33033         var _this = this;
33034         
33035         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33036         
33037         image.on('click', this.onClick, this, file);
33038         
33039         this.fireEvent('previewrendered', this, file);
33040         
33041         return file;
33042         
33043     },
33044     
33045     onPreviewLoad : function(file, image)
33046     {
33047         if(typeof(file.target) == 'undefined' || !file.target){
33048             return;
33049         }
33050         
33051         var width = image.dom.naturalWidth || image.dom.width;
33052         var height = image.dom.naturalHeight || image.dom.height;
33053         
33054         if(!this.previewResize) {
33055             return;
33056         }
33057         
33058         if(width > height){
33059             file.target.addClass('wide');
33060             return;
33061         }
33062         
33063         file.target.addClass('tall');
33064         return;
33065         
33066     },
33067     
33068     uploadFromSource : function(file, crop)
33069     {
33070         this.xhr = new XMLHttpRequest();
33071         
33072         this.managerEl.createChild({
33073             tag : 'div',
33074             cls : 'roo-document-manager-loading',
33075             cn : [
33076                 {
33077                     tag : 'div',
33078                     tooltip : file.name,
33079                     cls : 'roo-document-manager-thumb',
33080                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33081                 }
33082             ]
33083
33084         });
33085
33086         this.xhr.open(this.method, this.url, true);
33087         
33088         var headers = {
33089             "Accept": "application/json",
33090             "Cache-Control": "no-cache",
33091             "X-Requested-With": "XMLHttpRequest"
33092         };
33093         
33094         for (var headerName in headers) {
33095             var headerValue = headers[headerName];
33096             if (headerValue) {
33097                 this.xhr.setRequestHeader(headerName, headerValue);
33098             }
33099         }
33100         
33101         var _this = this;
33102         
33103         this.xhr.onload = function()
33104         {
33105             _this.xhrOnLoad(_this.xhr);
33106         }
33107         
33108         this.xhr.onerror = function()
33109         {
33110             _this.xhrOnError(_this.xhr);
33111         }
33112         
33113         var formData = new FormData();
33114
33115         formData.append('returnHTML', 'NO');
33116         
33117         formData.append('crop', crop);
33118         
33119         if(typeof(file.filename) != 'undefined'){
33120             formData.append('filename', file.filename);
33121         }
33122         
33123         if(typeof(file.mimetype) != 'undefined'){
33124             formData.append('mimetype', file.mimetype);
33125         }
33126         
33127         Roo.log(formData);
33128         
33129         if(this.fireEvent('prepare', this, formData) != false){
33130             this.xhr.send(formData);
33131         };
33132     }
33133 });
33134
33135 /*
33136 * Licence: LGPL
33137 */
33138
33139 /**
33140  * @class Roo.bootstrap.DocumentViewer
33141  * @extends Roo.bootstrap.Component
33142  * Bootstrap DocumentViewer class
33143  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33144  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33145  * 
33146  * @constructor
33147  * Create a new DocumentViewer
33148  * @param {Object} config The config object
33149  */
33150
33151 Roo.bootstrap.DocumentViewer = function(config){
33152     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33153     
33154     this.addEvents({
33155         /**
33156          * @event initial
33157          * Fire after initEvent
33158          * @param {Roo.bootstrap.DocumentViewer} this
33159          */
33160         "initial" : true,
33161         /**
33162          * @event click
33163          * Fire after click
33164          * @param {Roo.bootstrap.DocumentViewer} this
33165          */
33166         "click" : true,
33167         /**
33168          * @event download
33169          * Fire after download button
33170          * @param {Roo.bootstrap.DocumentViewer} this
33171          */
33172         "download" : true,
33173         /**
33174          * @event trash
33175          * Fire after trash button
33176          * @param {Roo.bootstrap.DocumentViewer} this
33177          */
33178         "trash" : true
33179         
33180     });
33181 };
33182
33183 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33184     
33185     showDownload : true,
33186     
33187     showTrash : true,
33188     
33189     getAutoCreate : function()
33190     {
33191         var cfg = {
33192             tag : 'div',
33193             cls : 'roo-document-viewer',
33194             cn : [
33195                 {
33196                     tag : 'div',
33197                     cls : 'roo-document-viewer-body',
33198                     cn : [
33199                         {
33200                             tag : 'div',
33201                             cls : 'roo-document-viewer-thumb',
33202                             cn : [
33203                                 {
33204                                     tag : 'img',
33205                                     cls : 'roo-document-viewer-image'
33206                                 }
33207                             ]
33208                         }
33209                     ]
33210                 },
33211                 {
33212                     tag : 'div',
33213                     cls : 'roo-document-viewer-footer',
33214                     cn : {
33215                         tag : 'div',
33216                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33217                         cn : [
33218                             {
33219                                 tag : 'div',
33220                                 cls : 'btn-group roo-document-viewer-download',
33221                                 cn : [
33222                                     {
33223                                         tag : 'button',
33224                                         cls : 'btn btn-default',
33225                                         html : '<i class="fa fa-download"></i>'
33226                                     }
33227                                 ]
33228                             },
33229                             {
33230                                 tag : 'div',
33231                                 cls : 'btn-group roo-document-viewer-trash',
33232                                 cn : [
33233                                     {
33234                                         tag : 'button',
33235                                         cls : 'btn btn-default',
33236                                         html : '<i class="fa fa-trash"></i>'
33237                                     }
33238                                 ]
33239                             }
33240                         ]
33241                     }
33242                 }
33243             ]
33244         };
33245         
33246         return cfg;
33247     },
33248     
33249     initEvents : function()
33250     {
33251         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33252         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33253         
33254         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33255         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33256         
33257         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33258         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33259         
33260         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33261         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33262         
33263         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33264         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33265         
33266         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33267         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33268         
33269         this.bodyEl.on('click', this.onClick, this);
33270         this.downloadBtn.on('click', this.onDownload, this);
33271         this.trashBtn.on('click', this.onTrash, this);
33272         
33273         this.downloadBtn.hide();
33274         this.trashBtn.hide();
33275         
33276         if(this.showDownload){
33277             this.downloadBtn.show();
33278         }
33279         
33280         if(this.showTrash){
33281             this.trashBtn.show();
33282         }
33283         
33284         if(!this.showDownload && !this.showTrash) {
33285             this.footerEl.hide();
33286         }
33287         
33288     },
33289     
33290     initial : function()
33291     {
33292         this.fireEvent('initial', this);
33293         
33294     },
33295     
33296     onClick : function(e)
33297     {
33298         e.preventDefault();
33299         
33300         this.fireEvent('click', this);
33301     },
33302     
33303     onDownload : function(e)
33304     {
33305         e.preventDefault();
33306         
33307         this.fireEvent('download', this);
33308     },
33309     
33310     onTrash : function(e)
33311     {
33312         e.preventDefault();
33313         
33314         this.fireEvent('trash', this);
33315     }
33316     
33317 });
33318 /*
33319  * - LGPL
33320  *
33321  * nav progress bar
33322  * 
33323  */
33324
33325 /**
33326  * @class Roo.bootstrap.NavProgressBar
33327  * @extends Roo.bootstrap.Component
33328  * Bootstrap NavProgressBar class
33329  * 
33330  * @constructor
33331  * Create a new nav progress bar
33332  * @param {Object} config The config object
33333  */
33334
33335 Roo.bootstrap.NavProgressBar = function(config){
33336     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33337
33338     this.bullets = this.bullets || [];
33339    
33340 //    Roo.bootstrap.NavProgressBar.register(this);
33341      this.addEvents({
33342         /**
33343              * @event changed
33344              * Fires when the active item changes
33345              * @param {Roo.bootstrap.NavProgressBar} this
33346              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33347              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33348          */
33349         'changed': true
33350      });
33351     
33352 };
33353
33354 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33355     
33356     bullets : [],
33357     barItems : [],
33358     
33359     getAutoCreate : function()
33360     {
33361         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33362         
33363         cfg = {
33364             tag : 'div',
33365             cls : 'roo-navigation-bar-group',
33366             cn : [
33367                 {
33368                     tag : 'div',
33369                     cls : 'roo-navigation-top-bar'
33370                 },
33371                 {
33372                     tag : 'div',
33373                     cls : 'roo-navigation-bullets-bar',
33374                     cn : [
33375                         {
33376                             tag : 'ul',
33377                             cls : 'roo-navigation-bar'
33378                         }
33379                     ]
33380                 },
33381                 
33382                 {
33383                     tag : 'div',
33384                     cls : 'roo-navigation-bottom-bar'
33385                 }
33386             ]
33387             
33388         };
33389         
33390         return cfg;
33391         
33392     },
33393     
33394     initEvents: function() 
33395     {
33396         
33397     },
33398     
33399     onRender : function(ct, position) 
33400     {
33401         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33402         
33403         if(this.bullets.length){
33404             Roo.each(this.bullets, function(b){
33405                this.addItem(b);
33406             }, this);
33407         }
33408         
33409         this.format();
33410         
33411     },
33412     
33413     addItem : function(cfg)
33414     {
33415         var item = new Roo.bootstrap.NavProgressItem(cfg);
33416         
33417         item.parentId = this.id;
33418         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33419         
33420         if(cfg.html){
33421             var top = new Roo.bootstrap.Element({
33422                 tag : 'div',
33423                 cls : 'roo-navigation-bar-text'
33424             });
33425             
33426             var bottom = new Roo.bootstrap.Element({
33427                 tag : 'div',
33428                 cls : 'roo-navigation-bar-text'
33429             });
33430             
33431             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33432             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33433             
33434             var topText = new Roo.bootstrap.Element({
33435                 tag : 'span',
33436                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33437             });
33438             
33439             var bottomText = new Roo.bootstrap.Element({
33440                 tag : 'span',
33441                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33442             });
33443             
33444             topText.onRender(top.el, null);
33445             bottomText.onRender(bottom.el, null);
33446             
33447             item.topEl = top;
33448             item.bottomEl = bottom;
33449         }
33450         
33451         this.barItems.push(item);
33452         
33453         return item;
33454     },
33455     
33456     getActive : function()
33457     {
33458         var active = false;
33459         
33460         Roo.each(this.barItems, function(v){
33461             
33462             if (!v.isActive()) {
33463                 return;
33464             }
33465             
33466             active = v;
33467             return false;
33468             
33469         });
33470         
33471         return active;
33472     },
33473     
33474     setActiveItem : function(item)
33475     {
33476         var prev = false;
33477         
33478         Roo.each(this.barItems, function(v){
33479             if (v.rid == item.rid) {
33480                 return ;
33481             }
33482             
33483             if (v.isActive()) {
33484                 v.setActive(false);
33485                 prev = v;
33486             }
33487         });
33488
33489         item.setActive(true);
33490         
33491         this.fireEvent('changed', this, item, prev);
33492     },
33493     
33494     getBarItem: function(rid)
33495     {
33496         var ret = false;
33497         
33498         Roo.each(this.barItems, function(e) {
33499             if (e.rid != rid) {
33500                 return;
33501             }
33502             
33503             ret =  e;
33504             return false;
33505         });
33506         
33507         return ret;
33508     },
33509     
33510     indexOfItem : function(item)
33511     {
33512         var index = false;
33513         
33514         Roo.each(this.barItems, function(v, i){
33515             
33516             if (v.rid != item.rid) {
33517                 return;
33518             }
33519             
33520             index = i;
33521             return false
33522         });
33523         
33524         return index;
33525     },
33526     
33527     setActiveNext : function()
33528     {
33529         var i = this.indexOfItem(this.getActive());
33530         
33531         if (i > this.barItems.length) {
33532             return;
33533         }
33534         
33535         this.setActiveItem(this.barItems[i+1]);
33536     },
33537     
33538     setActivePrev : function()
33539     {
33540         var i = this.indexOfItem(this.getActive());
33541         
33542         if (i  < 1) {
33543             return;
33544         }
33545         
33546         this.setActiveItem(this.barItems[i-1]);
33547     },
33548     
33549     format : function()
33550     {
33551         if(!this.barItems.length){
33552             return;
33553         }
33554      
33555         var width = 100 / this.barItems.length;
33556         
33557         Roo.each(this.barItems, function(i){
33558             i.el.setStyle('width', width + '%');
33559             i.topEl.el.setStyle('width', width + '%');
33560             i.bottomEl.el.setStyle('width', width + '%');
33561         }, this);
33562         
33563     }
33564     
33565 });
33566 /*
33567  * - LGPL
33568  *
33569  * Nav Progress Item
33570  * 
33571  */
33572
33573 /**
33574  * @class Roo.bootstrap.NavProgressItem
33575  * @extends Roo.bootstrap.Component
33576  * Bootstrap NavProgressItem class
33577  * @cfg {String} rid the reference id
33578  * @cfg {Boolean} active (true|false) Is item active default false
33579  * @cfg {Boolean} disabled (true|false) Is item active default false
33580  * @cfg {String} html
33581  * @cfg {String} position (top|bottom) text position default bottom
33582  * @cfg {String} icon show icon instead of number
33583  * 
33584  * @constructor
33585  * Create a new NavProgressItem
33586  * @param {Object} config The config object
33587  */
33588 Roo.bootstrap.NavProgressItem = function(config){
33589     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33590     this.addEvents({
33591         // raw events
33592         /**
33593          * @event click
33594          * The raw click event for the entire grid.
33595          * @param {Roo.bootstrap.NavProgressItem} this
33596          * @param {Roo.EventObject} e
33597          */
33598         "click" : true
33599     });
33600    
33601 };
33602
33603 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33604     
33605     rid : '',
33606     active : false,
33607     disabled : false,
33608     html : '',
33609     position : 'bottom',
33610     icon : false,
33611     
33612     getAutoCreate : function()
33613     {
33614         var iconCls = 'roo-navigation-bar-item-icon';
33615         
33616         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33617         
33618         var cfg = {
33619             tag: 'li',
33620             cls: 'roo-navigation-bar-item',
33621             cn : [
33622                 {
33623                     tag : 'i',
33624                     cls : iconCls
33625                 }
33626             ]
33627         };
33628         
33629         if(this.active){
33630             cfg.cls += ' active';
33631         }
33632         if(this.disabled){
33633             cfg.cls += ' disabled';
33634         }
33635         
33636         return cfg;
33637     },
33638     
33639     disable : function()
33640     {
33641         this.setDisabled(true);
33642     },
33643     
33644     enable : function()
33645     {
33646         this.setDisabled(false);
33647     },
33648     
33649     initEvents: function() 
33650     {
33651         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33652         
33653         this.iconEl.on('click', this.onClick, this);
33654     },
33655     
33656     onClick : function(e)
33657     {
33658         e.preventDefault();
33659         
33660         if(this.disabled){
33661             return;
33662         }
33663         
33664         if(this.fireEvent('click', this, e) === false){
33665             return;
33666         };
33667         
33668         this.parent().setActiveItem(this);
33669     },
33670     
33671     isActive: function () 
33672     {
33673         return this.active;
33674     },
33675     
33676     setActive : function(state)
33677     {
33678         if(this.active == state){
33679             return;
33680         }
33681         
33682         this.active = state;
33683         
33684         if (state) {
33685             this.el.addClass('active');
33686             return;
33687         }
33688         
33689         this.el.removeClass('active');
33690         
33691         return;
33692     },
33693     
33694     setDisabled : function(state)
33695     {
33696         if(this.disabled == state){
33697             return;
33698         }
33699         
33700         this.disabled = state;
33701         
33702         if (state) {
33703             this.el.addClass('disabled');
33704             return;
33705         }
33706         
33707         this.el.removeClass('disabled');
33708     },
33709     
33710     tooltipEl : function()
33711     {
33712         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33713     }
33714 });
33715  
33716
33717  /*
33718  * - LGPL
33719  *
33720  * FieldLabel
33721  * 
33722  */
33723
33724 /**
33725  * @class Roo.bootstrap.FieldLabel
33726  * @extends Roo.bootstrap.Component
33727  * Bootstrap FieldLabel class
33728  * @cfg {String} html contents of the element
33729  * @cfg {String} tag tag of the element default label
33730  * @cfg {String} cls class of the element
33731  * @cfg {String} target label target 
33732  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33733  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33734  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33735  * @cfg {String} iconTooltip default "This field is required"
33736  * @cfg {String} indicatorpos (left|right) default left
33737  * 
33738  * @constructor
33739  * Create a new FieldLabel
33740  * @param {Object} config The config object
33741  */
33742
33743 Roo.bootstrap.FieldLabel = function(config){
33744     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33745     
33746     this.addEvents({
33747             /**
33748              * @event invalid
33749              * Fires after the field has been marked as invalid.
33750              * @param {Roo.form.FieldLabel} this
33751              * @param {String} msg The validation message
33752              */
33753             invalid : true,
33754             /**
33755              * @event valid
33756              * Fires after the field has been validated with no errors.
33757              * @param {Roo.form.FieldLabel} this
33758              */
33759             valid : true
33760         });
33761 };
33762
33763 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33764     
33765     tag: 'label',
33766     cls: '',
33767     html: '',
33768     target: '',
33769     allowBlank : true,
33770     invalidClass : 'has-warning',
33771     validClass : 'has-success',
33772     iconTooltip : 'This field is required',
33773     indicatorpos : 'left',
33774     
33775     getAutoCreate : function(){
33776         
33777         var cls = "";
33778         if (!this.allowBlank) {
33779             cls  = "visible";
33780         }
33781         
33782         var cfg = {
33783             tag : this.tag,
33784             cls : 'roo-bootstrap-field-label ' + this.cls,
33785             for : this.target,
33786             cn : [
33787                 {
33788                     tag : 'i',
33789                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33790                     tooltip : this.iconTooltip
33791                 },
33792                 {
33793                     tag : 'span',
33794                     html : this.html
33795                 }
33796             ] 
33797         };
33798         
33799         if(this.indicatorpos == 'right'){
33800             var cfg = {
33801                 tag : this.tag,
33802                 cls : 'roo-bootstrap-field-label ' + this.cls,
33803                 for : this.target,
33804                 cn : [
33805                     {
33806                         tag : 'span',
33807                         html : this.html
33808                     },
33809                     {
33810                         tag : 'i',
33811                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33812                         tooltip : this.iconTooltip
33813                     }
33814                 ] 
33815             };
33816         }
33817         
33818         return cfg;
33819     },
33820     
33821     initEvents: function() 
33822     {
33823         Roo.bootstrap.Element.superclass.initEvents.call(this);
33824         
33825         this.indicator = this.indicatorEl();
33826         
33827         if(this.indicator){
33828             this.indicator.removeClass('visible');
33829             this.indicator.addClass('invisible');
33830         }
33831         
33832         Roo.bootstrap.FieldLabel.register(this);
33833     },
33834     
33835     indicatorEl : function()
33836     {
33837         var indicator = this.el.select('i.roo-required-indicator',true).first();
33838         
33839         if(!indicator){
33840             return false;
33841         }
33842         
33843         return indicator;
33844         
33845     },
33846     
33847     /**
33848      * Mark this field as valid
33849      */
33850     markValid : function()
33851     {
33852         if(this.indicator){
33853             this.indicator.removeClass('visible');
33854             this.indicator.addClass('invisible');
33855         }
33856         if (Roo.bootstrap.version == 3) {
33857             this.el.removeClass(this.invalidClass);
33858             this.el.addClass(this.validClass);
33859         } else {
33860             this.el.removeClass('is-invalid');
33861             this.el.addClass('is-valid');
33862         }
33863         
33864         
33865         this.fireEvent('valid', this);
33866     },
33867     
33868     /**
33869      * Mark this field as invalid
33870      * @param {String} msg The validation message
33871      */
33872     markInvalid : function(msg)
33873     {
33874         if(this.indicator){
33875             this.indicator.removeClass('invisible');
33876             this.indicator.addClass('visible');
33877         }
33878           if (Roo.bootstrap.version == 3) {
33879             this.el.removeClass(this.validClass);
33880             this.el.addClass(this.invalidClass);
33881         } else {
33882             this.el.removeClass('is-valid');
33883             this.el.addClass('is-invalid');
33884         }
33885         
33886         
33887         this.fireEvent('invalid', this, msg);
33888     }
33889     
33890    
33891 });
33892
33893 Roo.apply(Roo.bootstrap.FieldLabel, {
33894     
33895     groups: {},
33896     
33897      /**
33898     * register a FieldLabel Group
33899     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33900     */
33901     register : function(label)
33902     {
33903         if(this.groups.hasOwnProperty(label.target)){
33904             return;
33905         }
33906      
33907         this.groups[label.target] = label;
33908         
33909     },
33910     /**
33911     * fetch a FieldLabel Group based on the target
33912     * @param {string} target
33913     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33914     */
33915     get: function(target) {
33916         if (typeof(this.groups[target]) == 'undefined') {
33917             return false;
33918         }
33919         
33920         return this.groups[target] ;
33921     }
33922 });
33923
33924  
33925
33926  /*
33927  * - LGPL
33928  *
33929  * page DateSplitField.
33930  * 
33931  */
33932
33933
33934 /**
33935  * @class Roo.bootstrap.DateSplitField
33936  * @extends Roo.bootstrap.Component
33937  * Bootstrap DateSplitField class
33938  * @cfg {string} fieldLabel - the label associated
33939  * @cfg {Number} labelWidth set the width of label (0-12)
33940  * @cfg {String} labelAlign (top|left)
33941  * @cfg {Boolean} dayAllowBlank (true|false) default false
33942  * @cfg {Boolean} monthAllowBlank (true|false) default false
33943  * @cfg {Boolean} yearAllowBlank (true|false) default false
33944  * @cfg {string} dayPlaceholder 
33945  * @cfg {string} monthPlaceholder
33946  * @cfg {string} yearPlaceholder
33947  * @cfg {string} dayFormat default 'd'
33948  * @cfg {string} monthFormat default 'm'
33949  * @cfg {string} yearFormat default 'Y'
33950  * @cfg {Number} labellg set the width of label (1-12)
33951  * @cfg {Number} labelmd set the width of label (1-12)
33952  * @cfg {Number} labelsm set the width of label (1-12)
33953  * @cfg {Number} labelxs set the width of label (1-12)
33954
33955  *     
33956  * @constructor
33957  * Create a new DateSplitField
33958  * @param {Object} config The config object
33959  */
33960
33961 Roo.bootstrap.DateSplitField = function(config){
33962     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33963     
33964     this.addEvents({
33965         // raw events
33966          /**
33967          * @event years
33968          * getting the data of years
33969          * @param {Roo.bootstrap.DateSplitField} this
33970          * @param {Object} years
33971          */
33972         "years" : true,
33973         /**
33974          * @event days
33975          * getting the data of days
33976          * @param {Roo.bootstrap.DateSplitField} this
33977          * @param {Object} days
33978          */
33979         "days" : true,
33980         /**
33981          * @event invalid
33982          * Fires after the field has been marked as invalid.
33983          * @param {Roo.form.Field} this
33984          * @param {String} msg The validation message
33985          */
33986         invalid : true,
33987        /**
33988          * @event valid
33989          * Fires after the field has been validated with no errors.
33990          * @param {Roo.form.Field} this
33991          */
33992         valid : true
33993     });
33994 };
33995
33996 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33997     
33998     fieldLabel : '',
33999     labelAlign : 'top',
34000     labelWidth : 3,
34001     dayAllowBlank : false,
34002     monthAllowBlank : false,
34003     yearAllowBlank : false,
34004     dayPlaceholder : '',
34005     monthPlaceholder : '',
34006     yearPlaceholder : '',
34007     dayFormat : 'd',
34008     monthFormat : 'm',
34009     yearFormat : 'Y',
34010     isFormField : true,
34011     labellg : 0,
34012     labelmd : 0,
34013     labelsm : 0,
34014     labelxs : 0,
34015     
34016     getAutoCreate : function()
34017     {
34018         var cfg = {
34019             tag : 'div',
34020             cls : 'row roo-date-split-field-group',
34021             cn : [
34022                 {
34023                     tag : 'input',
34024                     type : 'hidden',
34025                     cls : 'form-hidden-field roo-date-split-field-group-value',
34026                     name : this.name
34027                 }
34028             ]
34029         };
34030         
34031         var labelCls = 'col-md-12';
34032         var contentCls = 'col-md-4';
34033         
34034         if(this.fieldLabel){
34035             
34036             var label = {
34037                 tag : 'div',
34038                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34039                 cn : [
34040                     {
34041                         tag : 'label',
34042                         html : this.fieldLabel
34043                     }
34044                 ]
34045             };
34046             
34047             if(this.labelAlign == 'left'){
34048             
34049                 if(this.labelWidth > 12){
34050                     label.style = "width: " + this.labelWidth + 'px';
34051                 }
34052
34053                 if(this.labelWidth < 13 && this.labelmd == 0){
34054                     this.labelmd = this.labelWidth;
34055                 }
34056
34057                 if(this.labellg > 0){
34058                     labelCls = ' col-lg-' + this.labellg;
34059                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34060                 }
34061
34062                 if(this.labelmd > 0){
34063                     labelCls = ' col-md-' + this.labelmd;
34064                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34065                 }
34066
34067                 if(this.labelsm > 0){
34068                     labelCls = ' col-sm-' + this.labelsm;
34069                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34070                 }
34071
34072                 if(this.labelxs > 0){
34073                     labelCls = ' col-xs-' + this.labelxs;
34074                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34075                 }
34076             }
34077             
34078             label.cls += ' ' + labelCls;
34079             
34080             cfg.cn.push(label);
34081         }
34082         
34083         Roo.each(['day', 'month', 'year'], function(t){
34084             cfg.cn.push({
34085                 tag : 'div',
34086                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34087             });
34088         }, this);
34089         
34090         return cfg;
34091     },
34092     
34093     inputEl: function ()
34094     {
34095         return this.el.select('.roo-date-split-field-group-value', true).first();
34096     },
34097     
34098     onRender : function(ct, position) 
34099     {
34100         var _this = this;
34101         
34102         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34103         
34104         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34105         
34106         this.dayField = new Roo.bootstrap.ComboBox({
34107             allowBlank : this.dayAllowBlank,
34108             alwaysQuery : true,
34109             displayField : 'value',
34110             editable : false,
34111             fieldLabel : '',
34112             forceSelection : true,
34113             mode : 'local',
34114             placeholder : this.dayPlaceholder,
34115             selectOnFocus : true,
34116             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34117             triggerAction : 'all',
34118             typeAhead : true,
34119             valueField : 'value',
34120             store : new Roo.data.SimpleStore({
34121                 data : (function() {    
34122                     var days = [];
34123                     _this.fireEvent('days', _this, days);
34124                     return days;
34125                 })(),
34126                 fields : [ 'value' ]
34127             }),
34128             listeners : {
34129                 select : function (_self, record, index)
34130                 {
34131                     _this.setValue(_this.getValue());
34132                 }
34133             }
34134         });
34135
34136         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34137         
34138         this.monthField = new Roo.bootstrap.MonthField({
34139             after : '<i class=\"fa fa-calendar\"></i>',
34140             allowBlank : this.monthAllowBlank,
34141             placeholder : this.monthPlaceholder,
34142             readOnly : true,
34143             listeners : {
34144                 render : function (_self)
34145                 {
34146                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34147                         e.preventDefault();
34148                         _self.focus();
34149                     });
34150                 },
34151                 select : function (_self, oldvalue, newvalue)
34152                 {
34153                     _this.setValue(_this.getValue());
34154                 }
34155             }
34156         });
34157         
34158         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34159         
34160         this.yearField = new Roo.bootstrap.ComboBox({
34161             allowBlank : this.yearAllowBlank,
34162             alwaysQuery : true,
34163             displayField : 'value',
34164             editable : false,
34165             fieldLabel : '',
34166             forceSelection : true,
34167             mode : 'local',
34168             placeholder : this.yearPlaceholder,
34169             selectOnFocus : true,
34170             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34171             triggerAction : 'all',
34172             typeAhead : true,
34173             valueField : 'value',
34174             store : new Roo.data.SimpleStore({
34175                 data : (function() {
34176                     var years = [];
34177                     _this.fireEvent('years', _this, years);
34178                     return years;
34179                 })(),
34180                 fields : [ 'value' ]
34181             }),
34182             listeners : {
34183                 select : function (_self, record, index)
34184                 {
34185                     _this.setValue(_this.getValue());
34186                 }
34187             }
34188         });
34189
34190         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34191     },
34192     
34193     setValue : function(v, format)
34194     {
34195         this.inputEl.dom.value = v;
34196         
34197         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34198         
34199         var d = Date.parseDate(v, f);
34200         
34201         if(!d){
34202             this.validate();
34203             return;
34204         }
34205         
34206         this.setDay(d.format(this.dayFormat));
34207         this.setMonth(d.format(this.monthFormat));
34208         this.setYear(d.format(this.yearFormat));
34209         
34210         this.validate();
34211         
34212         return;
34213     },
34214     
34215     setDay : function(v)
34216     {
34217         this.dayField.setValue(v);
34218         this.inputEl.dom.value = this.getValue();
34219         this.validate();
34220         return;
34221     },
34222     
34223     setMonth : function(v)
34224     {
34225         this.monthField.setValue(v, true);
34226         this.inputEl.dom.value = this.getValue();
34227         this.validate();
34228         return;
34229     },
34230     
34231     setYear : function(v)
34232     {
34233         this.yearField.setValue(v);
34234         this.inputEl.dom.value = this.getValue();
34235         this.validate();
34236         return;
34237     },
34238     
34239     getDay : function()
34240     {
34241         return this.dayField.getValue();
34242     },
34243     
34244     getMonth : function()
34245     {
34246         return this.monthField.getValue();
34247     },
34248     
34249     getYear : function()
34250     {
34251         return this.yearField.getValue();
34252     },
34253     
34254     getValue : function()
34255     {
34256         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34257         
34258         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34259         
34260         return date;
34261     },
34262     
34263     reset : function()
34264     {
34265         this.setDay('');
34266         this.setMonth('');
34267         this.setYear('');
34268         this.inputEl.dom.value = '';
34269         this.validate();
34270         return;
34271     },
34272     
34273     validate : function()
34274     {
34275         var d = this.dayField.validate();
34276         var m = this.monthField.validate();
34277         var y = this.yearField.validate();
34278         
34279         var valid = true;
34280         
34281         if(
34282                 (!this.dayAllowBlank && !d) ||
34283                 (!this.monthAllowBlank && !m) ||
34284                 (!this.yearAllowBlank && !y)
34285         ){
34286             valid = false;
34287         }
34288         
34289         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34290             return valid;
34291         }
34292         
34293         if(valid){
34294             this.markValid();
34295             return valid;
34296         }
34297         
34298         this.markInvalid();
34299         
34300         return valid;
34301     },
34302     
34303     markValid : function()
34304     {
34305         
34306         var label = this.el.select('label', true).first();
34307         var icon = this.el.select('i.fa-star', true).first();
34308
34309         if(label && icon){
34310             icon.remove();
34311         }
34312         
34313         this.fireEvent('valid', this);
34314     },
34315     
34316      /**
34317      * Mark this field as invalid
34318      * @param {String} msg The validation message
34319      */
34320     markInvalid : function(msg)
34321     {
34322         
34323         var label = this.el.select('label', true).first();
34324         var icon = this.el.select('i.fa-star', true).first();
34325
34326         if(label && !icon){
34327             this.el.select('.roo-date-split-field-label', true).createChild({
34328                 tag : 'i',
34329                 cls : 'text-danger fa fa-lg fa-star',
34330                 tooltip : 'This field is required',
34331                 style : 'margin-right:5px;'
34332             }, label, true);
34333         }
34334         
34335         this.fireEvent('invalid', this, msg);
34336     },
34337     
34338     clearInvalid : function()
34339     {
34340         var label = this.el.select('label', true).first();
34341         var icon = this.el.select('i.fa-star', true).first();
34342
34343         if(label && icon){
34344             icon.remove();
34345         }
34346         
34347         this.fireEvent('valid', this);
34348     },
34349     
34350     getName: function()
34351     {
34352         return this.name;
34353     }
34354     
34355 });
34356
34357  /**
34358  *
34359  * This is based on 
34360  * http://masonry.desandro.com
34361  *
34362  * The idea is to render all the bricks based on vertical width...
34363  *
34364  * The original code extends 'outlayer' - we might need to use that....
34365  * 
34366  */
34367
34368
34369 /**
34370  * @class Roo.bootstrap.LayoutMasonry
34371  * @extends Roo.bootstrap.Component
34372  * Bootstrap Layout Masonry class
34373  * 
34374  * @constructor
34375  * Create a new Element
34376  * @param {Object} config The config object
34377  */
34378
34379 Roo.bootstrap.LayoutMasonry = function(config){
34380     
34381     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34382     
34383     this.bricks = [];
34384     
34385     Roo.bootstrap.LayoutMasonry.register(this);
34386     
34387     this.addEvents({
34388         // raw events
34389         /**
34390          * @event layout
34391          * Fire after layout the items
34392          * @param {Roo.bootstrap.LayoutMasonry} this
34393          * @param {Roo.EventObject} e
34394          */
34395         "layout" : true
34396     });
34397     
34398 };
34399
34400 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34401     
34402     /**
34403      * @cfg {Boolean} isLayoutInstant = no animation?
34404      */   
34405     isLayoutInstant : false, // needed?
34406    
34407     /**
34408      * @cfg {Number} boxWidth  width of the columns
34409      */   
34410     boxWidth : 450,
34411     
34412       /**
34413      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34414      */   
34415     boxHeight : 0,
34416     
34417     /**
34418      * @cfg {Number} padWidth padding below box..
34419      */   
34420     padWidth : 10, 
34421     
34422     /**
34423      * @cfg {Number} gutter gutter width..
34424      */   
34425     gutter : 10,
34426     
34427      /**
34428      * @cfg {Number} maxCols maximum number of columns
34429      */   
34430     
34431     maxCols: 0,
34432     
34433     /**
34434      * @cfg {Boolean} isAutoInitial defalut true
34435      */   
34436     isAutoInitial : true, 
34437     
34438     containerWidth: 0,
34439     
34440     /**
34441      * @cfg {Boolean} isHorizontal defalut false
34442      */   
34443     isHorizontal : false, 
34444
34445     currentSize : null,
34446     
34447     tag: 'div',
34448     
34449     cls: '',
34450     
34451     bricks: null, //CompositeElement
34452     
34453     cols : 1,
34454     
34455     _isLayoutInited : false,
34456     
34457 //    isAlternative : false, // only use for vertical layout...
34458     
34459     /**
34460      * @cfg {Number} alternativePadWidth padding below box..
34461      */   
34462     alternativePadWidth : 50,
34463     
34464     selectedBrick : [],
34465     
34466     getAutoCreate : function(){
34467         
34468         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34469         
34470         var cfg = {
34471             tag: this.tag,
34472             cls: 'blog-masonary-wrapper ' + this.cls,
34473             cn : {
34474                 cls : 'mas-boxes masonary'
34475             }
34476         };
34477         
34478         return cfg;
34479     },
34480     
34481     getChildContainer: function( )
34482     {
34483         if (this.boxesEl) {
34484             return this.boxesEl;
34485         }
34486         
34487         this.boxesEl = this.el.select('.mas-boxes').first();
34488         
34489         return this.boxesEl;
34490     },
34491     
34492     
34493     initEvents : function()
34494     {
34495         var _this = this;
34496         
34497         if(this.isAutoInitial){
34498             Roo.log('hook children rendered');
34499             this.on('childrenrendered', function() {
34500                 Roo.log('children rendered');
34501                 _this.initial();
34502             } ,this);
34503         }
34504     },
34505     
34506     initial : function()
34507     {
34508         this.selectedBrick = [];
34509         
34510         this.currentSize = this.el.getBox(true);
34511         
34512         Roo.EventManager.onWindowResize(this.resize, this); 
34513
34514         if(!this.isAutoInitial){
34515             this.layout();
34516             return;
34517         }
34518         
34519         this.layout();
34520         
34521         return;
34522         //this.layout.defer(500,this);
34523         
34524     },
34525     
34526     resize : function()
34527     {
34528         var cs = this.el.getBox(true);
34529         
34530         if (
34531                 this.currentSize.width == cs.width && 
34532                 this.currentSize.x == cs.x && 
34533                 this.currentSize.height == cs.height && 
34534                 this.currentSize.y == cs.y 
34535         ) {
34536             Roo.log("no change in with or X or Y");
34537             return;
34538         }
34539         
34540         this.currentSize = cs;
34541         
34542         this.layout();
34543         
34544     },
34545     
34546     layout : function()
34547     {   
34548         this._resetLayout();
34549         
34550         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34551         
34552         this.layoutItems( isInstant );
34553       
34554         this._isLayoutInited = true;
34555         
34556         this.fireEvent('layout', this);
34557         
34558     },
34559     
34560     _resetLayout : function()
34561     {
34562         if(this.isHorizontal){
34563             this.horizontalMeasureColumns();
34564             return;
34565         }
34566         
34567         this.verticalMeasureColumns();
34568         
34569     },
34570     
34571     verticalMeasureColumns : function()
34572     {
34573         this.getContainerWidth();
34574         
34575 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34576 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34577 //            return;
34578 //        }
34579         
34580         var boxWidth = this.boxWidth + this.padWidth;
34581         
34582         if(this.containerWidth < this.boxWidth){
34583             boxWidth = this.containerWidth
34584         }
34585         
34586         var containerWidth = this.containerWidth;
34587         
34588         var cols = Math.floor(containerWidth / boxWidth);
34589         
34590         this.cols = Math.max( cols, 1 );
34591         
34592         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34593         
34594         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34595         
34596         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34597         
34598         this.colWidth = boxWidth + avail - this.padWidth;
34599         
34600         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34601         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34602     },
34603     
34604     horizontalMeasureColumns : function()
34605     {
34606         this.getContainerWidth();
34607         
34608         var boxWidth = this.boxWidth;
34609         
34610         if(this.containerWidth < boxWidth){
34611             boxWidth = this.containerWidth;
34612         }
34613         
34614         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34615         
34616         this.el.setHeight(boxWidth);
34617         
34618     },
34619     
34620     getContainerWidth : function()
34621     {
34622         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34623     },
34624     
34625     layoutItems : function( isInstant )
34626     {
34627         Roo.log(this.bricks);
34628         
34629         var items = Roo.apply([], this.bricks);
34630         
34631         if(this.isHorizontal){
34632             this._horizontalLayoutItems( items , isInstant );
34633             return;
34634         }
34635         
34636 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34637 //            this._verticalAlternativeLayoutItems( items , isInstant );
34638 //            return;
34639 //        }
34640         
34641         this._verticalLayoutItems( items , isInstant );
34642         
34643     },
34644     
34645     _verticalLayoutItems : function ( items , isInstant)
34646     {
34647         if ( !items || !items.length ) {
34648             return;
34649         }
34650         
34651         var standard = [
34652             ['xs', 'xs', 'xs', 'tall'],
34653             ['xs', 'xs', 'tall'],
34654             ['xs', 'xs', 'sm'],
34655             ['xs', 'xs', 'xs'],
34656             ['xs', 'tall'],
34657             ['xs', 'sm'],
34658             ['xs', 'xs'],
34659             ['xs'],
34660             
34661             ['sm', 'xs', 'xs'],
34662             ['sm', 'xs'],
34663             ['sm'],
34664             
34665             ['tall', 'xs', 'xs', 'xs'],
34666             ['tall', 'xs', 'xs'],
34667             ['tall', 'xs'],
34668             ['tall']
34669             
34670         ];
34671         
34672         var queue = [];
34673         
34674         var boxes = [];
34675         
34676         var box = [];
34677         
34678         Roo.each(items, function(item, k){
34679             
34680             switch (item.size) {
34681                 // these layouts take up a full box,
34682                 case 'md' :
34683                 case 'md-left' :
34684                 case 'md-right' :
34685                 case 'wide' :
34686                     
34687                     if(box.length){
34688                         boxes.push(box);
34689                         box = [];
34690                     }
34691                     
34692                     boxes.push([item]);
34693                     
34694                     break;
34695                     
34696                 case 'xs' :
34697                 case 'sm' :
34698                 case 'tall' :
34699                     
34700                     box.push(item);
34701                     
34702                     break;
34703                 default :
34704                     break;
34705                     
34706             }
34707             
34708         }, this);
34709         
34710         if(box.length){
34711             boxes.push(box);
34712             box = [];
34713         }
34714         
34715         var filterPattern = function(box, length)
34716         {
34717             if(!box.length){
34718                 return;
34719             }
34720             
34721             var match = false;
34722             
34723             var pattern = box.slice(0, length);
34724             
34725             var format = [];
34726             
34727             Roo.each(pattern, function(i){
34728                 format.push(i.size);
34729             }, this);
34730             
34731             Roo.each(standard, function(s){
34732                 
34733                 if(String(s) != String(format)){
34734                     return;
34735                 }
34736                 
34737                 match = true;
34738                 return false;
34739                 
34740             }, this);
34741             
34742             if(!match && length == 1){
34743                 return;
34744             }
34745             
34746             if(!match){
34747                 filterPattern(box, length - 1);
34748                 return;
34749             }
34750                 
34751             queue.push(pattern);
34752
34753             box = box.slice(length, box.length);
34754
34755             filterPattern(box, 4);
34756
34757             return;
34758             
34759         }
34760         
34761         Roo.each(boxes, function(box, k){
34762             
34763             if(!box.length){
34764                 return;
34765             }
34766             
34767             if(box.length == 1){
34768                 queue.push(box);
34769                 return;
34770             }
34771             
34772             filterPattern(box, 4);
34773             
34774         }, this);
34775         
34776         this._processVerticalLayoutQueue( queue, isInstant );
34777         
34778     },
34779     
34780 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34781 //    {
34782 //        if ( !items || !items.length ) {
34783 //            return;
34784 //        }
34785 //
34786 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34787 //        
34788 //    },
34789     
34790     _horizontalLayoutItems : function ( items , isInstant)
34791     {
34792         if ( !items || !items.length || items.length < 3) {
34793             return;
34794         }
34795         
34796         items.reverse();
34797         
34798         var eItems = items.slice(0, 3);
34799         
34800         items = items.slice(3, items.length);
34801         
34802         var standard = [
34803             ['xs', 'xs', 'xs', 'wide'],
34804             ['xs', 'xs', 'wide'],
34805             ['xs', 'xs', 'sm'],
34806             ['xs', 'xs', 'xs'],
34807             ['xs', 'wide'],
34808             ['xs', 'sm'],
34809             ['xs', 'xs'],
34810             ['xs'],
34811             
34812             ['sm', 'xs', 'xs'],
34813             ['sm', 'xs'],
34814             ['sm'],
34815             
34816             ['wide', 'xs', 'xs', 'xs'],
34817             ['wide', 'xs', 'xs'],
34818             ['wide', 'xs'],
34819             ['wide'],
34820             
34821             ['wide-thin']
34822         ];
34823         
34824         var queue = [];
34825         
34826         var boxes = [];
34827         
34828         var box = [];
34829         
34830         Roo.each(items, function(item, k){
34831             
34832             switch (item.size) {
34833                 case 'md' :
34834                 case 'md-left' :
34835                 case 'md-right' :
34836                 case 'tall' :
34837                     
34838                     if(box.length){
34839                         boxes.push(box);
34840                         box = [];
34841                     }
34842                     
34843                     boxes.push([item]);
34844                     
34845                     break;
34846                     
34847                 case 'xs' :
34848                 case 'sm' :
34849                 case 'wide' :
34850                 case 'wide-thin' :
34851                     
34852                     box.push(item);
34853                     
34854                     break;
34855                 default :
34856                     break;
34857                     
34858             }
34859             
34860         }, this);
34861         
34862         if(box.length){
34863             boxes.push(box);
34864             box = [];
34865         }
34866         
34867         var filterPattern = function(box, length)
34868         {
34869             if(!box.length){
34870                 return;
34871             }
34872             
34873             var match = false;
34874             
34875             var pattern = box.slice(0, length);
34876             
34877             var format = [];
34878             
34879             Roo.each(pattern, function(i){
34880                 format.push(i.size);
34881             }, this);
34882             
34883             Roo.each(standard, function(s){
34884                 
34885                 if(String(s) != String(format)){
34886                     return;
34887                 }
34888                 
34889                 match = true;
34890                 return false;
34891                 
34892             }, this);
34893             
34894             if(!match && length == 1){
34895                 return;
34896             }
34897             
34898             if(!match){
34899                 filterPattern(box, length - 1);
34900                 return;
34901             }
34902                 
34903             queue.push(pattern);
34904
34905             box = box.slice(length, box.length);
34906
34907             filterPattern(box, 4);
34908
34909             return;
34910             
34911         }
34912         
34913         Roo.each(boxes, function(box, k){
34914             
34915             if(!box.length){
34916                 return;
34917             }
34918             
34919             if(box.length == 1){
34920                 queue.push(box);
34921                 return;
34922             }
34923             
34924             filterPattern(box, 4);
34925             
34926         }, this);
34927         
34928         
34929         var prune = [];
34930         
34931         var pos = this.el.getBox(true);
34932         
34933         var minX = pos.x;
34934         
34935         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34936         
34937         var hit_end = false;
34938         
34939         Roo.each(queue, function(box){
34940             
34941             if(hit_end){
34942                 
34943                 Roo.each(box, function(b){
34944                 
34945                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34946                     b.el.hide();
34947
34948                 }, this);
34949
34950                 return;
34951             }
34952             
34953             var mx = 0;
34954             
34955             Roo.each(box, function(b){
34956                 
34957                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34958                 b.el.show();
34959
34960                 mx = Math.max(mx, b.x);
34961                 
34962             }, this);
34963             
34964             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34965             
34966             if(maxX < minX){
34967                 
34968                 Roo.each(box, function(b){
34969                 
34970                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34971                     b.el.hide();
34972                     
34973                 }, this);
34974                 
34975                 hit_end = true;
34976                 
34977                 return;
34978             }
34979             
34980             prune.push(box);
34981             
34982         }, this);
34983         
34984         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34985     },
34986     
34987     /** Sets position of item in DOM
34988     * @param {Element} item
34989     * @param {Number} x - horizontal position
34990     * @param {Number} y - vertical position
34991     * @param {Boolean} isInstant - disables transitions
34992     */
34993     _processVerticalLayoutQueue : function( queue, isInstant )
34994     {
34995         var pos = this.el.getBox(true);
34996         var x = pos.x;
34997         var y = pos.y;
34998         var maxY = [];
34999         
35000         for (var i = 0; i < this.cols; i++){
35001             maxY[i] = pos.y;
35002         }
35003         
35004         Roo.each(queue, function(box, k){
35005             
35006             var col = k % this.cols;
35007             
35008             Roo.each(box, function(b,kk){
35009                 
35010                 b.el.position('absolute');
35011                 
35012                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35013                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35014                 
35015                 if(b.size == 'md-left' || b.size == 'md-right'){
35016                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35017                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35018                 }
35019                 
35020                 b.el.setWidth(width);
35021                 b.el.setHeight(height);
35022                 // iframe?
35023                 b.el.select('iframe',true).setSize(width,height);
35024                 
35025             }, this);
35026             
35027             for (var i = 0; i < this.cols; i++){
35028                 
35029                 if(maxY[i] < maxY[col]){
35030                     col = i;
35031                     continue;
35032                 }
35033                 
35034                 col = Math.min(col, i);
35035                 
35036             }
35037             
35038             x = pos.x + col * (this.colWidth + this.padWidth);
35039             
35040             y = maxY[col];
35041             
35042             var positions = [];
35043             
35044             switch (box.length){
35045                 case 1 :
35046                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35047                     break;
35048                 case 2 :
35049                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35050                     break;
35051                 case 3 :
35052                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35053                     break;
35054                 case 4 :
35055                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35056                     break;
35057                 default :
35058                     break;
35059             }
35060             
35061             Roo.each(box, function(b,kk){
35062                 
35063                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35064                 
35065                 var sz = b.el.getSize();
35066                 
35067                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35068                 
35069             }, this);
35070             
35071         }, this);
35072         
35073         var mY = 0;
35074         
35075         for (var i = 0; i < this.cols; i++){
35076             mY = Math.max(mY, maxY[i]);
35077         }
35078         
35079         this.el.setHeight(mY - pos.y);
35080         
35081     },
35082     
35083 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35084 //    {
35085 //        var pos = this.el.getBox(true);
35086 //        var x = pos.x;
35087 //        var y = pos.y;
35088 //        var maxX = pos.right;
35089 //        
35090 //        var maxHeight = 0;
35091 //        
35092 //        Roo.each(items, function(item, k){
35093 //            
35094 //            var c = k % 2;
35095 //            
35096 //            item.el.position('absolute');
35097 //                
35098 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35099 //
35100 //            item.el.setWidth(width);
35101 //
35102 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35103 //
35104 //            item.el.setHeight(height);
35105 //            
35106 //            if(c == 0){
35107 //                item.el.setXY([x, y], isInstant ? false : true);
35108 //            } else {
35109 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35110 //            }
35111 //            
35112 //            y = y + height + this.alternativePadWidth;
35113 //            
35114 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35115 //            
35116 //        }, this);
35117 //        
35118 //        this.el.setHeight(maxHeight);
35119 //        
35120 //    },
35121     
35122     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35123     {
35124         var pos = this.el.getBox(true);
35125         
35126         var minX = pos.x;
35127         var minY = pos.y;
35128         
35129         var maxX = pos.right;
35130         
35131         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35132         
35133         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35134         
35135         Roo.each(queue, function(box, k){
35136             
35137             Roo.each(box, function(b, kk){
35138                 
35139                 b.el.position('absolute');
35140                 
35141                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35142                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35143                 
35144                 if(b.size == 'md-left' || b.size == 'md-right'){
35145                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35146                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35147                 }
35148                 
35149                 b.el.setWidth(width);
35150                 b.el.setHeight(height);
35151                 
35152             }, this);
35153             
35154             if(!box.length){
35155                 return;
35156             }
35157             
35158             var positions = [];
35159             
35160             switch (box.length){
35161                 case 1 :
35162                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35163                     break;
35164                 case 2 :
35165                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35166                     break;
35167                 case 3 :
35168                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35169                     break;
35170                 case 4 :
35171                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35172                     break;
35173                 default :
35174                     break;
35175             }
35176             
35177             Roo.each(box, function(b,kk){
35178                 
35179                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35180                 
35181                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35182                 
35183             }, this);
35184             
35185         }, this);
35186         
35187     },
35188     
35189     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35190     {
35191         Roo.each(eItems, function(b,k){
35192             
35193             b.size = (k == 0) ? 'sm' : 'xs';
35194             b.x = (k == 0) ? 2 : 1;
35195             b.y = (k == 0) ? 2 : 1;
35196             
35197             b.el.position('absolute');
35198             
35199             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35200                 
35201             b.el.setWidth(width);
35202             
35203             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35204             
35205             b.el.setHeight(height);
35206             
35207         }, this);
35208
35209         var positions = [];
35210         
35211         positions.push({
35212             x : maxX - this.unitWidth * 2 - this.gutter,
35213             y : minY
35214         });
35215         
35216         positions.push({
35217             x : maxX - this.unitWidth,
35218             y : minY + (this.unitWidth + this.gutter) * 2
35219         });
35220         
35221         positions.push({
35222             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35223             y : minY
35224         });
35225         
35226         Roo.each(eItems, function(b,k){
35227             
35228             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35229
35230         }, this);
35231         
35232     },
35233     
35234     getVerticalOneBoxColPositions : function(x, y, box)
35235     {
35236         var pos = [];
35237         
35238         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35239         
35240         if(box[0].size == 'md-left'){
35241             rand = 0;
35242         }
35243         
35244         if(box[0].size == 'md-right'){
35245             rand = 1;
35246         }
35247         
35248         pos.push({
35249             x : x + (this.unitWidth + this.gutter) * rand,
35250             y : y
35251         });
35252         
35253         return pos;
35254     },
35255     
35256     getVerticalTwoBoxColPositions : function(x, y, box)
35257     {
35258         var pos = [];
35259         
35260         if(box[0].size == 'xs'){
35261             
35262             pos.push({
35263                 x : x,
35264                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35265             });
35266
35267             pos.push({
35268                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35269                 y : y
35270             });
35271             
35272             return pos;
35273             
35274         }
35275         
35276         pos.push({
35277             x : x,
35278             y : y
35279         });
35280
35281         pos.push({
35282             x : x + (this.unitWidth + this.gutter) * 2,
35283             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35284         });
35285         
35286         return pos;
35287         
35288     },
35289     
35290     getVerticalThreeBoxColPositions : function(x, y, box)
35291     {
35292         var pos = [];
35293         
35294         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35295             
35296             pos.push({
35297                 x : x,
35298                 y : y
35299             });
35300
35301             pos.push({
35302                 x : x + (this.unitWidth + this.gutter) * 1,
35303                 y : y
35304             });
35305             
35306             pos.push({
35307                 x : x + (this.unitWidth + this.gutter) * 2,
35308                 y : y
35309             });
35310             
35311             return pos;
35312             
35313         }
35314         
35315         if(box[0].size == 'xs' && box[1].size == 'xs'){
35316             
35317             pos.push({
35318                 x : x,
35319                 y : y
35320             });
35321
35322             pos.push({
35323                 x : x,
35324                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35325             });
35326             
35327             pos.push({
35328                 x : x + (this.unitWidth + this.gutter) * 1,
35329                 y : y
35330             });
35331             
35332             return pos;
35333             
35334         }
35335         
35336         pos.push({
35337             x : x,
35338             y : y
35339         });
35340
35341         pos.push({
35342             x : x + (this.unitWidth + this.gutter) * 2,
35343             y : y
35344         });
35345
35346         pos.push({
35347             x : x + (this.unitWidth + this.gutter) * 2,
35348             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35349         });
35350             
35351         return pos;
35352         
35353     },
35354     
35355     getVerticalFourBoxColPositions : function(x, y, box)
35356     {
35357         var pos = [];
35358         
35359         if(box[0].size == 'xs'){
35360             
35361             pos.push({
35362                 x : x,
35363                 y : y
35364             });
35365
35366             pos.push({
35367                 x : x,
35368                 y : y + (this.unitHeight + this.gutter) * 1
35369             });
35370             
35371             pos.push({
35372                 x : x,
35373                 y : y + (this.unitHeight + this.gutter) * 2
35374             });
35375             
35376             pos.push({
35377                 x : x + (this.unitWidth + this.gutter) * 1,
35378                 y : y
35379             });
35380             
35381             return pos;
35382             
35383         }
35384         
35385         pos.push({
35386             x : x,
35387             y : y
35388         });
35389
35390         pos.push({
35391             x : x + (this.unitWidth + this.gutter) * 2,
35392             y : y
35393         });
35394
35395         pos.push({
35396             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35397             y : y + (this.unitHeight + this.gutter) * 1
35398         });
35399
35400         pos.push({
35401             x : x + (this.unitWidth + this.gutter) * 2,
35402             y : y + (this.unitWidth + this.gutter) * 2
35403         });
35404
35405         return pos;
35406         
35407     },
35408     
35409     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35410     {
35411         var pos = [];
35412         
35413         if(box[0].size == 'md-left'){
35414             pos.push({
35415                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35416                 y : minY
35417             });
35418             
35419             return pos;
35420         }
35421         
35422         if(box[0].size == 'md-right'){
35423             pos.push({
35424                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35425                 y : minY + (this.unitWidth + this.gutter) * 1
35426             });
35427             
35428             return pos;
35429         }
35430         
35431         var rand = Math.floor(Math.random() * (4 - box[0].y));
35432         
35433         pos.push({
35434             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35435             y : minY + (this.unitWidth + this.gutter) * rand
35436         });
35437         
35438         return pos;
35439         
35440     },
35441     
35442     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35443     {
35444         var pos = [];
35445         
35446         if(box[0].size == 'xs'){
35447             
35448             pos.push({
35449                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35450                 y : minY
35451             });
35452
35453             pos.push({
35454                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35455                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35456             });
35457             
35458             return pos;
35459             
35460         }
35461         
35462         pos.push({
35463             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35464             y : minY
35465         });
35466
35467         pos.push({
35468             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35469             y : minY + (this.unitWidth + this.gutter) * 2
35470         });
35471         
35472         return pos;
35473         
35474     },
35475     
35476     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35477     {
35478         var pos = [];
35479         
35480         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35481             
35482             pos.push({
35483                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35484                 y : minY
35485             });
35486
35487             pos.push({
35488                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35489                 y : minY + (this.unitWidth + this.gutter) * 1
35490             });
35491             
35492             pos.push({
35493                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35494                 y : minY + (this.unitWidth + this.gutter) * 2
35495             });
35496             
35497             return pos;
35498             
35499         }
35500         
35501         if(box[0].size == 'xs' && box[1].size == 'xs'){
35502             
35503             pos.push({
35504                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35505                 y : minY
35506             });
35507
35508             pos.push({
35509                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35510                 y : minY
35511             });
35512             
35513             pos.push({
35514                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35515                 y : minY + (this.unitWidth + this.gutter) * 1
35516             });
35517             
35518             return pos;
35519             
35520         }
35521         
35522         pos.push({
35523             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35524             y : minY
35525         });
35526
35527         pos.push({
35528             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35529             y : minY + (this.unitWidth + this.gutter) * 2
35530         });
35531
35532         pos.push({
35533             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35534             y : minY + (this.unitWidth + this.gutter) * 2
35535         });
35536             
35537         return pos;
35538         
35539     },
35540     
35541     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35542     {
35543         var pos = [];
35544         
35545         if(box[0].size == 'xs'){
35546             
35547             pos.push({
35548                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35549                 y : minY
35550             });
35551
35552             pos.push({
35553                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35554                 y : minY
35555             });
35556             
35557             pos.push({
35558                 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),
35559                 y : minY
35560             });
35561             
35562             pos.push({
35563                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35564                 y : minY + (this.unitWidth + this.gutter) * 1
35565             });
35566             
35567             return pos;
35568             
35569         }
35570         
35571         pos.push({
35572             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35573             y : minY
35574         });
35575         
35576         pos.push({
35577             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35578             y : minY + (this.unitWidth + this.gutter) * 2
35579         });
35580         
35581         pos.push({
35582             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35583             y : minY + (this.unitWidth + this.gutter) * 2
35584         });
35585         
35586         pos.push({
35587             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),
35588             y : minY + (this.unitWidth + this.gutter) * 2
35589         });
35590
35591         return pos;
35592         
35593     },
35594     
35595     /**
35596     * remove a Masonry Brick
35597     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35598     */
35599     removeBrick : function(brick_id)
35600     {
35601         if (!brick_id) {
35602             return;
35603         }
35604         
35605         for (var i = 0; i<this.bricks.length; i++) {
35606             if (this.bricks[i].id == brick_id) {
35607                 this.bricks.splice(i,1);
35608                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35609                 this.initial();
35610             }
35611         }
35612     },
35613     
35614     /**
35615     * adds a Masonry Brick
35616     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35617     */
35618     addBrick : function(cfg)
35619     {
35620         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35621         //this.register(cn);
35622         cn.parentId = this.id;
35623         cn.render(this.el);
35624         return cn;
35625     },
35626     
35627     /**
35628     * register a Masonry Brick
35629     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35630     */
35631     
35632     register : function(brick)
35633     {
35634         this.bricks.push(brick);
35635         brick.masonryId = this.id;
35636     },
35637     
35638     /**
35639     * clear all the Masonry Brick
35640     */
35641     clearAll : function()
35642     {
35643         this.bricks = [];
35644         //this.getChildContainer().dom.innerHTML = "";
35645         this.el.dom.innerHTML = '';
35646     },
35647     
35648     getSelected : function()
35649     {
35650         if (!this.selectedBrick) {
35651             return false;
35652         }
35653         
35654         return this.selectedBrick;
35655     }
35656 });
35657
35658 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35659     
35660     groups: {},
35661      /**
35662     * register a Masonry Layout
35663     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35664     */
35665     
35666     register : function(layout)
35667     {
35668         this.groups[layout.id] = layout;
35669     },
35670     /**
35671     * fetch a  Masonry Layout based on the masonry layout ID
35672     * @param {string} the masonry layout to add
35673     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35674     */
35675     
35676     get: function(layout_id) {
35677         if (typeof(this.groups[layout_id]) == 'undefined') {
35678             return false;
35679         }
35680         return this.groups[layout_id] ;
35681     }
35682     
35683     
35684     
35685 });
35686
35687  
35688
35689  /**
35690  *
35691  * This is based on 
35692  * http://masonry.desandro.com
35693  *
35694  * The idea is to render all the bricks based on vertical width...
35695  *
35696  * The original code extends 'outlayer' - we might need to use that....
35697  * 
35698  */
35699
35700
35701 /**
35702  * @class Roo.bootstrap.LayoutMasonryAuto
35703  * @extends Roo.bootstrap.Component
35704  * Bootstrap Layout Masonry class
35705  * 
35706  * @constructor
35707  * Create a new Element
35708  * @param {Object} config The config object
35709  */
35710
35711 Roo.bootstrap.LayoutMasonryAuto = function(config){
35712     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35713 };
35714
35715 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35716     
35717       /**
35718      * @cfg {Boolean} isFitWidth  - resize the width..
35719      */   
35720     isFitWidth : false,  // options..
35721     /**
35722      * @cfg {Boolean} isOriginLeft = left align?
35723      */   
35724     isOriginLeft : true,
35725     /**
35726      * @cfg {Boolean} isOriginTop = top align?
35727      */   
35728     isOriginTop : false,
35729     /**
35730      * @cfg {Boolean} isLayoutInstant = no animation?
35731      */   
35732     isLayoutInstant : false, // needed?
35733     /**
35734      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35735      */   
35736     isResizingContainer : true,
35737     /**
35738      * @cfg {Number} columnWidth  width of the columns 
35739      */   
35740     
35741     columnWidth : 0,
35742     
35743     /**
35744      * @cfg {Number} maxCols maximum number of columns
35745      */   
35746     
35747     maxCols: 0,
35748     /**
35749      * @cfg {Number} padHeight padding below box..
35750      */   
35751     
35752     padHeight : 10, 
35753     
35754     /**
35755      * @cfg {Boolean} isAutoInitial defalut true
35756      */   
35757     
35758     isAutoInitial : true, 
35759     
35760     // private?
35761     gutter : 0,
35762     
35763     containerWidth: 0,
35764     initialColumnWidth : 0,
35765     currentSize : null,
35766     
35767     colYs : null, // array.
35768     maxY : 0,
35769     padWidth: 10,
35770     
35771     
35772     tag: 'div',
35773     cls: '',
35774     bricks: null, //CompositeElement
35775     cols : 0, // array?
35776     // element : null, // wrapped now this.el
35777     _isLayoutInited : null, 
35778     
35779     
35780     getAutoCreate : function(){
35781         
35782         var cfg = {
35783             tag: this.tag,
35784             cls: 'blog-masonary-wrapper ' + this.cls,
35785             cn : {
35786                 cls : 'mas-boxes masonary'
35787             }
35788         };
35789         
35790         return cfg;
35791     },
35792     
35793     getChildContainer: function( )
35794     {
35795         if (this.boxesEl) {
35796             return this.boxesEl;
35797         }
35798         
35799         this.boxesEl = this.el.select('.mas-boxes').first();
35800         
35801         return this.boxesEl;
35802     },
35803     
35804     
35805     initEvents : function()
35806     {
35807         var _this = this;
35808         
35809         if(this.isAutoInitial){
35810             Roo.log('hook children rendered');
35811             this.on('childrenrendered', function() {
35812                 Roo.log('children rendered');
35813                 _this.initial();
35814             } ,this);
35815         }
35816         
35817     },
35818     
35819     initial : function()
35820     {
35821         this.reloadItems();
35822
35823         this.currentSize = this.el.getBox(true);
35824
35825         /// was window resize... - let's see if this works..
35826         Roo.EventManager.onWindowResize(this.resize, this); 
35827
35828         if(!this.isAutoInitial){
35829             this.layout();
35830             return;
35831         }
35832         
35833         this.layout.defer(500,this);
35834     },
35835     
35836     reloadItems: function()
35837     {
35838         this.bricks = this.el.select('.masonry-brick', true);
35839         
35840         this.bricks.each(function(b) {
35841             //Roo.log(b.getSize());
35842             if (!b.attr('originalwidth')) {
35843                 b.attr('originalwidth',  b.getSize().width);
35844             }
35845             
35846         });
35847         
35848         Roo.log(this.bricks.elements.length);
35849     },
35850     
35851     resize : function()
35852     {
35853         Roo.log('resize');
35854         var cs = this.el.getBox(true);
35855         
35856         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35857             Roo.log("no change in with or X");
35858             return;
35859         }
35860         this.currentSize = cs;
35861         this.layout();
35862     },
35863     
35864     layout : function()
35865     {
35866          Roo.log('layout');
35867         this._resetLayout();
35868         //this._manageStamps();
35869       
35870         // don't animate first layout
35871         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35872         this.layoutItems( isInstant );
35873       
35874         // flag for initalized
35875         this._isLayoutInited = true;
35876     },
35877     
35878     layoutItems : function( isInstant )
35879     {
35880         //var items = this._getItemsForLayout( this.items );
35881         // original code supports filtering layout items.. we just ignore it..
35882         
35883         this._layoutItems( this.bricks , isInstant );
35884       
35885         this._postLayout();
35886     },
35887     _layoutItems : function ( items , isInstant)
35888     {
35889        //this.fireEvent( 'layout', this, items );
35890     
35891
35892         if ( !items || !items.elements.length ) {
35893           // no items, emit event with empty array
35894             return;
35895         }
35896
35897         var queue = [];
35898         items.each(function(item) {
35899             Roo.log("layout item");
35900             Roo.log(item);
35901             // get x/y object from method
35902             var position = this._getItemLayoutPosition( item );
35903             // enqueue
35904             position.item = item;
35905             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35906             queue.push( position );
35907         }, this);
35908       
35909         this._processLayoutQueue( queue );
35910     },
35911     /** Sets position of item in DOM
35912     * @param {Element} item
35913     * @param {Number} x - horizontal position
35914     * @param {Number} y - vertical position
35915     * @param {Boolean} isInstant - disables transitions
35916     */
35917     _processLayoutQueue : function( queue )
35918     {
35919         for ( var i=0, len = queue.length; i < len; i++ ) {
35920             var obj = queue[i];
35921             obj.item.position('absolute');
35922             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35923         }
35924     },
35925       
35926     
35927     /**
35928     * Any logic you want to do after each layout,
35929     * i.e. size the container
35930     */
35931     _postLayout : function()
35932     {
35933         this.resizeContainer();
35934     },
35935     
35936     resizeContainer : function()
35937     {
35938         if ( !this.isResizingContainer ) {
35939             return;
35940         }
35941         var size = this._getContainerSize();
35942         if ( size ) {
35943             this.el.setSize(size.width,size.height);
35944             this.boxesEl.setSize(size.width,size.height);
35945         }
35946     },
35947     
35948     
35949     
35950     _resetLayout : function()
35951     {
35952         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35953         this.colWidth = this.el.getWidth();
35954         //this.gutter = this.el.getWidth(); 
35955         
35956         this.measureColumns();
35957
35958         // reset column Y
35959         var i = this.cols;
35960         this.colYs = [];
35961         while (i--) {
35962             this.colYs.push( 0 );
35963         }
35964     
35965         this.maxY = 0;
35966     },
35967
35968     measureColumns : function()
35969     {
35970         this.getContainerWidth();
35971       // if columnWidth is 0, default to outerWidth of first item
35972         if ( !this.columnWidth ) {
35973             var firstItem = this.bricks.first();
35974             Roo.log(firstItem);
35975             this.columnWidth  = this.containerWidth;
35976             if (firstItem && firstItem.attr('originalwidth') ) {
35977                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35978             }
35979             // columnWidth fall back to item of first element
35980             Roo.log("set column width?");
35981                         this.initialColumnWidth = this.columnWidth  ;
35982
35983             // if first elem has no width, default to size of container
35984             
35985         }
35986         
35987         
35988         if (this.initialColumnWidth) {
35989             this.columnWidth = this.initialColumnWidth;
35990         }
35991         
35992         
35993             
35994         // column width is fixed at the top - however if container width get's smaller we should
35995         // reduce it...
35996         
35997         // this bit calcs how man columns..
35998             
35999         var columnWidth = this.columnWidth += this.gutter;
36000       
36001         // calculate columns
36002         var containerWidth = this.containerWidth + this.gutter;
36003         
36004         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36005         // fix rounding errors, typically with gutters
36006         var excess = columnWidth - containerWidth % columnWidth;
36007         
36008         
36009         // if overshoot is less than a pixel, round up, otherwise floor it
36010         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36011         cols = Math[ mathMethod ]( cols );
36012         this.cols = Math.max( cols, 1 );
36013         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36014         
36015          // padding positioning..
36016         var totalColWidth = this.cols * this.columnWidth;
36017         var padavail = this.containerWidth - totalColWidth;
36018         // so for 2 columns - we need 3 'pads'
36019         
36020         var padNeeded = (1+this.cols) * this.padWidth;
36021         
36022         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36023         
36024         this.columnWidth += padExtra
36025         //this.padWidth = Math.floor(padavail /  ( this.cols));
36026         
36027         // adjust colum width so that padding is fixed??
36028         
36029         // we have 3 columns ... total = width * 3
36030         // we have X left over... that should be used by 
36031         
36032         //if (this.expandC) {
36033             
36034         //}
36035         
36036         
36037         
36038     },
36039     
36040     getContainerWidth : function()
36041     {
36042        /* // container is parent if fit width
36043         var container = this.isFitWidth ? this.element.parentNode : this.element;
36044         // check that this.size and size are there
36045         // IE8 triggers resize on body size change, so they might not be
36046         
36047         var size = getSize( container );  //FIXME
36048         this.containerWidth = size && size.innerWidth; //FIXME
36049         */
36050          
36051         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36052         
36053     },
36054     
36055     _getItemLayoutPosition : function( item )  // what is item?
36056     {
36057         // we resize the item to our columnWidth..
36058       
36059         item.setWidth(this.columnWidth);
36060         item.autoBoxAdjust  = false;
36061         
36062         var sz = item.getSize();
36063  
36064         // how many columns does this brick span
36065         var remainder = this.containerWidth % this.columnWidth;
36066         
36067         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36068         // round if off by 1 pixel, otherwise use ceil
36069         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36070         colSpan = Math.min( colSpan, this.cols );
36071         
36072         // normally this should be '1' as we dont' currently allow multi width columns..
36073         
36074         var colGroup = this._getColGroup( colSpan );
36075         // get the minimum Y value from the columns
36076         var minimumY = Math.min.apply( Math, colGroup );
36077         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36078         
36079         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36080          
36081         // position the brick
36082         var position = {
36083             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36084             y: this.currentSize.y + minimumY + this.padHeight
36085         };
36086         
36087         Roo.log(position);
36088         // apply setHeight to necessary columns
36089         var setHeight = minimumY + sz.height + this.padHeight;
36090         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36091         
36092         var setSpan = this.cols + 1 - colGroup.length;
36093         for ( var i = 0; i < setSpan; i++ ) {
36094           this.colYs[ shortColIndex + i ] = setHeight ;
36095         }
36096       
36097         return position;
36098     },
36099     
36100     /**
36101      * @param {Number} colSpan - number of columns the element spans
36102      * @returns {Array} colGroup
36103      */
36104     _getColGroup : function( colSpan )
36105     {
36106         if ( colSpan < 2 ) {
36107           // if brick spans only one column, use all the column Ys
36108           return this.colYs;
36109         }
36110       
36111         var colGroup = [];
36112         // how many different places could this brick fit horizontally
36113         var groupCount = this.cols + 1 - colSpan;
36114         // for each group potential horizontal position
36115         for ( var i = 0; i < groupCount; i++ ) {
36116           // make an array of colY values for that one group
36117           var groupColYs = this.colYs.slice( i, i + colSpan );
36118           // and get the max value of the array
36119           colGroup[i] = Math.max.apply( Math, groupColYs );
36120         }
36121         return colGroup;
36122     },
36123     /*
36124     _manageStamp : function( stamp )
36125     {
36126         var stampSize =  stamp.getSize();
36127         var offset = stamp.getBox();
36128         // get the columns that this stamp affects
36129         var firstX = this.isOriginLeft ? offset.x : offset.right;
36130         var lastX = firstX + stampSize.width;
36131         var firstCol = Math.floor( firstX / this.columnWidth );
36132         firstCol = Math.max( 0, firstCol );
36133         
36134         var lastCol = Math.floor( lastX / this.columnWidth );
36135         // lastCol should not go over if multiple of columnWidth #425
36136         lastCol -= lastX % this.columnWidth ? 0 : 1;
36137         lastCol = Math.min( this.cols - 1, lastCol );
36138         
36139         // set colYs to bottom of the stamp
36140         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36141             stampSize.height;
36142             
36143         for ( var i = firstCol; i <= lastCol; i++ ) {
36144           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36145         }
36146     },
36147     */
36148     
36149     _getContainerSize : function()
36150     {
36151         this.maxY = Math.max.apply( Math, this.colYs );
36152         var size = {
36153             height: this.maxY
36154         };
36155       
36156         if ( this.isFitWidth ) {
36157             size.width = this._getContainerFitWidth();
36158         }
36159       
36160         return size;
36161     },
36162     
36163     _getContainerFitWidth : function()
36164     {
36165         var unusedCols = 0;
36166         // count unused columns
36167         var i = this.cols;
36168         while ( --i ) {
36169           if ( this.colYs[i] !== 0 ) {
36170             break;
36171           }
36172           unusedCols++;
36173         }
36174         // fit container to columns that have been used
36175         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36176     },
36177     
36178     needsResizeLayout : function()
36179     {
36180         var previousWidth = this.containerWidth;
36181         this.getContainerWidth();
36182         return previousWidth !== this.containerWidth;
36183     }
36184  
36185 });
36186
36187  
36188
36189  /*
36190  * - LGPL
36191  *
36192  * element
36193  * 
36194  */
36195
36196 /**
36197  * @class Roo.bootstrap.MasonryBrick
36198  * @extends Roo.bootstrap.Component
36199  * Bootstrap MasonryBrick class
36200  * 
36201  * @constructor
36202  * Create a new MasonryBrick
36203  * @param {Object} config The config object
36204  */
36205
36206 Roo.bootstrap.MasonryBrick = function(config){
36207     
36208     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36209     
36210     Roo.bootstrap.MasonryBrick.register(this);
36211     
36212     this.addEvents({
36213         // raw events
36214         /**
36215          * @event click
36216          * When a MasonryBrick is clcik
36217          * @param {Roo.bootstrap.MasonryBrick} this
36218          * @param {Roo.EventObject} e
36219          */
36220         "click" : true
36221     });
36222 };
36223
36224 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36225     
36226     /**
36227      * @cfg {String} title
36228      */   
36229     title : '',
36230     /**
36231      * @cfg {String} html
36232      */   
36233     html : '',
36234     /**
36235      * @cfg {String} bgimage
36236      */   
36237     bgimage : '',
36238     /**
36239      * @cfg {String} videourl
36240      */   
36241     videourl : '',
36242     /**
36243      * @cfg {String} cls
36244      */   
36245     cls : '',
36246     /**
36247      * @cfg {String} href
36248      */   
36249     href : '',
36250     /**
36251      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36252      */   
36253     size : 'xs',
36254     
36255     /**
36256      * @cfg {String} placetitle (center|bottom)
36257      */   
36258     placetitle : '',
36259     
36260     /**
36261      * @cfg {Boolean} isFitContainer defalut true
36262      */   
36263     isFitContainer : true, 
36264     
36265     /**
36266      * @cfg {Boolean} preventDefault defalut false
36267      */   
36268     preventDefault : false, 
36269     
36270     /**
36271      * @cfg {Boolean} inverse defalut false
36272      */   
36273     maskInverse : false, 
36274     
36275     getAutoCreate : function()
36276     {
36277         if(!this.isFitContainer){
36278             return this.getSplitAutoCreate();
36279         }
36280         
36281         var cls = 'masonry-brick masonry-brick-full';
36282         
36283         if(this.href.length){
36284             cls += ' masonry-brick-link';
36285         }
36286         
36287         if(this.bgimage.length){
36288             cls += ' masonry-brick-image';
36289         }
36290         
36291         if(this.maskInverse){
36292             cls += ' mask-inverse';
36293         }
36294         
36295         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36296             cls += ' enable-mask';
36297         }
36298         
36299         if(this.size){
36300             cls += ' masonry-' + this.size + '-brick';
36301         }
36302         
36303         if(this.placetitle.length){
36304             
36305             switch (this.placetitle) {
36306                 case 'center' :
36307                     cls += ' masonry-center-title';
36308                     break;
36309                 case 'bottom' :
36310                     cls += ' masonry-bottom-title';
36311                     break;
36312                 default:
36313                     break;
36314             }
36315             
36316         } else {
36317             if(!this.html.length && !this.bgimage.length){
36318                 cls += ' masonry-center-title';
36319             }
36320
36321             if(!this.html.length && this.bgimage.length){
36322                 cls += ' masonry-bottom-title';
36323             }
36324         }
36325         
36326         if(this.cls){
36327             cls += ' ' + this.cls;
36328         }
36329         
36330         var cfg = {
36331             tag: (this.href.length) ? 'a' : 'div',
36332             cls: cls,
36333             cn: [
36334                 {
36335                     tag: 'div',
36336                     cls: 'masonry-brick-mask'
36337                 },
36338                 {
36339                     tag: 'div',
36340                     cls: 'masonry-brick-paragraph',
36341                     cn: []
36342                 }
36343             ]
36344         };
36345         
36346         if(this.href.length){
36347             cfg.href = this.href;
36348         }
36349         
36350         var cn = cfg.cn[1].cn;
36351         
36352         if(this.title.length){
36353             cn.push({
36354                 tag: 'h4',
36355                 cls: 'masonry-brick-title',
36356                 html: this.title
36357             });
36358         }
36359         
36360         if(this.html.length){
36361             cn.push({
36362                 tag: 'p',
36363                 cls: 'masonry-brick-text',
36364                 html: this.html
36365             });
36366         }
36367         
36368         if (!this.title.length && !this.html.length) {
36369             cfg.cn[1].cls += ' hide';
36370         }
36371         
36372         if(this.bgimage.length){
36373             cfg.cn.push({
36374                 tag: 'img',
36375                 cls: 'masonry-brick-image-view',
36376                 src: this.bgimage
36377             });
36378         }
36379         
36380         if(this.videourl.length){
36381             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36382             // youtube support only?
36383             cfg.cn.push({
36384                 tag: 'iframe',
36385                 cls: 'masonry-brick-image-view',
36386                 src: vurl,
36387                 frameborder : 0,
36388                 allowfullscreen : true
36389             });
36390         }
36391         
36392         return cfg;
36393         
36394     },
36395     
36396     getSplitAutoCreate : function()
36397     {
36398         var cls = 'masonry-brick masonry-brick-split';
36399         
36400         if(this.href.length){
36401             cls += ' masonry-brick-link';
36402         }
36403         
36404         if(this.bgimage.length){
36405             cls += ' masonry-brick-image';
36406         }
36407         
36408         if(this.size){
36409             cls += ' masonry-' + this.size + '-brick';
36410         }
36411         
36412         switch (this.placetitle) {
36413             case 'center' :
36414                 cls += ' masonry-center-title';
36415                 break;
36416             case 'bottom' :
36417                 cls += ' masonry-bottom-title';
36418                 break;
36419             default:
36420                 if(!this.bgimage.length){
36421                     cls += ' masonry-center-title';
36422                 }
36423
36424                 if(this.bgimage.length){
36425                     cls += ' masonry-bottom-title';
36426                 }
36427                 break;
36428         }
36429         
36430         if(this.cls){
36431             cls += ' ' + this.cls;
36432         }
36433         
36434         var cfg = {
36435             tag: (this.href.length) ? 'a' : 'div',
36436             cls: cls,
36437             cn: [
36438                 {
36439                     tag: 'div',
36440                     cls: 'masonry-brick-split-head',
36441                     cn: [
36442                         {
36443                             tag: 'div',
36444                             cls: 'masonry-brick-paragraph',
36445                             cn: []
36446                         }
36447                     ]
36448                 },
36449                 {
36450                     tag: 'div',
36451                     cls: 'masonry-brick-split-body',
36452                     cn: []
36453                 }
36454             ]
36455         };
36456         
36457         if(this.href.length){
36458             cfg.href = this.href;
36459         }
36460         
36461         if(this.title.length){
36462             cfg.cn[0].cn[0].cn.push({
36463                 tag: 'h4',
36464                 cls: 'masonry-brick-title',
36465                 html: this.title
36466             });
36467         }
36468         
36469         if(this.html.length){
36470             cfg.cn[1].cn.push({
36471                 tag: 'p',
36472                 cls: 'masonry-brick-text',
36473                 html: this.html
36474             });
36475         }
36476
36477         if(this.bgimage.length){
36478             cfg.cn[0].cn.push({
36479                 tag: 'img',
36480                 cls: 'masonry-brick-image-view',
36481                 src: this.bgimage
36482             });
36483         }
36484         
36485         if(this.videourl.length){
36486             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36487             // youtube support only?
36488             cfg.cn[0].cn.cn.push({
36489                 tag: 'iframe',
36490                 cls: 'masonry-brick-image-view',
36491                 src: vurl,
36492                 frameborder : 0,
36493                 allowfullscreen : true
36494             });
36495         }
36496         
36497         return cfg;
36498     },
36499     
36500     initEvents: function() 
36501     {
36502         switch (this.size) {
36503             case 'xs' :
36504                 this.x = 1;
36505                 this.y = 1;
36506                 break;
36507             case 'sm' :
36508                 this.x = 2;
36509                 this.y = 2;
36510                 break;
36511             case 'md' :
36512             case 'md-left' :
36513             case 'md-right' :
36514                 this.x = 3;
36515                 this.y = 3;
36516                 break;
36517             case 'tall' :
36518                 this.x = 2;
36519                 this.y = 3;
36520                 break;
36521             case 'wide' :
36522                 this.x = 3;
36523                 this.y = 2;
36524                 break;
36525             case 'wide-thin' :
36526                 this.x = 3;
36527                 this.y = 1;
36528                 break;
36529                         
36530             default :
36531                 break;
36532         }
36533         
36534         if(Roo.isTouch){
36535             this.el.on('touchstart', this.onTouchStart, this);
36536             this.el.on('touchmove', this.onTouchMove, this);
36537             this.el.on('touchend', this.onTouchEnd, this);
36538             this.el.on('contextmenu', this.onContextMenu, this);
36539         } else {
36540             this.el.on('mouseenter'  ,this.enter, this);
36541             this.el.on('mouseleave', this.leave, this);
36542             this.el.on('click', this.onClick, this);
36543         }
36544         
36545         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36546             this.parent().bricks.push(this);   
36547         }
36548         
36549     },
36550     
36551     onClick: function(e, el)
36552     {
36553         var time = this.endTimer - this.startTimer;
36554         // Roo.log(e.preventDefault());
36555         if(Roo.isTouch){
36556             if(time > 1000){
36557                 e.preventDefault();
36558                 return;
36559             }
36560         }
36561         
36562         if(!this.preventDefault){
36563             return;
36564         }
36565         
36566         e.preventDefault();
36567         
36568         if (this.activeClass != '') {
36569             this.selectBrick();
36570         }
36571         
36572         this.fireEvent('click', this, e);
36573     },
36574     
36575     enter: function(e, el)
36576     {
36577         e.preventDefault();
36578         
36579         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36580             return;
36581         }
36582         
36583         if(this.bgimage.length && this.html.length){
36584             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36585         }
36586     },
36587     
36588     leave: function(e, el)
36589     {
36590         e.preventDefault();
36591         
36592         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36593             return;
36594         }
36595         
36596         if(this.bgimage.length && this.html.length){
36597             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36598         }
36599     },
36600     
36601     onTouchStart: function(e, el)
36602     {
36603 //        e.preventDefault();
36604         
36605         this.touchmoved = false;
36606         
36607         if(!this.isFitContainer){
36608             return;
36609         }
36610         
36611         if(!this.bgimage.length || !this.html.length){
36612             return;
36613         }
36614         
36615         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36616         
36617         this.timer = new Date().getTime();
36618         
36619     },
36620     
36621     onTouchMove: function(e, el)
36622     {
36623         this.touchmoved = true;
36624     },
36625     
36626     onContextMenu : function(e,el)
36627     {
36628         e.preventDefault();
36629         e.stopPropagation();
36630         return false;
36631     },
36632     
36633     onTouchEnd: function(e, el)
36634     {
36635 //        e.preventDefault();
36636         
36637         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36638         
36639             this.leave(e,el);
36640             
36641             return;
36642         }
36643         
36644         if(!this.bgimage.length || !this.html.length){
36645             
36646             if(this.href.length){
36647                 window.location.href = this.href;
36648             }
36649             
36650             return;
36651         }
36652         
36653         if(!this.isFitContainer){
36654             return;
36655         }
36656         
36657         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36658         
36659         window.location.href = this.href;
36660     },
36661     
36662     //selection on single brick only
36663     selectBrick : function() {
36664         
36665         if (!this.parentId) {
36666             return;
36667         }
36668         
36669         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36670         var index = m.selectedBrick.indexOf(this.id);
36671         
36672         if ( index > -1) {
36673             m.selectedBrick.splice(index,1);
36674             this.el.removeClass(this.activeClass);
36675             return;
36676         }
36677         
36678         for(var i = 0; i < m.selectedBrick.length; i++) {
36679             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36680             b.el.removeClass(b.activeClass);
36681         }
36682         
36683         m.selectedBrick = [];
36684         
36685         m.selectedBrick.push(this.id);
36686         this.el.addClass(this.activeClass);
36687         return;
36688     },
36689     
36690     isSelected : function(){
36691         return this.el.hasClass(this.activeClass);
36692         
36693     }
36694 });
36695
36696 Roo.apply(Roo.bootstrap.MasonryBrick, {
36697     
36698     //groups: {},
36699     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36700      /**
36701     * register a Masonry Brick
36702     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36703     */
36704     
36705     register : function(brick)
36706     {
36707         //this.groups[brick.id] = brick;
36708         this.groups.add(brick.id, brick);
36709     },
36710     /**
36711     * fetch a  masonry brick based on the masonry brick ID
36712     * @param {string} the masonry brick to add
36713     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36714     */
36715     
36716     get: function(brick_id) 
36717     {
36718         // if (typeof(this.groups[brick_id]) == 'undefined') {
36719         //     return false;
36720         // }
36721         // return this.groups[brick_id] ;
36722         
36723         if(this.groups.key(brick_id)) {
36724             return this.groups.key(brick_id);
36725         }
36726         
36727         return false;
36728     }
36729     
36730     
36731     
36732 });
36733
36734  /*
36735  * - LGPL
36736  *
36737  * element
36738  * 
36739  */
36740
36741 /**
36742  * @class Roo.bootstrap.Brick
36743  * @extends Roo.bootstrap.Component
36744  * Bootstrap Brick class
36745  * 
36746  * @constructor
36747  * Create a new Brick
36748  * @param {Object} config The config object
36749  */
36750
36751 Roo.bootstrap.Brick = function(config){
36752     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36753     
36754     this.addEvents({
36755         // raw events
36756         /**
36757          * @event click
36758          * When a Brick is click
36759          * @param {Roo.bootstrap.Brick} this
36760          * @param {Roo.EventObject} e
36761          */
36762         "click" : true
36763     });
36764 };
36765
36766 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36767     
36768     /**
36769      * @cfg {String} title
36770      */   
36771     title : '',
36772     /**
36773      * @cfg {String} html
36774      */   
36775     html : '',
36776     /**
36777      * @cfg {String} bgimage
36778      */   
36779     bgimage : '',
36780     /**
36781      * @cfg {String} cls
36782      */   
36783     cls : '',
36784     /**
36785      * @cfg {String} href
36786      */   
36787     href : '',
36788     /**
36789      * @cfg {String} video
36790      */   
36791     video : '',
36792     /**
36793      * @cfg {Boolean} square
36794      */   
36795     square : true,
36796     
36797     getAutoCreate : function()
36798     {
36799         var cls = 'roo-brick';
36800         
36801         if(this.href.length){
36802             cls += ' roo-brick-link';
36803         }
36804         
36805         if(this.bgimage.length){
36806             cls += ' roo-brick-image';
36807         }
36808         
36809         if(!this.html.length && !this.bgimage.length){
36810             cls += ' roo-brick-center-title';
36811         }
36812         
36813         if(!this.html.length && this.bgimage.length){
36814             cls += ' roo-brick-bottom-title';
36815         }
36816         
36817         if(this.cls){
36818             cls += ' ' + this.cls;
36819         }
36820         
36821         var cfg = {
36822             tag: (this.href.length) ? 'a' : 'div',
36823             cls: cls,
36824             cn: [
36825                 {
36826                     tag: 'div',
36827                     cls: 'roo-brick-paragraph',
36828                     cn: []
36829                 }
36830             ]
36831         };
36832         
36833         if(this.href.length){
36834             cfg.href = this.href;
36835         }
36836         
36837         var cn = cfg.cn[0].cn;
36838         
36839         if(this.title.length){
36840             cn.push({
36841                 tag: 'h4',
36842                 cls: 'roo-brick-title',
36843                 html: this.title
36844             });
36845         }
36846         
36847         if(this.html.length){
36848             cn.push({
36849                 tag: 'p',
36850                 cls: 'roo-brick-text',
36851                 html: this.html
36852             });
36853         } else {
36854             cn.cls += ' hide';
36855         }
36856         
36857         if(this.bgimage.length){
36858             cfg.cn.push({
36859                 tag: 'img',
36860                 cls: 'roo-brick-image-view',
36861                 src: this.bgimage
36862             });
36863         }
36864         
36865         return cfg;
36866     },
36867     
36868     initEvents: function() 
36869     {
36870         if(this.title.length || this.html.length){
36871             this.el.on('mouseenter'  ,this.enter, this);
36872             this.el.on('mouseleave', this.leave, this);
36873         }
36874         
36875         Roo.EventManager.onWindowResize(this.resize, this); 
36876         
36877         if(this.bgimage.length){
36878             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36879             this.imageEl.on('load', this.onImageLoad, this);
36880             return;
36881         }
36882         
36883         this.resize();
36884     },
36885     
36886     onImageLoad : function()
36887     {
36888         this.resize();
36889     },
36890     
36891     resize : function()
36892     {
36893         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36894         
36895         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36896         
36897         if(this.bgimage.length){
36898             var image = this.el.select('.roo-brick-image-view', true).first();
36899             
36900             image.setWidth(paragraph.getWidth());
36901             
36902             if(this.square){
36903                 image.setHeight(paragraph.getWidth());
36904             }
36905             
36906             this.el.setHeight(image.getHeight());
36907             paragraph.setHeight(image.getHeight());
36908             
36909         }
36910         
36911     },
36912     
36913     enter: function(e, el)
36914     {
36915         e.preventDefault();
36916         
36917         if(this.bgimage.length){
36918             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36919             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36920         }
36921     },
36922     
36923     leave: function(e, el)
36924     {
36925         e.preventDefault();
36926         
36927         if(this.bgimage.length){
36928             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36929             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36930         }
36931     }
36932     
36933 });
36934
36935  
36936
36937  /*
36938  * - LGPL
36939  *
36940  * Number field 
36941  */
36942
36943 /**
36944  * @class Roo.bootstrap.NumberField
36945  * @extends Roo.bootstrap.Input
36946  * Bootstrap NumberField class
36947  * 
36948  * 
36949  * 
36950  * 
36951  * @constructor
36952  * Create a new NumberField
36953  * @param {Object} config The config object
36954  */
36955
36956 Roo.bootstrap.NumberField = function(config){
36957     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36958 };
36959
36960 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36961     
36962     /**
36963      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36964      */
36965     allowDecimals : true,
36966     /**
36967      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36968      */
36969     decimalSeparator : ".",
36970     /**
36971      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36972      */
36973     decimalPrecision : 2,
36974     /**
36975      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36976      */
36977     allowNegative : true,
36978     
36979     /**
36980      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36981      */
36982     allowZero: true,
36983     /**
36984      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36985      */
36986     minValue : Number.NEGATIVE_INFINITY,
36987     /**
36988      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36989      */
36990     maxValue : Number.MAX_VALUE,
36991     /**
36992      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36993      */
36994     minText : "The minimum value for this field is {0}",
36995     /**
36996      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36997      */
36998     maxText : "The maximum value for this field is {0}",
36999     /**
37000      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37001      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37002      */
37003     nanText : "{0} is not a valid number",
37004     /**
37005      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37006      */
37007     thousandsDelimiter : false,
37008     /**
37009      * @cfg {String} valueAlign alignment of value
37010      */
37011     valueAlign : "left",
37012
37013     getAutoCreate : function()
37014     {
37015         var hiddenInput = {
37016             tag: 'input',
37017             type: 'hidden',
37018             id: Roo.id(),
37019             cls: 'hidden-number-input'
37020         };
37021         
37022         if (this.name) {
37023             hiddenInput.name = this.name;
37024         }
37025         
37026         this.name = '';
37027         
37028         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37029         
37030         this.name = hiddenInput.name;
37031         
37032         if(cfg.cn.length > 0) {
37033             cfg.cn.push(hiddenInput);
37034         }
37035         
37036         return cfg;
37037     },
37038
37039     // private
37040     initEvents : function()
37041     {   
37042         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37043         
37044         var allowed = "0123456789";
37045         
37046         if(this.allowDecimals){
37047             allowed += this.decimalSeparator;
37048         }
37049         
37050         if(this.allowNegative){
37051             allowed += "-";
37052         }
37053         
37054         if(this.thousandsDelimiter) {
37055             allowed += ",";
37056         }
37057         
37058         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37059         
37060         var keyPress = function(e){
37061             
37062             var k = e.getKey();
37063             
37064             var c = e.getCharCode();
37065             
37066             if(
37067                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37068                     allowed.indexOf(String.fromCharCode(c)) === -1
37069             ){
37070                 e.stopEvent();
37071                 return;
37072             }
37073             
37074             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37075                 return;
37076             }
37077             
37078             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37079                 e.stopEvent();
37080             }
37081         };
37082         
37083         this.el.on("keypress", keyPress, this);
37084     },
37085     
37086     validateValue : function(value)
37087     {
37088         
37089         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37090             return false;
37091         }
37092         
37093         var num = this.parseValue(value);
37094         
37095         if(isNaN(num)){
37096             this.markInvalid(String.format(this.nanText, value));
37097             return false;
37098         }
37099         
37100         if(num < this.minValue){
37101             this.markInvalid(String.format(this.minText, this.minValue));
37102             return false;
37103         }
37104         
37105         if(num > this.maxValue){
37106             this.markInvalid(String.format(this.maxText, this.maxValue));
37107             return false;
37108         }
37109         
37110         return true;
37111     },
37112
37113     getValue : function()
37114     {
37115         var v = this.hiddenEl().getValue();
37116         
37117         return this.fixPrecision(this.parseValue(v));
37118     },
37119
37120     parseValue : function(value)
37121     {
37122         if(this.thousandsDelimiter) {
37123             value += "";
37124             r = new RegExp(",", "g");
37125             value = value.replace(r, "");
37126         }
37127         
37128         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37129         return isNaN(value) ? '' : value;
37130     },
37131
37132     fixPrecision : function(value)
37133     {
37134         if(this.thousandsDelimiter) {
37135             value += "";
37136             r = new RegExp(",", "g");
37137             value = value.replace(r, "");
37138         }
37139         
37140         var nan = isNaN(value);
37141         
37142         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37143             return nan ? '' : value;
37144         }
37145         return parseFloat(value).toFixed(this.decimalPrecision);
37146     },
37147
37148     setValue : function(v)
37149     {
37150         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37151         
37152         this.value = v;
37153         
37154         if(this.rendered){
37155             
37156             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37157             
37158             this.inputEl().dom.value = (v == '') ? '' :
37159                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37160             
37161             if(!this.allowZero && v === '0') {
37162                 this.hiddenEl().dom.value = '';
37163                 this.inputEl().dom.value = '';
37164             }
37165             
37166             this.validate();
37167         }
37168     },
37169
37170     decimalPrecisionFcn : function(v)
37171     {
37172         return Math.floor(v);
37173     },
37174
37175     beforeBlur : function()
37176     {
37177         var v = this.parseValue(this.getRawValue());
37178         
37179         if(v || v === 0 || v === ''){
37180             this.setValue(v);
37181         }
37182     },
37183     
37184     hiddenEl : function()
37185     {
37186         return this.el.select('input.hidden-number-input',true).first();
37187     }
37188     
37189 });
37190
37191  
37192
37193 /*
37194 * Licence: LGPL
37195 */
37196
37197 /**
37198  * @class Roo.bootstrap.DocumentSlider
37199  * @extends Roo.bootstrap.Component
37200  * Bootstrap DocumentSlider class
37201  * 
37202  * @constructor
37203  * Create a new DocumentViewer
37204  * @param {Object} config The config object
37205  */
37206
37207 Roo.bootstrap.DocumentSlider = function(config){
37208     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37209     
37210     this.files = [];
37211     
37212     this.addEvents({
37213         /**
37214          * @event initial
37215          * Fire after initEvent
37216          * @param {Roo.bootstrap.DocumentSlider} this
37217          */
37218         "initial" : true,
37219         /**
37220          * @event update
37221          * Fire after update
37222          * @param {Roo.bootstrap.DocumentSlider} this
37223          */
37224         "update" : true,
37225         /**
37226          * @event click
37227          * Fire after click
37228          * @param {Roo.bootstrap.DocumentSlider} this
37229          */
37230         "click" : true
37231     });
37232 };
37233
37234 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37235     
37236     files : false,
37237     
37238     indicator : 0,
37239     
37240     getAutoCreate : function()
37241     {
37242         var cfg = {
37243             tag : 'div',
37244             cls : 'roo-document-slider',
37245             cn : [
37246                 {
37247                     tag : 'div',
37248                     cls : 'roo-document-slider-header',
37249                     cn : [
37250                         {
37251                             tag : 'div',
37252                             cls : 'roo-document-slider-header-title'
37253                         }
37254                     ]
37255                 },
37256                 {
37257                     tag : 'div',
37258                     cls : 'roo-document-slider-body',
37259                     cn : [
37260                         {
37261                             tag : 'div',
37262                             cls : 'roo-document-slider-prev',
37263                             cn : [
37264                                 {
37265                                     tag : 'i',
37266                                     cls : 'fa fa-chevron-left'
37267                                 }
37268                             ]
37269                         },
37270                         {
37271                             tag : 'div',
37272                             cls : 'roo-document-slider-thumb',
37273                             cn : [
37274                                 {
37275                                     tag : 'img',
37276                                     cls : 'roo-document-slider-image'
37277                                 }
37278                             ]
37279                         },
37280                         {
37281                             tag : 'div',
37282                             cls : 'roo-document-slider-next',
37283                             cn : [
37284                                 {
37285                                     tag : 'i',
37286                                     cls : 'fa fa-chevron-right'
37287                                 }
37288                             ]
37289                         }
37290                     ]
37291                 }
37292             ]
37293         };
37294         
37295         return cfg;
37296     },
37297     
37298     initEvents : function()
37299     {
37300         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37301         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37302         
37303         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37304         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37305         
37306         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37307         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37308         
37309         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37310         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37311         
37312         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37313         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37314         
37315         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37316         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37317         
37318         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37319         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37320         
37321         this.thumbEl.on('click', this.onClick, this);
37322         
37323         this.prevIndicator.on('click', this.prev, this);
37324         
37325         this.nextIndicator.on('click', this.next, this);
37326         
37327     },
37328     
37329     initial : function()
37330     {
37331         if(this.files.length){
37332             this.indicator = 1;
37333             this.update()
37334         }
37335         
37336         this.fireEvent('initial', this);
37337     },
37338     
37339     update : function()
37340     {
37341         this.imageEl.attr('src', this.files[this.indicator - 1]);
37342         
37343         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37344         
37345         this.prevIndicator.show();
37346         
37347         if(this.indicator == 1){
37348             this.prevIndicator.hide();
37349         }
37350         
37351         this.nextIndicator.show();
37352         
37353         if(this.indicator == this.files.length){
37354             this.nextIndicator.hide();
37355         }
37356         
37357         this.thumbEl.scrollTo('top');
37358         
37359         this.fireEvent('update', this);
37360     },
37361     
37362     onClick : function(e)
37363     {
37364         e.preventDefault();
37365         
37366         this.fireEvent('click', this);
37367     },
37368     
37369     prev : function(e)
37370     {
37371         e.preventDefault();
37372         
37373         this.indicator = Math.max(1, this.indicator - 1);
37374         
37375         this.update();
37376     },
37377     
37378     next : function(e)
37379     {
37380         e.preventDefault();
37381         
37382         this.indicator = Math.min(this.files.length, this.indicator + 1);
37383         
37384         this.update();
37385     }
37386 });
37387 /*
37388  * - LGPL
37389  *
37390  * RadioSet
37391  *
37392  *
37393  */
37394
37395 /**
37396  * @class Roo.bootstrap.RadioSet
37397  * @extends Roo.bootstrap.Input
37398  * Bootstrap RadioSet class
37399  * @cfg {String} indicatorpos (left|right) default left
37400  * @cfg {Boolean} inline (true|false) inline the element (default true)
37401  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37402  * @constructor
37403  * Create a new RadioSet
37404  * @param {Object} config The config object
37405  */
37406
37407 Roo.bootstrap.RadioSet = function(config){
37408     
37409     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37410     
37411     this.radioes = [];
37412     
37413     Roo.bootstrap.RadioSet.register(this);
37414     
37415     this.addEvents({
37416         /**
37417         * @event check
37418         * Fires when the element is checked or unchecked.
37419         * @param {Roo.bootstrap.RadioSet} this This radio
37420         * @param {Roo.bootstrap.Radio} item The checked item
37421         */
37422        check : true,
37423        /**
37424         * @event click
37425         * Fires when the element is click.
37426         * @param {Roo.bootstrap.RadioSet} this This radio set
37427         * @param {Roo.bootstrap.Radio} item The checked item
37428         * @param {Roo.EventObject} e The event object
37429         */
37430        click : true
37431     });
37432     
37433 };
37434
37435 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37436
37437     radioes : false,
37438     
37439     inline : true,
37440     
37441     weight : '',
37442     
37443     indicatorpos : 'left',
37444     
37445     getAutoCreate : function()
37446     {
37447         var label = {
37448             tag : 'label',
37449             cls : 'roo-radio-set-label',
37450             cn : [
37451                 {
37452                     tag : 'span',
37453                     html : this.fieldLabel
37454                 }
37455             ]
37456         };
37457         if (Roo.bootstrap.version == 3) {
37458             
37459             
37460             if(this.indicatorpos == 'left'){
37461                 label.cn.unshift({
37462                     tag : 'i',
37463                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37464                     tooltip : 'This field is required'
37465                 });
37466             } else {
37467                 label.cn.push({
37468                     tag : 'i',
37469                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37470                     tooltip : 'This field is required'
37471                 });
37472             }
37473         }
37474         var items = {
37475             tag : 'div',
37476             cls : 'roo-radio-set-items'
37477         };
37478         
37479         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37480         
37481         if (align === 'left' && this.fieldLabel.length) {
37482             
37483             items = {
37484                 cls : "roo-radio-set-right", 
37485                 cn: [
37486                     items
37487                 ]
37488             };
37489             
37490             if(this.labelWidth > 12){
37491                 label.style = "width: " + this.labelWidth + 'px';
37492             }
37493             
37494             if(this.labelWidth < 13 && this.labelmd == 0){
37495                 this.labelmd = this.labelWidth;
37496             }
37497             
37498             if(this.labellg > 0){
37499                 label.cls += ' col-lg-' + this.labellg;
37500                 items.cls += ' col-lg-' + (12 - this.labellg);
37501             }
37502             
37503             if(this.labelmd > 0){
37504                 label.cls += ' col-md-' + this.labelmd;
37505                 items.cls += ' col-md-' + (12 - this.labelmd);
37506             }
37507             
37508             if(this.labelsm > 0){
37509                 label.cls += ' col-sm-' + this.labelsm;
37510                 items.cls += ' col-sm-' + (12 - this.labelsm);
37511             }
37512             
37513             if(this.labelxs > 0){
37514                 label.cls += ' col-xs-' + this.labelxs;
37515                 items.cls += ' col-xs-' + (12 - this.labelxs);
37516             }
37517         }
37518         
37519         var cfg = {
37520             tag : 'div',
37521             cls : 'roo-radio-set',
37522             cn : [
37523                 {
37524                     tag : 'input',
37525                     cls : 'roo-radio-set-input',
37526                     type : 'hidden',
37527                     name : this.name,
37528                     value : this.value ? this.value :  ''
37529                 },
37530                 label,
37531                 items
37532             ]
37533         };
37534         
37535         if(this.weight.length){
37536             cfg.cls += ' roo-radio-' + this.weight;
37537         }
37538         
37539         if(this.inline) {
37540             cfg.cls += ' roo-radio-set-inline';
37541         }
37542         
37543         var settings=this;
37544         ['xs','sm','md','lg'].map(function(size){
37545             if (settings[size]) {
37546                 cfg.cls += ' col-' + size + '-' + settings[size];
37547             }
37548         });
37549         
37550         return cfg;
37551         
37552     },
37553
37554     initEvents : function()
37555     {
37556         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37557         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37558         
37559         if(!this.fieldLabel.length){
37560             this.labelEl.hide();
37561         }
37562         
37563         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37564         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37565         
37566         this.indicator = this.indicatorEl();
37567         
37568         if(this.indicator){
37569             this.indicator.addClass('invisible');
37570         }
37571         
37572         this.originalValue = this.getValue();
37573         
37574     },
37575     
37576     inputEl: function ()
37577     {
37578         return this.el.select('.roo-radio-set-input', true).first();
37579     },
37580     
37581     getChildContainer : function()
37582     {
37583         return this.itemsEl;
37584     },
37585     
37586     register : function(item)
37587     {
37588         this.radioes.push(item);
37589         
37590     },
37591     
37592     validate : function()
37593     {   
37594         if(this.getVisibilityEl().hasClass('hidden')){
37595             return true;
37596         }
37597         
37598         var valid = false;
37599         
37600         Roo.each(this.radioes, function(i){
37601             if(!i.checked){
37602                 return;
37603             }
37604             
37605             valid = true;
37606             return false;
37607         });
37608         
37609         if(this.allowBlank) {
37610             return true;
37611         }
37612         
37613         if(this.disabled || valid){
37614             this.markValid();
37615             return true;
37616         }
37617         
37618         this.markInvalid();
37619         return false;
37620         
37621     },
37622     
37623     markValid : function()
37624     {
37625         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37626             this.indicatorEl().removeClass('visible');
37627             this.indicatorEl().addClass('invisible');
37628         }
37629         
37630         
37631         if (Roo.bootstrap.version == 3) {
37632             this.el.removeClass([this.invalidClass, this.validClass]);
37633             this.el.addClass(this.validClass);
37634         } else {
37635             this.el.removeClass(['is-invalid','is-valid']);
37636             this.el.addClass(['is-valid']);
37637         }
37638         this.fireEvent('valid', this);
37639     },
37640     
37641     markInvalid : function(msg)
37642     {
37643         if(this.allowBlank || this.disabled){
37644             return;
37645         }
37646         
37647         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37648             this.indicatorEl().removeClass('invisible');
37649             this.indicatorEl().addClass('visible');
37650         }
37651         if (Roo.bootstrap.version == 3) {
37652             this.el.removeClass([this.invalidClass, this.validClass]);
37653             this.el.addClass(this.invalidClass);
37654         } else {
37655             this.el.removeClass(['is-invalid','is-valid']);
37656             this.el.addClass(['is-invalid']);
37657         }
37658         
37659         this.fireEvent('invalid', this, msg);
37660         
37661     },
37662     
37663     setValue : function(v, suppressEvent)
37664     {   
37665         if(this.value === v){
37666             return;
37667         }
37668         
37669         this.value = v;
37670         
37671         if(this.rendered){
37672             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37673         }
37674         
37675         Roo.each(this.radioes, function(i){
37676             i.checked = false;
37677             i.el.removeClass('checked');
37678         });
37679         
37680         Roo.each(this.radioes, function(i){
37681             
37682             if(i.value === v || i.value.toString() === v.toString()){
37683                 i.checked = true;
37684                 i.el.addClass('checked');
37685                 
37686                 if(suppressEvent !== true){
37687                     this.fireEvent('check', this, i);
37688                 }
37689                 
37690                 return false;
37691             }
37692             
37693         }, this);
37694         
37695         this.validate();
37696     },
37697     
37698     clearInvalid : function(){
37699         
37700         if(!this.el || this.preventMark){
37701             return;
37702         }
37703         
37704         this.el.removeClass([this.invalidClass]);
37705         
37706         this.fireEvent('valid', this);
37707     }
37708     
37709 });
37710
37711 Roo.apply(Roo.bootstrap.RadioSet, {
37712     
37713     groups: {},
37714     
37715     register : function(set)
37716     {
37717         this.groups[set.name] = set;
37718     },
37719     
37720     get: function(name) 
37721     {
37722         if (typeof(this.groups[name]) == 'undefined') {
37723             return false;
37724         }
37725         
37726         return this.groups[name] ;
37727     }
37728     
37729 });
37730 /*
37731  * Based on:
37732  * Ext JS Library 1.1.1
37733  * Copyright(c) 2006-2007, Ext JS, LLC.
37734  *
37735  * Originally Released Under LGPL - original licence link has changed is not relivant.
37736  *
37737  * Fork - LGPL
37738  * <script type="text/javascript">
37739  */
37740
37741
37742 /**
37743  * @class Roo.bootstrap.SplitBar
37744  * @extends Roo.util.Observable
37745  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37746  * <br><br>
37747  * Usage:
37748  * <pre><code>
37749 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37750                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37751 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37752 split.minSize = 100;
37753 split.maxSize = 600;
37754 split.animate = true;
37755 split.on('moved', splitterMoved);
37756 </code></pre>
37757  * @constructor
37758  * Create a new SplitBar
37759  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37760  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37761  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37762  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37763                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37764                         position of the SplitBar).
37765  */
37766 Roo.bootstrap.SplitBar = function(cfg){
37767     
37768     /** @private */
37769     
37770     //{
37771     //  dragElement : elm
37772     //  resizingElement: el,
37773         // optional..
37774     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37775     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37776         // existingProxy ???
37777     //}
37778     
37779     this.el = Roo.get(cfg.dragElement, true);
37780     this.el.dom.unselectable = "on";
37781     /** @private */
37782     this.resizingEl = Roo.get(cfg.resizingElement, true);
37783
37784     /**
37785      * @private
37786      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37787      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37788      * @type Number
37789      */
37790     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37791     
37792     /**
37793      * The minimum size of the resizing element. (Defaults to 0)
37794      * @type Number
37795      */
37796     this.minSize = 0;
37797     
37798     /**
37799      * The maximum size of the resizing element. (Defaults to 2000)
37800      * @type Number
37801      */
37802     this.maxSize = 2000;
37803     
37804     /**
37805      * Whether to animate the transition to the new size
37806      * @type Boolean
37807      */
37808     this.animate = false;
37809     
37810     /**
37811      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37812      * @type Boolean
37813      */
37814     this.useShim = false;
37815     
37816     /** @private */
37817     this.shim = null;
37818     
37819     if(!cfg.existingProxy){
37820         /** @private */
37821         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37822     }else{
37823         this.proxy = Roo.get(cfg.existingProxy).dom;
37824     }
37825     /** @private */
37826     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37827     
37828     /** @private */
37829     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37830     
37831     /** @private */
37832     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37833     
37834     /** @private */
37835     this.dragSpecs = {};
37836     
37837     /**
37838      * @private The adapter to use to positon and resize elements
37839      */
37840     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37841     this.adapter.init(this);
37842     
37843     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37844         /** @private */
37845         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37846         this.el.addClass("roo-splitbar-h");
37847     }else{
37848         /** @private */
37849         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37850         this.el.addClass("roo-splitbar-v");
37851     }
37852     
37853     this.addEvents({
37854         /**
37855          * @event resize
37856          * Fires when the splitter is moved (alias for {@link #event-moved})
37857          * @param {Roo.bootstrap.SplitBar} this
37858          * @param {Number} newSize the new width or height
37859          */
37860         "resize" : true,
37861         /**
37862          * @event moved
37863          * Fires when the splitter is moved
37864          * @param {Roo.bootstrap.SplitBar} this
37865          * @param {Number} newSize the new width or height
37866          */
37867         "moved" : true,
37868         /**
37869          * @event beforeresize
37870          * Fires before the splitter is dragged
37871          * @param {Roo.bootstrap.SplitBar} this
37872          */
37873         "beforeresize" : true,
37874
37875         "beforeapply" : true
37876     });
37877
37878     Roo.util.Observable.call(this);
37879 };
37880
37881 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37882     onStartProxyDrag : function(x, y){
37883         this.fireEvent("beforeresize", this);
37884         if(!this.overlay){
37885             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37886             o.unselectable();
37887             o.enableDisplayMode("block");
37888             // all splitbars share the same overlay
37889             Roo.bootstrap.SplitBar.prototype.overlay = o;
37890         }
37891         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37892         this.overlay.show();
37893         Roo.get(this.proxy).setDisplayed("block");
37894         var size = this.adapter.getElementSize(this);
37895         this.activeMinSize = this.getMinimumSize();;
37896         this.activeMaxSize = this.getMaximumSize();;
37897         var c1 = size - this.activeMinSize;
37898         var c2 = Math.max(this.activeMaxSize - size, 0);
37899         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37900             this.dd.resetConstraints();
37901             this.dd.setXConstraint(
37902                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37903                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37904             );
37905             this.dd.setYConstraint(0, 0);
37906         }else{
37907             this.dd.resetConstraints();
37908             this.dd.setXConstraint(0, 0);
37909             this.dd.setYConstraint(
37910                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37911                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37912             );
37913          }
37914         this.dragSpecs.startSize = size;
37915         this.dragSpecs.startPoint = [x, y];
37916         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37917     },
37918     
37919     /** 
37920      * @private Called after the drag operation by the DDProxy
37921      */
37922     onEndProxyDrag : function(e){
37923         Roo.get(this.proxy).setDisplayed(false);
37924         var endPoint = Roo.lib.Event.getXY(e);
37925         if(this.overlay){
37926             this.overlay.hide();
37927         }
37928         var newSize;
37929         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37930             newSize = this.dragSpecs.startSize + 
37931                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37932                     endPoint[0] - this.dragSpecs.startPoint[0] :
37933                     this.dragSpecs.startPoint[0] - endPoint[0]
37934                 );
37935         }else{
37936             newSize = this.dragSpecs.startSize + 
37937                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37938                     endPoint[1] - this.dragSpecs.startPoint[1] :
37939                     this.dragSpecs.startPoint[1] - endPoint[1]
37940                 );
37941         }
37942         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37943         if(newSize != this.dragSpecs.startSize){
37944             if(this.fireEvent('beforeapply', this, newSize) !== false){
37945                 this.adapter.setElementSize(this, newSize);
37946                 this.fireEvent("moved", this, newSize);
37947                 this.fireEvent("resize", this, newSize);
37948             }
37949         }
37950     },
37951     
37952     /**
37953      * Get the adapter this SplitBar uses
37954      * @return The adapter object
37955      */
37956     getAdapter : function(){
37957         return this.adapter;
37958     },
37959     
37960     /**
37961      * Set the adapter this SplitBar uses
37962      * @param {Object} adapter A SplitBar adapter object
37963      */
37964     setAdapter : function(adapter){
37965         this.adapter = adapter;
37966         this.adapter.init(this);
37967     },
37968     
37969     /**
37970      * Gets the minimum size for the resizing element
37971      * @return {Number} The minimum size
37972      */
37973     getMinimumSize : function(){
37974         return this.minSize;
37975     },
37976     
37977     /**
37978      * Sets the minimum size for the resizing element
37979      * @param {Number} minSize The minimum size
37980      */
37981     setMinimumSize : function(minSize){
37982         this.minSize = minSize;
37983     },
37984     
37985     /**
37986      * Gets the maximum size for the resizing element
37987      * @return {Number} The maximum size
37988      */
37989     getMaximumSize : function(){
37990         return this.maxSize;
37991     },
37992     
37993     /**
37994      * Sets the maximum size for the resizing element
37995      * @param {Number} maxSize The maximum size
37996      */
37997     setMaximumSize : function(maxSize){
37998         this.maxSize = maxSize;
37999     },
38000     
38001     /**
38002      * Sets the initialize size for the resizing element
38003      * @param {Number} size The initial size
38004      */
38005     setCurrentSize : function(size){
38006         var oldAnimate = this.animate;
38007         this.animate = false;
38008         this.adapter.setElementSize(this, size);
38009         this.animate = oldAnimate;
38010     },
38011     
38012     /**
38013      * Destroy this splitbar. 
38014      * @param {Boolean} removeEl True to remove the element
38015      */
38016     destroy : function(removeEl){
38017         if(this.shim){
38018             this.shim.remove();
38019         }
38020         this.dd.unreg();
38021         this.proxy.parentNode.removeChild(this.proxy);
38022         if(removeEl){
38023             this.el.remove();
38024         }
38025     }
38026 });
38027
38028 /**
38029  * @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.
38030  */
38031 Roo.bootstrap.SplitBar.createProxy = function(dir){
38032     var proxy = new Roo.Element(document.createElement("div"));
38033     proxy.unselectable();
38034     var cls = 'roo-splitbar-proxy';
38035     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38036     document.body.appendChild(proxy.dom);
38037     return proxy.dom;
38038 };
38039
38040 /** 
38041  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38042  * Default Adapter. It assumes the splitter and resizing element are not positioned
38043  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38044  */
38045 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38046 };
38047
38048 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38049     // do nothing for now
38050     init : function(s){
38051     
38052     },
38053     /**
38054      * Called before drag operations to get the current size of the resizing element. 
38055      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38056      */
38057      getElementSize : function(s){
38058         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38059             return s.resizingEl.getWidth();
38060         }else{
38061             return s.resizingEl.getHeight();
38062         }
38063     },
38064     
38065     /**
38066      * Called after drag operations to set the size of the resizing element.
38067      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38068      * @param {Number} newSize The new size to set
38069      * @param {Function} onComplete A function to be invoked when resizing is complete
38070      */
38071     setElementSize : function(s, newSize, onComplete){
38072         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38073             if(!s.animate){
38074                 s.resizingEl.setWidth(newSize);
38075                 if(onComplete){
38076                     onComplete(s, newSize);
38077                 }
38078             }else{
38079                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38080             }
38081         }else{
38082             
38083             if(!s.animate){
38084                 s.resizingEl.setHeight(newSize);
38085                 if(onComplete){
38086                     onComplete(s, newSize);
38087                 }
38088             }else{
38089                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38090             }
38091         }
38092     }
38093 };
38094
38095 /** 
38096  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38097  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38098  * Adapter that  moves the splitter element to align with the resized sizing element. 
38099  * Used with an absolute positioned SplitBar.
38100  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38101  * document.body, make sure you assign an id to the body element.
38102  */
38103 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38104     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38105     this.container = Roo.get(container);
38106 };
38107
38108 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38109     init : function(s){
38110         this.basic.init(s);
38111     },
38112     
38113     getElementSize : function(s){
38114         return this.basic.getElementSize(s);
38115     },
38116     
38117     setElementSize : function(s, newSize, onComplete){
38118         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38119     },
38120     
38121     moveSplitter : function(s){
38122         var yes = Roo.bootstrap.SplitBar;
38123         switch(s.placement){
38124             case yes.LEFT:
38125                 s.el.setX(s.resizingEl.getRight());
38126                 break;
38127             case yes.RIGHT:
38128                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38129                 break;
38130             case yes.TOP:
38131                 s.el.setY(s.resizingEl.getBottom());
38132                 break;
38133             case yes.BOTTOM:
38134                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38135                 break;
38136         }
38137     }
38138 };
38139
38140 /**
38141  * Orientation constant - Create a vertical SplitBar
38142  * @static
38143  * @type Number
38144  */
38145 Roo.bootstrap.SplitBar.VERTICAL = 1;
38146
38147 /**
38148  * Orientation constant - Create a horizontal SplitBar
38149  * @static
38150  * @type Number
38151  */
38152 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38153
38154 /**
38155  * Placement constant - The resizing element is to the left of the splitter element
38156  * @static
38157  * @type Number
38158  */
38159 Roo.bootstrap.SplitBar.LEFT = 1;
38160
38161 /**
38162  * Placement constant - The resizing element is to the right of the splitter element
38163  * @static
38164  * @type Number
38165  */
38166 Roo.bootstrap.SplitBar.RIGHT = 2;
38167
38168 /**
38169  * Placement constant - The resizing element is positioned above the splitter element
38170  * @static
38171  * @type Number
38172  */
38173 Roo.bootstrap.SplitBar.TOP = 3;
38174
38175 /**
38176  * Placement constant - The resizing element is positioned under splitter element
38177  * @static
38178  * @type Number
38179  */
38180 Roo.bootstrap.SplitBar.BOTTOM = 4;
38181 Roo.namespace("Roo.bootstrap.layout");/*
38182  * Based on:
38183  * Ext JS Library 1.1.1
38184  * Copyright(c) 2006-2007, Ext JS, LLC.
38185  *
38186  * Originally Released Under LGPL - original licence link has changed is not relivant.
38187  *
38188  * Fork - LGPL
38189  * <script type="text/javascript">
38190  */
38191
38192 /**
38193  * @class Roo.bootstrap.layout.Manager
38194  * @extends Roo.bootstrap.Component
38195  * Base class for layout managers.
38196  */
38197 Roo.bootstrap.layout.Manager = function(config)
38198 {
38199     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38200
38201
38202
38203
38204
38205     /** false to disable window resize monitoring @type Boolean */
38206     this.monitorWindowResize = true;
38207     this.regions = {};
38208     this.addEvents({
38209         /**
38210          * @event layout
38211          * Fires when a layout is performed.
38212          * @param {Roo.LayoutManager} this
38213          */
38214         "layout" : true,
38215         /**
38216          * @event regionresized
38217          * Fires when the user resizes a region.
38218          * @param {Roo.LayoutRegion} region The resized region
38219          * @param {Number} newSize The new size (width for east/west, height for north/south)
38220          */
38221         "regionresized" : true,
38222         /**
38223          * @event regioncollapsed
38224          * Fires when a region is collapsed.
38225          * @param {Roo.LayoutRegion} region The collapsed region
38226          */
38227         "regioncollapsed" : true,
38228         /**
38229          * @event regionexpanded
38230          * Fires when a region is expanded.
38231          * @param {Roo.LayoutRegion} region The expanded region
38232          */
38233         "regionexpanded" : true
38234     });
38235     this.updating = false;
38236
38237     if (config.el) {
38238         this.el = Roo.get(config.el);
38239         this.initEvents();
38240     }
38241
38242 };
38243
38244 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38245
38246
38247     regions : null,
38248
38249     monitorWindowResize : true,
38250
38251
38252     updating : false,
38253
38254
38255     onRender : function(ct, position)
38256     {
38257         if(!this.el){
38258             this.el = Roo.get(ct);
38259             this.initEvents();
38260         }
38261         //this.fireEvent('render',this);
38262     },
38263
38264
38265     initEvents: function()
38266     {
38267
38268
38269         // ie scrollbar fix
38270         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38271             document.body.scroll = "no";
38272         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38273             this.el.position('relative');
38274         }
38275         this.id = this.el.id;
38276         this.el.addClass("roo-layout-container");
38277         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38278         if(this.el.dom != document.body ) {
38279             this.el.on('resize', this.layout,this);
38280             this.el.on('show', this.layout,this);
38281         }
38282
38283     },
38284
38285     /**
38286      * Returns true if this layout is currently being updated
38287      * @return {Boolean}
38288      */
38289     isUpdating : function(){
38290         return this.updating;
38291     },
38292
38293     /**
38294      * Suspend the LayoutManager from doing auto-layouts while
38295      * making multiple add or remove calls
38296      */
38297     beginUpdate : function(){
38298         this.updating = true;
38299     },
38300
38301     /**
38302      * Restore auto-layouts and optionally disable the manager from performing a layout
38303      * @param {Boolean} noLayout true to disable a layout update
38304      */
38305     endUpdate : function(noLayout){
38306         this.updating = false;
38307         if(!noLayout){
38308             this.layout();
38309         }
38310     },
38311
38312     layout: function(){
38313         // abstract...
38314     },
38315
38316     onRegionResized : function(region, newSize){
38317         this.fireEvent("regionresized", region, newSize);
38318         this.layout();
38319     },
38320
38321     onRegionCollapsed : function(region){
38322         this.fireEvent("regioncollapsed", region);
38323     },
38324
38325     onRegionExpanded : function(region){
38326         this.fireEvent("regionexpanded", region);
38327     },
38328
38329     /**
38330      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38331      * performs box-model adjustments.
38332      * @return {Object} The size as an object {width: (the width), height: (the height)}
38333      */
38334     getViewSize : function()
38335     {
38336         var size;
38337         if(this.el.dom != document.body){
38338             size = this.el.getSize();
38339         }else{
38340             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38341         }
38342         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38343         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38344         return size;
38345     },
38346
38347     /**
38348      * Returns the Element this layout is bound to.
38349      * @return {Roo.Element}
38350      */
38351     getEl : function(){
38352         return this.el;
38353     },
38354
38355     /**
38356      * Returns the specified region.
38357      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38358      * @return {Roo.LayoutRegion}
38359      */
38360     getRegion : function(target){
38361         return this.regions[target.toLowerCase()];
38362     },
38363
38364     onWindowResize : function(){
38365         if(this.monitorWindowResize){
38366             this.layout();
38367         }
38368     }
38369 });
38370 /*
38371  * Based on:
38372  * Ext JS Library 1.1.1
38373  * Copyright(c) 2006-2007, Ext JS, LLC.
38374  *
38375  * Originally Released Under LGPL - original licence link has changed is not relivant.
38376  *
38377  * Fork - LGPL
38378  * <script type="text/javascript">
38379  */
38380 /**
38381  * @class Roo.bootstrap.layout.Border
38382  * @extends Roo.bootstrap.layout.Manager
38383  * @builder-top
38384  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38385  * please see: examples/bootstrap/nested.html<br><br>
38386  
38387 <b>The container the layout is rendered into can be either the body element or any other element.
38388 If it is not the body element, the container needs to either be an absolute positioned element,
38389 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38390 the container size if it is not the body element.</b>
38391
38392 * @constructor
38393 * Create a new Border
38394 * @param {Object} config Configuration options
38395  */
38396 Roo.bootstrap.layout.Border = function(config){
38397     config = config || {};
38398     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38399     
38400     
38401     
38402     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38403         if(config[region]){
38404             config[region].region = region;
38405             this.addRegion(config[region]);
38406         }
38407     },this);
38408     
38409 };
38410
38411 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38412
38413 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38414     
38415     parent : false, // this might point to a 'nest' or a ???
38416     
38417     /**
38418      * Creates and adds a new region if it doesn't already exist.
38419      * @param {String} target The target region key (north, south, east, west or center).
38420      * @param {Object} config The regions config object
38421      * @return {BorderLayoutRegion} The new region
38422      */
38423     addRegion : function(config)
38424     {
38425         if(!this.regions[config.region]){
38426             var r = this.factory(config);
38427             this.bindRegion(r);
38428         }
38429         return this.regions[config.region];
38430     },
38431
38432     // private (kinda)
38433     bindRegion : function(r){
38434         this.regions[r.config.region] = r;
38435         
38436         r.on("visibilitychange",    this.layout, this);
38437         r.on("paneladded",          this.layout, this);
38438         r.on("panelremoved",        this.layout, this);
38439         r.on("invalidated",         this.layout, this);
38440         r.on("resized",             this.onRegionResized, this);
38441         r.on("collapsed",           this.onRegionCollapsed, this);
38442         r.on("expanded",            this.onRegionExpanded, this);
38443     },
38444
38445     /**
38446      * Performs a layout update.
38447      */
38448     layout : function()
38449     {
38450         if(this.updating) {
38451             return;
38452         }
38453         
38454         // render all the rebions if they have not been done alreayd?
38455         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38456             if(this.regions[region] && !this.regions[region].bodyEl){
38457                 this.regions[region].onRender(this.el)
38458             }
38459         },this);
38460         
38461         var size = this.getViewSize();
38462         var w = size.width;
38463         var h = size.height;
38464         var centerW = w;
38465         var centerH = h;
38466         var centerY = 0;
38467         var centerX = 0;
38468         //var x = 0, y = 0;
38469
38470         var rs = this.regions;
38471         var north = rs["north"];
38472         var south = rs["south"]; 
38473         var west = rs["west"];
38474         var east = rs["east"];
38475         var center = rs["center"];
38476         //if(this.hideOnLayout){ // not supported anymore
38477             //c.el.setStyle("display", "none");
38478         //}
38479         if(north && north.isVisible()){
38480             var b = north.getBox();
38481             var m = north.getMargins();
38482             b.width = w - (m.left+m.right);
38483             b.x = m.left;
38484             b.y = m.top;
38485             centerY = b.height + b.y + m.bottom;
38486             centerH -= centerY;
38487             north.updateBox(this.safeBox(b));
38488         }
38489         if(south && south.isVisible()){
38490             var b = south.getBox();
38491             var m = south.getMargins();
38492             b.width = w - (m.left+m.right);
38493             b.x = m.left;
38494             var totalHeight = (b.height + m.top + m.bottom);
38495             b.y = h - totalHeight + m.top;
38496             centerH -= totalHeight;
38497             south.updateBox(this.safeBox(b));
38498         }
38499         if(west && west.isVisible()){
38500             var b = west.getBox();
38501             var m = west.getMargins();
38502             b.height = centerH - (m.top+m.bottom);
38503             b.x = m.left;
38504             b.y = centerY + m.top;
38505             var totalWidth = (b.width + m.left + m.right);
38506             centerX += totalWidth;
38507             centerW -= totalWidth;
38508             west.updateBox(this.safeBox(b));
38509         }
38510         if(east && east.isVisible()){
38511             var b = east.getBox();
38512             var m = east.getMargins();
38513             b.height = centerH - (m.top+m.bottom);
38514             var totalWidth = (b.width + m.left + m.right);
38515             b.x = w - totalWidth + m.left;
38516             b.y = centerY + m.top;
38517             centerW -= totalWidth;
38518             east.updateBox(this.safeBox(b));
38519         }
38520         if(center){
38521             var m = center.getMargins();
38522             var centerBox = {
38523                 x: centerX + m.left,
38524                 y: centerY + m.top,
38525                 width: centerW - (m.left+m.right),
38526                 height: centerH - (m.top+m.bottom)
38527             };
38528             //if(this.hideOnLayout){
38529                 //center.el.setStyle("display", "block");
38530             //}
38531             center.updateBox(this.safeBox(centerBox));
38532         }
38533         this.el.repaint();
38534         this.fireEvent("layout", this);
38535     },
38536
38537     // private
38538     safeBox : function(box){
38539         box.width = Math.max(0, box.width);
38540         box.height = Math.max(0, box.height);
38541         return box;
38542     },
38543
38544     /**
38545      * Adds a ContentPanel (or subclass) to this layout.
38546      * @param {String} target The target region key (north, south, east, west or center).
38547      * @param {Roo.ContentPanel} panel The panel to add
38548      * @return {Roo.ContentPanel} The added panel
38549      */
38550     add : function(target, panel){
38551          
38552         target = target.toLowerCase();
38553         return this.regions[target].add(panel);
38554     },
38555
38556     /**
38557      * Remove a ContentPanel (or subclass) to this layout.
38558      * @param {String} target The target region key (north, south, east, west or center).
38559      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38560      * @return {Roo.ContentPanel} The removed panel
38561      */
38562     remove : function(target, panel){
38563         target = target.toLowerCase();
38564         return this.regions[target].remove(panel);
38565     },
38566
38567     /**
38568      * Searches all regions for a panel with the specified id
38569      * @param {String} panelId
38570      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38571      */
38572     findPanel : function(panelId){
38573         var rs = this.regions;
38574         for(var target in rs){
38575             if(typeof rs[target] != "function"){
38576                 var p = rs[target].getPanel(panelId);
38577                 if(p){
38578                     return p;
38579                 }
38580             }
38581         }
38582         return null;
38583     },
38584
38585     /**
38586      * Searches all regions for a panel with the specified id and activates (shows) it.
38587      * @param {String/ContentPanel} panelId The panels id or the panel itself
38588      * @return {Roo.ContentPanel} The shown panel or null
38589      */
38590     showPanel : function(panelId) {
38591       var rs = this.regions;
38592       for(var target in rs){
38593          var r = rs[target];
38594          if(typeof r != "function"){
38595             if(r.hasPanel(panelId)){
38596                return r.showPanel(panelId);
38597             }
38598          }
38599       }
38600       return null;
38601    },
38602
38603    /**
38604      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38605      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38606      */
38607    /*
38608     restoreState : function(provider){
38609         if(!provider){
38610             provider = Roo.state.Manager;
38611         }
38612         var sm = new Roo.LayoutStateManager();
38613         sm.init(this, provider);
38614     },
38615 */
38616  
38617  
38618     /**
38619      * Adds a xtype elements to the layout.
38620      * <pre><code>
38621
38622 layout.addxtype({
38623        xtype : 'ContentPanel',
38624        region: 'west',
38625        items: [ .... ]
38626    }
38627 );
38628
38629 layout.addxtype({
38630         xtype : 'NestedLayoutPanel',
38631         region: 'west',
38632         layout: {
38633            center: { },
38634            west: { }   
38635         },
38636         items : [ ... list of content panels or nested layout panels.. ]
38637    }
38638 );
38639 </code></pre>
38640      * @param {Object} cfg Xtype definition of item to add.
38641      */
38642     addxtype : function(cfg)
38643     {
38644         // basically accepts a pannel...
38645         // can accept a layout region..!?!?
38646         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38647         
38648         
38649         // theory?  children can only be panels??
38650         
38651         //if (!cfg.xtype.match(/Panel$/)) {
38652         //    return false;
38653         //}
38654         var ret = false;
38655         
38656         if (typeof(cfg.region) == 'undefined') {
38657             Roo.log("Failed to add Panel, region was not set");
38658             Roo.log(cfg);
38659             return false;
38660         }
38661         var region = cfg.region;
38662         delete cfg.region;
38663         
38664           
38665         var xitems = [];
38666         if (cfg.items) {
38667             xitems = cfg.items;
38668             delete cfg.items;
38669         }
38670         var nb = false;
38671         
38672         if ( region == 'center') {
38673             Roo.log("Center: " + cfg.title);
38674         }
38675         
38676         
38677         switch(cfg.xtype) 
38678         {
38679             case 'Content':  // ContentPanel (el, cfg)
38680             case 'Scroll':  // ContentPanel (el, cfg)
38681             case 'View': 
38682                 cfg.autoCreate = cfg.autoCreate || true;
38683                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38684                 //} else {
38685                 //    var el = this.el.createChild();
38686                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38687                 //}
38688                 
38689                 this.add(region, ret);
38690                 break;
38691             
38692             /*
38693             case 'TreePanel': // our new panel!
38694                 cfg.el = this.el.createChild();
38695                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38696                 this.add(region, ret);
38697                 break;
38698             */
38699             
38700             case 'Nest': 
38701                 // create a new Layout (which is  a Border Layout...
38702                 
38703                 var clayout = cfg.layout;
38704                 clayout.el  = this.el.createChild();
38705                 clayout.items   = clayout.items  || [];
38706                 
38707                 delete cfg.layout;
38708                 
38709                 // replace this exitems with the clayout ones..
38710                 xitems = clayout.items;
38711                  
38712                 // force background off if it's in center...
38713                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38714                     cfg.background = false;
38715                 }
38716                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38717                 
38718                 
38719                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38720                 //console.log('adding nested layout panel '  + cfg.toSource());
38721                 this.add(region, ret);
38722                 nb = {}; /// find first...
38723                 break;
38724             
38725             case 'Grid':
38726                 
38727                 // needs grid and region
38728                 
38729                 //var el = this.getRegion(region).el.createChild();
38730                 /*
38731                  *var el = this.el.createChild();
38732                 // create the grid first...
38733                 cfg.grid.container = el;
38734                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38735                 */
38736                 
38737                 if (region == 'center' && this.active ) {
38738                     cfg.background = false;
38739                 }
38740                 
38741                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38742                 
38743                 this.add(region, ret);
38744                 /*
38745                 if (cfg.background) {
38746                     // render grid on panel activation (if panel background)
38747                     ret.on('activate', function(gp) {
38748                         if (!gp.grid.rendered) {
38749                     //        gp.grid.render(el);
38750                         }
38751                     });
38752                 } else {
38753                   //  cfg.grid.render(el);
38754                 }
38755                 */
38756                 break;
38757            
38758            
38759             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38760                 // it was the old xcomponent building that caused this before.
38761                 // espeically if border is the top element in the tree.
38762                 ret = this;
38763                 break; 
38764                 
38765                     
38766                 
38767                 
38768                 
38769             default:
38770                 /*
38771                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38772                     
38773                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38774                     this.add(region, ret);
38775                 } else {
38776                 */
38777                     Roo.log(cfg);
38778                     throw "Can not add '" + cfg.xtype + "' to Border";
38779                     return null;
38780              
38781                                 
38782              
38783         }
38784         this.beginUpdate();
38785         // add children..
38786         var region = '';
38787         var abn = {};
38788         Roo.each(xitems, function(i)  {
38789             region = nb && i.region ? i.region : false;
38790             
38791             var add = ret.addxtype(i);
38792            
38793             if (region) {
38794                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38795                 if (!i.background) {
38796                     abn[region] = nb[region] ;
38797                 }
38798             }
38799             
38800         });
38801         this.endUpdate();
38802
38803         // make the last non-background panel active..
38804         //if (nb) { Roo.log(abn); }
38805         if (nb) {
38806             
38807             for(var r in abn) {
38808                 region = this.getRegion(r);
38809                 if (region) {
38810                     // tried using nb[r], but it does not work..
38811                      
38812                     region.showPanel(abn[r]);
38813                    
38814                 }
38815             }
38816         }
38817         return ret;
38818         
38819     },
38820     
38821     
38822 // private
38823     factory : function(cfg)
38824     {
38825         
38826         var validRegions = Roo.bootstrap.layout.Border.regions;
38827
38828         var target = cfg.region;
38829         cfg.mgr = this;
38830         
38831         var r = Roo.bootstrap.layout;
38832         Roo.log(target);
38833         switch(target){
38834             case "north":
38835                 return new r.North(cfg);
38836             case "south":
38837                 return new r.South(cfg);
38838             case "east":
38839                 return new r.East(cfg);
38840             case "west":
38841                 return new r.West(cfg);
38842             case "center":
38843                 return new r.Center(cfg);
38844         }
38845         throw 'Layout region "'+target+'" not supported.';
38846     }
38847     
38848     
38849 });
38850  /*
38851  * Based on:
38852  * Ext JS Library 1.1.1
38853  * Copyright(c) 2006-2007, Ext JS, LLC.
38854  *
38855  * Originally Released Under LGPL - original licence link has changed is not relivant.
38856  *
38857  * Fork - LGPL
38858  * <script type="text/javascript">
38859  */
38860  
38861 /**
38862  * @class Roo.bootstrap.layout.Basic
38863  * @extends Roo.util.Observable
38864  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38865  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38866  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38867  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38868  * @cfg {string}   region  the region that it inhabits..
38869  * @cfg {bool}   skipConfig skip config?
38870  * 
38871
38872  */
38873 Roo.bootstrap.layout.Basic = function(config){
38874     
38875     this.mgr = config.mgr;
38876     
38877     this.position = config.region;
38878     
38879     var skipConfig = config.skipConfig;
38880     
38881     this.events = {
38882         /**
38883          * @scope Roo.BasicLayoutRegion
38884          */
38885         
38886         /**
38887          * @event beforeremove
38888          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38889          * @param {Roo.LayoutRegion} this
38890          * @param {Roo.ContentPanel} panel The panel
38891          * @param {Object} e The cancel event object
38892          */
38893         "beforeremove" : true,
38894         /**
38895          * @event invalidated
38896          * Fires when the layout for this region is changed.
38897          * @param {Roo.LayoutRegion} this
38898          */
38899         "invalidated" : true,
38900         /**
38901          * @event visibilitychange
38902          * Fires when this region is shown or hidden 
38903          * @param {Roo.LayoutRegion} this
38904          * @param {Boolean} visibility true or false
38905          */
38906         "visibilitychange" : true,
38907         /**
38908          * @event paneladded
38909          * Fires when a panel is added. 
38910          * @param {Roo.LayoutRegion} this
38911          * @param {Roo.ContentPanel} panel The panel
38912          */
38913         "paneladded" : true,
38914         /**
38915          * @event panelremoved
38916          * Fires when a panel is removed. 
38917          * @param {Roo.LayoutRegion} this
38918          * @param {Roo.ContentPanel} panel The panel
38919          */
38920         "panelremoved" : true,
38921         /**
38922          * @event beforecollapse
38923          * Fires when this region before collapse.
38924          * @param {Roo.LayoutRegion} this
38925          */
38926         "beforecollapse" : true,
38927         /**
38928          * @event collapsed
38929          * Fires when this region is collapsed.
38930          * @param {Roo.LayoutRegion} this
38931          */
38932         "collapsed" : true,
38933         /**
38934          * @event expanded
38935          * Fires when this region is expanded.
38936          * @param {Roo.LayoutRegion} this
38937          */
38938         "expanded" : true,
38939         /**
38940          * @event slideshow
38941          * Fires when this region is slid into view.
38942          * @param {Roo.LayoutRegion} this
38943          */
38944         "slideshow" : true,
38945         /**
38946          * @event slidehide
38947          * Fires when this region slides out of view. 
38948          * @param {Roo.LayoutRegion} this
38949          */
38950         "slidehide" : true,
38951         /**
38952          * @event panelactivated
38953          * Fires when a panel is activated. 
38954          * @param {Roo.LayoutRegion} this
38955          * @param {Roo.ContentPanel} panel The activated panel
38956          */
38957         "panelactivated" : true,
38958         /**
38959          * @event resized
38960          * Fires when the user resizes this region. 
38961          * @param {Roo.LayoutRegion} this
38962          * @param {Number} newSize The new size (width for east/west, height for north/south)
38963          */
38964         "resized" : true
38965     };
38966     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38967     this.panels = new Roo.util.MixedCollection();
38968     this.panels.getKey = this.getPanelId.createDelegate(this);
38969     this.box = null;
38970     this.activePanel = null;
38971     // ensure listeners are added...
38972     
38973     if (config.listeners || config.events) {
38974         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38975             listeners : config.listeners || {},
38976             events : config.events || {}
38977         });
38978     }
38979     
38980     if(skipConfig !== true){
38981         this.applyConfig(config);
38982     }
38983 };
38984
38985 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38986 {
38987     getPanelId : function(p){
38988         return p.getId();
38989     },
38990     
38991     applyConfig : function(config){
38992         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38993         this.config = config;
38994         
38995     },
38996     
38997     /**
38998      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38999      * the width, for horizontal (north, south) the height.
39000      * @param {Number} newSize The new width or height
39001      */
39002     resizeTo : function(newSize){
39003         var el = this.el ? this.el :
39004                  (this.activePanel ? this.activePanel.getEl() : null);
39005         if(el){
39006             switch(this.position){
39007                 case "east":
39008                 case "west":
39009                     el.setWidth(newSize);
39010                     this.fireEvent("resized", this, newSize);
39011                 break;
39012                 case "north":
39013                 case "south":
39014                     el.setHeight(newSize);
39015                     this.fireEvent("resized", this, newSize);
39016                 break;                
39017             }
39018         }
39019     },
39020     
39021     getBox : function(){
39022         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39023     },
39024     
39025     getMargins : function(){
39026         return this.margins;
39027     },
39028     
39029     updateBox : function(box){
39030         this.box = box;
39031         var el = this.activePanel.getEl();
39032         el.dom.style.left = box.x + "px";
39033         el.dom.style.top = box.y + "px";
39034         this.activePanel.setSize(box.width, box.height);
39035     },
39036     
39037     /**
39038      * Returns the container element for this region.
39039      * @return {Roo.Element}
39040      */
39041     getEl : function(){
39042         return this.activePanel;
39043     },
39044     
39045     /**
39046      * Returns true if this region is currently visible.
39047      * @return {Boolean}
39048      */
39049     isVisible : function(){
39050         return this.activePanel ? true : false;
39051     },
39052     
39053     setActivePanel : function(panel){
39054         panel = this.getPanel(panel);
39055         if(this.activePanel && this.activePanel != panel){
39056             this.activePanel.setActiveState(false);
39057             this.activePanel.getEl().setLeftTop(-10000,-10000);
39058         }
39059         this.activePanel = panel;
39060         panel.setActiveState(true);
39061         if(this.box){
39062             panel.setSize(this.box.width, this.box.height);
39063         }
39064         this.fireEvent("panelactivated", this, panel);
39065         this.fireEvent("invalidated");
39066     },
39067     
39068     /**
39069      * Show the specified panel.
39070      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39071      * @return {Roo.ContentPanel} The shown panel or null
39072      */
39073     showPanel : function(panel){
39074         panel = this.getPanel(panel);
39075         if(panel){
39076             this.setActivePanel(panel);
39077         }
39078         return panel;
39079     },
39080     
39081     /**
39082      * Get the active panel for this region.
39083      * @return {Roo.ContentPanel} The active panel or null
39084      */
39085     getActivePanel : function(){
39086         return this.activePanel;
39087     },
39088     
39089     /**
39090      * Add the passed ContentPanel(s)
39091      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39092      * @return {Roo.ContentPanel} The panel added (if only one was added)
39093      */
39094     add : function(panel){
39095         if(arguments.length > 1){
39096             for(var i = 0, len = arguments.length; i < len; i++) {
39097                 this.add(arguments[i]);
39098             }
39099             return null;
39100         }
39101         if(this.hasPanel(panel)){
39102             this.showPanel(panel);
39103             return panel;
39104         }
39105         var el = panel.getEl();
39106         if(el.dom.parentNode != this.mgr.el.dom){
39107             this.mgr.el.dom.appendChild(el.dom);
39108         }
39109         if(panel.setRegion){
39110             panel.setRegion(this);
39111         }
39112         this.panels.add(panel);
39113         el.setStyle("position", "absolute");
39114         if(!panel.background){
39115             this.setActivePanel(panel);
39116             if(this.config.initialSize && this.panels.getCount()==1){
39117                 this.resizeTo(this.config.initialSize);
39118             }
39119         }
39120         this.fireEvent("paneladded", this, panel);
39121         return panel;
39122     },
39123     
39124     /**
39125      * Returns true if the panel is in this region.
39126      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39127      * @return {Boolean}
39128      */
39129     hasPanel : function(panel){
39130         if(typeof panel == "object"){ // must be panel obj
39131             panel = panel.getId();
39132         }
39133         return this.getPanel(panel) ? true : false;
39134     },
39135     
39136     /**
39137      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39138      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39139      * @param {Boolean} preservePanel Overrides the config preservePanel option
39140      * @return {Roo.ContentPanel} The panel that was removed
39141      */
39142     remove : function(panel, preservePanel){
39143         panel = this.getPanel(panel);
39144         if(!panel){
39145             return null;
39146         }
39147         var e = {};
39148         this.fireEvent("beforeremove", this, panel, e);
39149         if(e.cancel === true){
39150             return null;
39151         }
39152         var panelId = panel.getId();
39153         this.panels.removeKey(panelId);
39154         return panel;
39155     },
39156     
39157     /**
39158      * Returns the panel specified or null if it's not in this region.
39159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39160      * @return {Roo.ContentPanel}
39161      */
39162     getPanel : function(id){
39163         if(typeof id == "object"){ // must be panel obj
39164             return id;
39165         }
39166         return this.panels.get(id);
39167     },
39168     
39169     /**
39170      * Returns this regions position (north/south/east/west/center).
39171      * @return {String} 
39172      */
39173     getPosition: function(){
39174         return this.position;    
39175     }
39176 });/*
39177  * Based on:
39178  * Ext JS Library 1.1.1
39179  * Copyright(c) 2006-2007, Ext JS, LLC.
39180  *
39181  * Originally Released Under LGPL - original licence link has changed is not relivant.
39182  *
39183  * Fork - LGPL
39184  * <script type="text/javascript">
39185  */
39186  
39187 /**
39188  * @class Roo.bootstrap.layout.Region
39189  * @extends Roo.bootstrap.layout.Basic
39190  * This class represents a region in a layout manager.
39191  
39192  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39193  * @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})
39194  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39195  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39196  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39197  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39198  * @cfg {String}    title           The title for the region (overrides panel titles)
39199  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39200  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39201  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39202  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39203  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39204  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39205  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39206  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39207  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39208  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39209
39210  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39211  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39212  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39213  * @cfg {Number}    width           For East/West panels
39214  * @cfg {Number}    height          For North/South panels
39215  * @cfg {Boolean}   split           To show the splitter
39216  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39217  * 
39218  * @cfg {string}   cls             Extra CSS classes to add to region
39219  * 
39220  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39221  * @cfg {string}   region  the region that it inhabits..
39222  *
39223
39224  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39225  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39226
39227  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39228  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39229  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39230  */
39231 Roo.bootstrap.layout.Region = function(config)
39232 {
39233     this.applyConfig(config);
39234
39235     var mgr = config.mgr;
39236     var pos = config.region;
39237     config.skipConfig = true;
39238     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39239     
39240     if (mgr.el) {
39241         this.onRender(mgr.el);   
39242     }
39243      
39244     this.visible = true;
39245     this.collapsed = false;
39246     this.unrendered_panels = [];
39247 };
39248
39249 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39250
39251     position: '', // set by wrapper (eg. north/south etc..)
39252     unrendered_panels : null,  // unrendered panels.
39253     
39254     tabPosition : false,
39255     
39256     mgr: false, // points to 'Border'
39257     
39258     
39259     createBody : function(){
39260         /** This region's body element 
39261         * @type Roo.Element */
39262         this.bodyEl = this.el.createChild({
39263                 tag: "div",
39264                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39265         });
39266     },
39267
39268     onRender: function(ctr, pos)
39269     {
39270         var dh = Roo.DomHelper;
39271         /** This region's container element 
39272         * @type Roo.Element */
39273         this.el = dh.append(ctr.dom, {
39274                 tag: "div",
39275                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39276             }, true);
39277         /** This region's title element 
39278         * @type Roo.Element */
39279     
39280         this.titleEl = dh.append(this.el.dom,  {
39281                 tag: "div",
39282                 unselectable: "on",
39283                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39284                 children:[
39285                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39286                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39287                 ]
39288             }, true);
39289         
39290         this.titleEl.enableDisplayMode();
39291         /** This region's title text element 
39292         * @type HTMLElement */
39293         this.titleTextEl = this.titleEl.dom.firstChild;
39294         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39295         /*
39296         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39297         this.closeBtn.enableDisplayMode();
39298         this.closeBtn.on("click", this.closeClicked, this);
39299         this.closeBtn.hide();
39300     */
39301         this.createBody(this.config);
39302         if(this.config.hideWhenEmpty){
39303             this.hide();
39304             this.on("paneladded", this.validateVisibility, this);
39305             this.on("panelremoved", this.validateVisibility, this);
39306         }
39307         if(this.autoScroll){
39308             this.bodyEl.setStyle("overflow", "auto");
39309         }else{
39310             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39311         }
39312         //if(c.titlebar !== false){
39313             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39314                 this.titleEl.hide();
39315             }else{
39316                 this.titleEl.show();
39317                 if(this.config.title){
39318                     this.titleTextEl.innerHTML = this.config.title;
39319                 }
39320             }
39321         //}
39322         if(this.config.collapsed){
39323             this.collapse(true);
39324         }
39325         if(this.config.hidden){
39326             this.hide();
39327         }
39328         
39329         if (this.unrendered_panels && this.unrendered_panels.length) {
39330             for (var i =0;i< this.unrendered_panels.length; i++) {
39331                 this.add(this.unrendered_panels[i]);
39332             }
39333             this.unrendered_panels = null;
39334             
39335         }
39336         
39337     },
39338     
39339     applyConfig : function(c)
39340     {
39341         /*
39342          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39343             var dh = Roo.DomHelper;
39344             if(c.titlebar !== false){
39345                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39346                 this.collapseBtn.on("click", this.collapse, this);
39347                 this.collapseBtn.enableDisplayMode();
39348                 /*
39349                 if(c.showPin === true || this.showPin){
39350                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39351                     this.stickBtn.enableDisplayMode();
39352                     this.stickBtn.on("click", this.expand, this);
39353                     this.stickBtn.hide();
39354                 }
39355                 
39356             }
39357             */
39358             /** This region's collapsed element
39359             * @type Roo.Element */
39360             /*
39361              *
39362             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39363                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39364             ]}, true);
39365             
39366             if(c.floatable !== false){
39367                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39368                this.collapsedEl.on("click", this.collapseClick, this);
39369             }
39370
39371             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39372                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39373                    id: "message", unselectable: "on", style:{"float":"left"}});
39374                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39375              }
39376             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39377             this.expandBtn.on("click", this.expand, this);
39378             
39379         }
39380         
39381         if(this.collapseBtn){
39382             this.collapseBtn.setVisible(c.collapsible == true);
39383         }
39384         
39385         this.cmargins = c.cmargins || this.cmargins ||
39386                          (this.position == "west" || this.position == "east" ?
39387                              {top: 0, left: 2, right:2, bottom: 0} :
39388                              {top: 2, left: 0, right:0, bottom: 2});
39389         */
39390         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39391         
39392         
39393         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39394         
39395         this.autoScroll = c.autoScroll || false;
39396         
39397         
39398        
39399         
39400         this.duration = c.duration || .30;
39401         this.slideDuration = c.slideDuration || .45;
39402         this.config = c;
39403        
39404     },
39405     /**
39406      * Returns true if this region is currently visible.
39407      * @return {Boolean}
39408      */
39409     isVisible : function(){
39410         return this.visible;
39411     },
39412
39413     /**
39414      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39415      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39416      */
39417     //setCollapsedTitle : function(title){
39418     //    title = title || "&#160;";
39419      //   if(this.collapsedTitleTextEl){
39420       //      this.collapsedTitleTextEl.innerHTML = title;
39421        // }
39422     //},
39423
39424     getBox : function(){
39425         var b;
39426       //  if(!this.collapsed){
39427             b = this.el.getBox(false, true);
39428        // }else{
39429           //  b = this.collapsedEl.getBox(false, true);
39430         //}
39431         return b;
39432     },
39433
39434     getMargins : function(){
39435         return this.margins;
39436         //return this.collapsed ? this.cmargins : this.margins;
39437     },
39438 /*
39439     highlight : function(){
39440         this.el.addClass("x-layout-panel-dragover");
39441     },
39442
39443     unhighlight : function(){
39444         this.el.removeClass("x-layout-panel-dragover");
39445     },
39446 */
39447     updateBox : function(box)
39448     {
39449         if (!this.bodyEl) {
39450             return; // not rendered yet..
39451         }
39452         
39453         this.box = box;
39454         if(!this.collapsed){
39455             this.el.dom.style.left = box.x + "px";
39456             this.el.dom.style.top = box.y + "px";
39457             this.updateBody(box.width, box.height);
39458         }else{
39459             this.collapsedEl.dom.style.left = box.x + "px";
39460             this.collapsedEl.dom.style.top = box.y + "px";
39461             this.collapsedEl.setSize(box.width, box.height);
39462         }
39463         if(this.tabs){
39464             this.tabs.autoSizeTabs();
39465         }
39466     },
39467
39468     updateBody : function(w, h)
39469     {
39470         if(w !== null){
39471             this.el.setWidth(w);
39472             w -= this.el.getBorderWidth("rl");
39473             if(this.config.adjustments){
39474                 w += this.config.adjustments[0];
39475             }
39476         }
39477         if(h !== null && h > 0){
39478             this.el.setHeight(h);
39479             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39480             h -= this.el.getBorderWidth("tb");
39481             if(this.config.adjustments){
39482                 h += this.config.adjustments[1];
39483             }
39484             this.bodyEl.setHeight(h);
39485             if(this.tabs){
39486                 h = this.tabs.syncHeight(h);
39487             }
39488         }
39489         if(this.panelSize){
39490             w = w !== null ? w : this.panelSize.width;
39491             h = h !== null ? h : this.panelSize.height;
39492         }
39493         if(this.activePanel){
39494             var el = this.activePanel.getEl();
39495             w = w !== null ? w : el.getWidth();
39496             h = h !== null ? h : el.getHeight();
39497             this.panelSize = {width: w, height: h};
39498             this.activePanel.setSize(w, h);
39499         }
39500         if(Roo.isIE && this.tabs){
39501             this.tabs.el.repaint();
39502         }
39503     },
39504
39505     /**
39506      * Returns the container element for this region.
39507      * @return {Roo.Element}
39508      */
39509     getEl : function(){
39510         return this.el;
39511     },
39512
39513     /**
39514      * Hides this region.
39515      */
39516     hide : function(){
39517         //if(!this.collapsed){
39518             this.el.dom.style.left = "-2000px";
39519             this.el.hide();
39520         //}else{
39521          //   this.collapsedEl.dom.style.left = "-2000px";
39522          //   this.collapsedEl.hide();
39523        // }
39524         this.visible = false;
39525         this.fireEvent("visibilitychange", this, false);
39526     },
39527
39528     /**
39529      * Shows this region if it was previously hidden.
39530      */
39531     show : function(){
39532         //if(!this.collapsed){
39533             this.el.show();
39534         //}else{
39535         //    this.collapsedEl.show();
39536        // }
39537         this.visible = true;
39538         this.fireEvent("visibilitychange", this, true);
39539     },
39540 /*
39541     closeClicked : function(){
39542         if(this.activePanel){
39543             this.remove(this.activePanel);
39544         }
39545     },
39546
39547     collapseClick : function(e){
39548         if(this.isSlid){
39549            e.stopPropagation();
39550            this.slideIn();
39551         }else{
39552            e.stopPropagation();
39553            this.slideOut();
39554         }
39555     },
39556 */
39557     /**
39558      * Collapses this region.
39559      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39560      */
39561     /*
39562     collapse : function(skipAnim, skipCheck = false){
39563         if(this.collapsed) {
39564             return;
39565         }
39566         
39567         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39568             
39569             this.collapsed = true;
39570             if(this.split){
39571                 this.split.el.hide();
39572             }
39573             if(this.config.animate && skipAnim !== true){
39574                 this.fireEvent("invalidated", this);
39575                 this.animateCollapse();
39576             }else{
39577                 this.el.setLocation(-20000,-20000);
39578                 this.el.hide();
39579                 this.collapsedEl.show();
39580                 this.fireEvent("collapsed", this);
39581                 this.fireEvent("invalidated", this);
39582             }
39583         }
39584         
39585     },
39586 */
39587     animateCollapse : function(){
39588         // overridden
39589     },
39590
39591     /**
39592      * Expands this region if it was previously collapsed.
39593      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39594      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39595      */
39596     /*
39597     expand : function(e, skipAnim){
39598         if(e) {
39599             e.stopPropagation();
39600         }
39601         if(!this.collapsed || this.el.hasActiveFx()) {
39602             return;
39603         }
39604         if(this.isSlid){
39605             this.afterSlideIn();
39606             skipAnim = true;
39607         }
39608         this.collapsed = false;
39609         if(this.config.animate && skipAnim !== true){
39610             this.animateExpand();
39611         }else{
39612             this.el.show();
39613             if(this.split){
39614                 this.split.el.show();
39615             }
39616             this.collapsedEl.setLocation(-2000,-2000);
39617             this.collapsedEl.hide();
39618             this.fireEvent("invalidated", this);
39619             this.fireEvent("expanded", this);
39620         }
39621     },
39622 */
39623     animateExpand : function(){
39624         // overridden
39625     },
39626
39627     initTabs : function()
39628     {
39629         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39630         
39631         var ts = new Roo.bootstrap.panel.Tabs({
39632             el: this.bodyEl.dom,
39633             region : this,
39634             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39635             disableTooltips: this.config.disableTabTips,
39636             toolbar : this.config.toolbar
39637         });
39638         
39639         if(this.config.hideTabs){
39640             ts.stripWrap.setDisplayed(false);
39641         }
39642         this.tabs = ts;
39643         ts.resizeTabs = this.config.resizeTabs === true;
39644         ts.minTabWidth = this.config.minTabWidth || 40;
39645         ts.maxTabWidth = this.config.maxTabWidth || 250;
39646         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39647         ts.monitorResize = false;
39648         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39649         ts.bodyEl.addClass('roo-layout-tabs-body');
39650         this.panels.each(this.initPanelAsTab, this);
39651     },
39652
39653     initPanelAsTab : function(panel){
39654         var ti = this.tabs.addTab(
39655             panel.getEl().id,
39656             panel.getTitle(),
39657             null,
39658             this.config.closeOnTab && panel.isClosable(),
39659             panel.tpl
39660         );
39661         if(panel.tabTip !== undefined){
39662             ti.setTooltip(panel.tabTip);
39663         }
39664         ti.on("activate", function(){
39665               this.setActivePanel(panel);
39666         }, this);
39667         
39668         if(this.config.closeOnTab){
39669             ti.on("beforeclose", function(t, e){
39670                 e.cancel = true;
39671                 this.remove(panel);
39672             }, this);
39673         }
39674         
39675         panel.tabItem = ti;
39676         
39677         return ti;
39678     },
39679
39680     updatePanelTitle : function(panel, title)
39681     {
39682         if(this.activePanel == panel){
39683             this.updateTitle(title);
39684         }
39685         if(this.tabs){
39686             var ti = this.tabs.getTab(panel.getEl().id);
39687             ti.setText(title);
39688             if(panel.tabTip !== undefined){
39689                 ti.setTooltip(panel.tabTip);
39690             }
39691         }
39692     },
39693
39694     updateTitle : function(title){
39695         if(this.titleTextEl && !this.config.title){
39696             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39697         }
39698     },
39699
39700     setActivePanel : function(panel)
39701     {
39702         panel = this.getPanel(panel);
39703         if(this.activePanel && this.activePanel != panel){
39704             if(this.activePanel.setActiveState(false) === false){
39705                 return;
39706             }
39707         }
39708         this.activePanel = panel;
39709         panel.setActiveState(true);
39710         if(this.panelSize){
39711             panel.setSize(this.panelSize.width, this.panelSize.height);
39712         }
39713         if(this.closeBtn){
39714             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39715         }
39716         this.updateTitle(panel.getTitle());
39717         if(this.tabs){
39718             this.fireEvent("invalidated", this);
39719         }
39720         this.fireEvent("panelactivated", this, panel);
39721     },
39722
39723     /**
39724      * Shows the specified panel.
39725      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39726      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39727      */
39728     showPanel : function(panel)
39729     {
39730         panel = this.getPanel(panel);
39731         if(panel){
39732             if(this.tabs){
39733                 var tab = this.tabs.getTab(panel.getEl().id);
39734                 if(tab.isHidden()){
39735                     this.tabs.unhideTab(tab.id);
39736                 }
39737                 tab.activate();
39738             }else{
39739                 this.setActivePanel(panel);
39740             }
39741         }
39742         return panel;
39743     },
39744
39745     /**
39746      * Get the active panel for this region.
39747      * @return {Roo.ContentPanel} The active panel or null
39748      */
39749     getActivePanel : function(){
39750         return this.activePanel;
39751     },
39752
39753     validateVisibility : function(){
39754         if(this.panels.getCount() < 1){
39755             this.updateTitle("&#160;");
39756             this.closeBtn.hide();
39757             this.hide();
39758         }else{
39759             if(!this.isVisible()){
39760                 this.show();
39761             }
39762         }
39763     },
39764
39765     /**
39766      * Adds the passed ContentPanel(s) to this region.
39767      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39768      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39769      */
39770     add : function(panel)
39771     {
39772         if(arguments.length > 1){
39773             for(var i = 0, len = arguments.length; i < len; i++) {
39774                 this.add(arguments[i]);
39775             }
39776             return null;
39777         }
39778         
39779         // if we have not been rendered yet, then we can not really do much of this..
39780         if (!this.bodyEl) {
39781             this.unrendered_panels.push(panel);
39782             return panel;
39783         }
39784         
39785         
39786         
39787         
39788         if(this.hasPanel(panel)){
39789             this.showPanel(panel);
39790             return panel;
39791         }
39792         panel.setRegion(this);
39793         this.panels.add(panel);
39794        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39795             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39796             // and hide them... ???
39797             this.bodyEl.dom.appendChild(panel.getEl().dom);
39798             if(panel.background !== true){
39799                 this.setActivePanel(panel);
39800             }
39801             this.fireEvent("paneladded", this, panel);
39802             return panel;
39803         }
39804         */
39805         if(!this.tabs){
39806             this.initTabs();
39807         }else{
39808             this.initPanelAsTab(panel);
39809         }
39810         
39811         
39812         if(panel.background !== true){
39813             this.tabs.activate(panel.getEl().id);
39814         }
39815         this.fireEvent("paneladded", this, panel);
39816         return panel;
39817     },
39818
39819     /**
39820      * Hides the tab for the specified panel.
39821      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39822      */
39823     hidePanel : function(panel){
39824         if(this.tabs && (panel = this.getPanel(panel))){
39825             this.tabs.hideTab(panel.getEl().id);
39826         }
39827     },
39828
39829     /**
39830      * Unhides the tab for a previously hidden panel.
39831      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39832      */
39833     unhidePanel : function(panel){
39834         if(this.tabs && (panel = this.getPanel(panel))){
39835             this.tabs.unhideTab(panel.getEl().id);
39836         }
39837     },
39838
39839     clearPanels : function(){
39840         while(this.panels.getCount() > 0){
39841              this.remove(this.panels.first());
39842         }
39843     },
39844
39845     /**
39846      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39847      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39848      * @param {Boolean} preservePanel Overrides the config preservePanel option
39849      * @return {Roo.ContentPanel} The panel that was removed
39850      */
39851     remove : function(panel, preservePanel)
39852     {
39853         panel = this.getPanel(panel);
39854         if(!panel){
39855             return null;
39856         }
39857         var e = {};
39858         this.fireEvent("beforeremove", this, panel, e);
39859         if(e.cancel === true){
39860             return null;
39861         }
39862         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39863         var panelId = panel.getId();
39864         this.panels.removeKey(panelId);
39865         if(preservePanel){
39866             document.body.appendChild(panel.getEl().dom);
39867         }
39868         if(this.tabs){
39869             this.tabs.removeTab(panel.getEl().id);
39870         }else if (!preservePanel){
39871             this.bodyEl.dom.removeChild(panel.getEl().dom);
39872         }
39873         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39874             var p = this.panels.first();
39875             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39876             tempEl.appendChild(p.getEl().dom);
39877             this.bodyEl.update("");
39878             this.bodyEl.dom.appendChild(p.getEl().dom);
39879             tempEl = null;
39880             this.updateTitle(p.getTitle());
39881             this.tabs = null;
39882             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39883             this.setActivePanel(p);
39884         }
39885         panel.setRegion(null);
39886         if(this.activePanel == panel){
39887             this.activePanel = null;
39888         }
39889         if(this.config.autoDestroy !== false && preservePanel !== true){
39890             try{panel.destroy();}catch(e){}
39891         }
39892         this.fireEvent("panelremoved", this, panel);
39893         return panel;
39894     },
39895
39896     /**
39897      * Returns the TabPanel component used by this region
39898      * @return {Roo.TabPanel}
39899      */
39900     getTabs : function(){
39901         return this.tabs;
39902     },
39903
39904     createTool : function(parentEl, className){
39905         var btn = Roo.DomHelper.append(parentEl, {
39906             tag: "div",
39907             cls: "x-layout-tools-button",
39908             children: [ {
39909                 tag: "div",
39910                 cls: "roo-layout-tools-button-inner " + className,
39911                 html: "&#160;"
39912             }]
39913         }, true);
39914         btn.addClassOnOver("roo-layout-tools-button-over");
39915         return btn;
39916     }
39917 });/*
39918  * Based on:
39919  * Ext JS Library 1.1.1
39920  * Copyright(c) 2006-2007, Ext JS, LLC.
39921  *
39922  * Originally Released Under LGPL - original licence link has changed is not relivant.
39923  *
39924  * Fork - LGPL
39925  * <script type="text/javascript">
39926  */
39927  
39928
39929
39930 /**
39931  * @class Roo.SplitLayoutRegion
39932  * @extends Roo.LayoutRegion
39933  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39934  */
39935 Roo.bootstrap.layout.Split = function(config){
39936     this.cursor = config.cursor;
39937     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39938 };
39939
39940 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39941 {
39942     splitTip : "Drag to resize.",
39943     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39944     useSplitTips : false,
39945
39946     applyConfig : function(config){
39947         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39948     },
39949     
39950     onRender : function(ctr,pos) {
39951         
39952         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39953         if(!this.config.split){
39954             return;
39955         }
39956         if(!this.split){
39957             
39958             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39959                             tag: "div",
39960                             id: this.el.id + "-split",
39961                             cls: "roo-layout-split roo-layout-split-"+this.position,
39962                             html: "&#160;"
39963             });
39964             /** The SplitBar for this region 
39965             * @type Roo.SplitBar */
39966             // does not exist yet...
39967             Roo.log([this.position, this.orientation]);
39968             
39969             this.split = new Roo.bootstrap.SplitBar({
39970                 dragElement : splitEl,
39971                 resizingElement: this.el,
39972                 orientation : this.orientation
39973             });
39974             
39975             this.split.on("moved", this.onSplitMove, this);
39976             this.split.useShim = this.config.useShim === true;
39977             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39978             if(this.useSplitTips){
39979                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39980             }
39981             //if(config.collapsible){
39982             //    this.split.el.on("dblclick", this.collapse,  this);
39983             //}
39984         }
39985         if(typeof this.config.minSize != "undefined"){
39986             this.split.minSize = this.config.minSize;
39987         }
39988         if(typeof this.config.maxSize != "undefined"){
39989             this.split.maxSize = this.config.maxSize;
39990         }
39991         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39992             this.hideSplitter();
39993         }
39994         
39995     },
39996
39997     getHMaxSize : function(){
39998          var cmax = this.config.maxSize || 10000;
39999          var center = this.mgr.getRegion("center");
40000          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40001     },
40002
40003     getVMaxSize : function(){
40004          var cmax = this.config.maxSize || 10000;
40005          var center = this.mgr.getRegion("center");
40006          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40007     },
40008
40009     onSplitMove : function(split, newSize){
40010         this.fireEvent("resized", this, newSize);
40011     },
40012     
40013     /** 
40014      * Returns the {@link Roo.SplitBar} for this region.
40015      * @return {Roo.SplitBar}
40016      */
40017     getSplitBar : function(){
40018         return this.split;
40019     },
40020     
40021     hide : function(){
40022         this.hideSplitter();
40023         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40024     },
40025
40026     hideSplitter : function(){
40027         if(this.split){
40028             this.split.el.setLocation(-2000,-2000);
40029             this.split.el.hide();
40030         }
40031     },
40032
40033     show : function(){
40034         if(this.split){
40035             this.split.el.show();
40036         }
40037         Roo.bootstrap.layout.Split.superclass.show.call(this);
40038     },
40039     
40040     beforeSlide: function(){
40041         if(Roo.isGecko){// firefox overflow auto bug workaround
40042             this.bodyEl.clip();
40043             if(this.tabs) {
40044                 this.tabs.bodyEl.clip();
40045             }
40046             if(this.activePanel){
40047                 this.activePanel.getEl().clip();
40048                 
40049                 if(this.activePanel.beforeSlide){
40050                     this.activePanel.beforeSlide();
40051                 }
40052             }
40053         }
40054     },
40055     
40056     afterSlide : function(){
40057         if(Roo.isGecko){// firefox overflow auto bug workaround
40058             this.bodyEl.unclip();
40059             if(this.tabs) {
40060                 this.tabs.bodyEl.unclip();
40061             }
40062             if(this.activePanel){
40063                 this.activePanel.getEl().unclip();
40064                 if(this.activePanel.afterSlide){
40065                     this.activePanel.afterSlide();
40066                 }
40067             }
40068         }
40069     },
40070
40071     initAutoHide : function(){
40072         if(this.autoHide !== false){
40073             if(!this.autoHideHd){
40074                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40075                 this.autoHideHd = {
40076                     "mouseout": function(e){
40077                         if(!e.within(this.el, true)){
40078                             st.delay(500);
40079                         }
40080                     },
40081                     "mouseover" : function(e){
40082                         st.cancel();
40083                     },
40084                     scope : this
40085                 };
40086             }
40087             this.el.on(this.autoHideHd);
40088         }
40089     },
40090
40091     clearAutoHide : function(){
40092         if(this.autoHide !== false){
40093             this.el.un("mouseout", this.autoHideHd.mouseout);
40094             this.el.un("mouseover", this.autoHideHd.mouseover);
40095         }
40096     },
40097
40098     clearMonitor : function(){
40099         Roo.get(document).un("click", this.slideInIf, this);
40100     },
40101
40102     // these names are backwards but not changed for compat
40103     slideOut : function(){
40104         if(this.isSlid || this.el.hasActiveFx()){
40105             return;
40106         }
40107         this.isSlid = true;
40108         if(this.collapseBtn){
40109             this.collapseBtn.hide();
40110         }
40111         this.closeBtnState = this.closeBtn.getStyle('display');
40112         this.closeBtn.hide();
40113         if(this.stickBtn){
40114             this.stickBtn.show();
40115         }
40116         this.el.show();
40117         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40118         this.beforeSlide();
40119         this.el.setStyle("z-index", 10001);
40120         this.el.slideIn(this.getSlideAnchor(), {
40121             callback: function(){
40122                 this.afterSlide();
40123                 this.initAutoHide();
40124                 Roo.get(document).on("click", this.slideInIf, this);
40125                 this.fireEvent("slideshow", this);
40126             },
40127             scope: this,
40128             block: true
40129         });
40130     },
40131
40132     afterSlideIn : function(){
40133         this.clearAutoHide();
40134         this.isSlid = false;
40135         this.clearMonitor();
40136         this.el.setStyle("z-index", "");
40137         if(this.collapseBtn){
40138             this.collapseBtn.show();
40139         }
40140         this.closeBtn.setStyle('display', this.closeBtnState);
40141         if(this.stickBtn){
40142             this.stickBtn.hide();
40143         }
40144         this.fireEvent("slidehide", this);
40145     },
40146
40147     slideIn : function(cb){
40148         if(!this.isSlid || this.el.hasActiveFx()){
40149             Roo.callback(cb);
40150             return;
40151         }
40152         this.isSlid = false;
40153         this.beforeSlide();
40154         this.el.slideOut(this.getSlideAnchor(), {
40155             callback: function(){
40156                 this.el.setLeftTop(-10000, -10000);
40157                 this.afterSlide();
40158                 this.afterSlideIn();
40159                 Roo.callback(cb);
40160             },
40161             scope: this,
40162             block: true
40163         });
40164     },
40165     
40166     slideInIf : function(e){
40167         if(!e.within(this.el)){
40168             this.slideIn();
40169         }
40170     },
40171
40172     animateCollapse : function(){
40173         this.beforeSlide();
40174         this.el.setStyle("z-index", 20000);
40175         var anchor = this.getSlideAnchor();
40176         this.el.slideOut(anchor, {
40177             callback : function(){
40178                 this.el.setStyle("z-index", "");
40179                 this.collapsedEl.slideIn(anchor, {duration:.3});
40180                 this.afterSlide();
40181                 this.el.setLocation(-10000,-10000);
40182                 this.el.hide();
40183                 this.fireEvent("collapsed", this);
40184             },
40185             scope: this,
40186             block: true
40187         });
40188     },
40189
40190     animateExpand : function(){
40191         this.beforeSlide();
40192         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40193         this.el.setStyle("z-index", 20000);
40194         this.collapsedEl.hide({
40195             duration:.1
40196         });
40197         this.el.slideIn(this.getSlideAnchor(), {
40198             callback : function(){
40199                 this.el.setStyle("z-index", "");
40200                 this.afterSlide();
40201                 if(this.split){
40202                     this.split.el.show();
40203                 }
40204                 this.fireEvent("invalidated", this);
40205                 this.fireEvent("expanded", this);
40206             },
40207             scope: this,
40208             block: true
40209         });
40210     },
40211
40212     anchors : {
40213         "west" : "left",
40214         "east" : "right",
40215         "north" : "top",
40216         "south" : "bottom"
40217     },
40218
40219     sanchors : {
40220         "west" : "l",
40221         "east" : "r",
40222         "north" : "t",
40223         "south" : "b"
40224     },
40225
40226     canchors : {
40227         "west" : "tl-tr",
40228         "east" : "tr-tl",
40229         "north" : "tl-bl",
40230         "south" : "bl-tl"
40231     },
40232
40233     getAnchor : function(){
40234         return this.anchors[this.position];
40235     },
40236
40237     getCollapseAnchor : function(){
40238         return this.canchors[this.position];
40239     },
40240
40241     getSlideAnchor : function(){
40242         return this.sanchors[this.position];
40243     },
40244
40245     getAlignAdj : function(){
40246         var cm = this.cmargins;
40247         switch(this.position){
40248             case "west":
40249                 return [0, 0];
40250             break;
40251             case "east":
40252                 return [0, 0];
40253             break;
40254             case "north":
40255                 return [0, 0];
40256             break;
40257             case "south":
40258                 return [0, 0];
40259             break;
40260         }
40261     },
40262
40263     getExpandAdj : function(){
40264         var c = this.collapsedEl, cm = this.cmargins;
40265         switch(this.position){
40266             case "west":
40267                 return [-(cm.right+c.getWidth()+cm.left), 0];
40268             break;
40269             case "east":
40270                 return [cm.right+c.getWidth()+cm.left, 0];
40271             break;
40272             case "north":
40273                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40274             break;
40275             case "south":
40276                 return [0, cm.top+cm.bottom+c.getHeight()];
40277             break;
40278         }
40279     }
40280 });/*
40281  * Based on:
40282  * Ext JS Library 1.1.1
40283  * Copyright(c) 2006-2007, Ext JS, LLC.
40284  *
40285  * Originally Released Under LGPL - original licence link has changed is not relivant.
40286  *
40287  * Fork - LGPL
40288  * <script type="text/javascript">
40289  */
40290 /*
40291  * These classes are private internal classes
40292  */
40293 Roo.bootstrap.layout.Center = function(config){
40294     config.region = "center";
40295     Roo.bootstrap.layout.Region.call(this, config);
40296     this.visible = true;
40297     this.minWidth = config.minWidth || 20;
40298     this.minHeight = config.minHeight || 20;
40299 };
40300
40301 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40302     hide : function(){
40303         // center panel can't be hidden
40304     },
40305     
40306     show : function(){
40307         // center panel can't be hidden
40308     },
40309     
40310     getMinWidth: function(){
40311         return this.minWidth;
40312     },
40313     
40314     getMinHeight: function(){
40315         return this.minHeight;
40316     }
40317 });
40318
40319
40320
40321
40322  
40323
40324
40325
40326
40327
40328
40329 Roo.bootstrap.layout.North = function(config)
40330 {
40331     config.region = 'north';
40332     config.cursor = 'n-resize';
40333     
40334     Roo.bootstrap.layout.Split.call(this, config);
40335     
40336     
40337     if(this.split){
40338         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40339         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40340         this.split.el.addClass("roo-layout-split-v");
40341     }
40342     //var size = config.initialSize || config.height;
40343     //if(this.el && typeof size != "undefined"){
40344     //    this.el.setHeight(size);
40345     //}
40346 };
40347 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40348 {
40349     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40350      
40351      
40352     onRender : function(ctr, pos)
40353     {
40354         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40355         var size = this.config.initialSize || this.config.height;
40356         if(this.el && typeof size != "undefined"){
40357             this.el.setHeight(size);
40358         }
40359     
40360     },
40361     
40362     getBox : function(){
40363         if(this.collapsed){
40364             return this.collapsedEl.getBox();
40365         }
40366         var box = this.el.getBox();
40367         if(this.split){
40368             box.height += this.split.el.getHeight();
40369         }
40370         return box;
40371     },
40372     
40373     updateBox : function(box){
40374         if(this.split && !this.collapsed){
40375             box.height -= this.split.el.getHeight();
40376             this.split.el.setLeft(box.x);
40377             this.split.el.setTop(box.y+box.height);
40378             this.split.el.setWidth(box.width);
40379         }
40380         if(this.collapsed){
40381             this.updateBody(box.width, null);
40382         }
40383         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40384     }
40385 });
40386
40387
40388
40389
40390
40391 Roo.bootstrap.layout.South = function(config){
40392     config.region = 'south';
40393     config.cursor = 's-resize';
40394     Roo.bootstrap.layout.Split.call(this, config);
40395     if(this.split){
40396         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40397         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40398         this.split.el.addClass("roo-layout-split-v");
40399     }
40400     
40401 };
40402
40403 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40404     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40405     
40406     onRender : function(ctr, pos)
40407     {
40408         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40409         var size = this.config.initialSize || this.config.height;
40410         if(this.el && typeof size != "undefined"){
40411             this.el.setHeight(size);
40412         }
40413     
40414     },
40415     
40416     getBox : function(){
40417         if(this.collapsed){
40418             return this.collapsedEl.getBox();
40419         }
40420         var box = this.el.getBox();
40421         if(this.split){
40422             var sh = this.split.el.getHeight();
40423             box.height += sh;
40424             box.y -= sh;
40425         }
40426         return box;
40427     },
40428     
40429     updateBox : function(box){
40430         if(this.split && !this.collapsed){
40431             var sh = this.split.el.getHeight();
40432             box.height -= sh;
40433             box.y += sh;
40434             this.split.el.setLeft(box.x);
40435             this.split.el.setTop(box.y-sh);
40436             this.split.el.setWidth(box.width);
40437         }
40438         if(this.collapsed){
40439             this.updateBody(box.width, null);
40440         }
40441         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40442     }
40443 });
40444
40445 Roo.bootstrap.layout.East = function(config){
40446     config.region = "east";
40447     config.cursor = "e-resize";
40448     Roo.bootstrap.layout.Split.call(this, config);
40449     if(this.split){
40450         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40451         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40452         this.split.el.addClass("roo-layout-split-h");
40453     }
40454     
40455 };
40456 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40457     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40458     
40459     onRender : function(ctr, pos)
40460     {
40461         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40462         var size = this.config.initialSize || this.config.width;
40463         if(this.el && typeof size != "undefined"){
40464             this.el.setWidth(size);
40465         }
40466     
40467     },
40468     
40469     getBox : function(){
40470         if(this.collapsed){
40471             return this.collapsedEl.getBox();
40472         }
40473         var box = this.el.getBox();
40474         if(this.split){
40475             var sw = this.split.el.getWidth();
40476             box.width += sw;
40477             box.x -= sw;
40478         }
40479         return box;
40480     },
40481
40482     updateBox : function(box){
40483         if(this.split && !this.collapsed){
40484             var sw = this.split.el.getWidth();
40485             box.width -= sw;
40486             this.split.el.setLeft(box.x);
40487             this.split.el.setTop(box.y);
40488             this.split.el.setHeight(box.height);
40489             box.x += sw;
40490         }
40491         if(this.collapsed){
40492             this.updateBody(null, box.height);
40493         }
40494         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40495     }
40496 });
40497
40498 Roo.bootstrap.layout.West = function(config){
40499     config.region = "west";
40500     config.cursor = "w-resize";
40501     
40502     Roo.bootstrap.layout.Split.call(this, config);
40503     if(this.split){
40504         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40505         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40506         this.split.el.addClass("roo-layout-split-h");
40507     }
40508     
40509 };
40510 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40511     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40512     
40513     onRender: function(ctr, pos)
40514     {
40515         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40516         var size = this.config.initialSize || this.config.width;
40517         if(typeof size != "undefined"){
40518             this.el.setWidth(size);
40519         }
40520     },
40521     
40522     getBox : function(){
40523         if(this.collapsed){
40524             return this.collapsedEl.getBox();
40525         }
40526         var box = this.el.getBox();
40527         if (box.width == 0) {
40528             box.width = this.config.width; // kludge?
40529         }
40530         if(this.split){
40531             box.width += this.split.el.getWidth();
40532         }
40533         return box;
40534     },
40535     
40536     updateBox : function(box){
40537         if(this.split && !this.collapsed){
40538             var sw = this.split.el.getWidth();
40539             box.width -= sw;
40540             this.split.el.setLeft(box.x+box.width);
40541             this.split.el.setTop(box.y);
40542             this.split.el.setHeight(box.height);
40543         }
40544         if(this.collapsed){
40545             this.updateBody(null, box.height);
40546         }
40547         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40548     }
40549 });Roo.namespace("Roo.bootstrap.panel");/*
40550  * Based on:
40551  * Ext JS Library 1.1.1
40552  * Copyright(c) 2006-2007, Ext JS, LLC.
40553  *
40554  * Originally Released Under LGPL - original licence link has changed is not relivant.
40555  *
40556  * Fork - LGPL
40557  * <script type="text/javascript">
40558  */
40559 /**
40560  * @class Roo.ContentPanel
40561  * @extends Roo.util.Observable
40562  * @builder-top
40563  * A basic ContentPanel element.
40564  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40565  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40566  * @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
40567  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40568  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40569  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40570  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40571  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40572  * @cfg {String} title          The title for this panel
40573  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40574  * @cfg {String} url            Calls {@link #setUrl} with this value
40575  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40576  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40577  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40578  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40579  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40580  * @cfg {Boolean} badges render the badges
40581  * @cfg {String} cls  extra classes to use  
40582  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40583
40584  * @constructor
40585  * Create a new ContentPanel.
40586  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40587  * @param {String/Object} config A string to set only the title or a config object
40588  * @param {String} content (optional) Set the HTML content for this panel
40589  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40590  */
40591 Roo.bootstrap.panel.Content = function( config){
40592     
40593     this.tpl = config.tpl || false;
40594     
40595     var el = config.el;
40596     var content = config.content;
40597
40598     if(config.autoCreate){ // xtype is available if this is called from factory
40599         el = Roo.id();
40600     }
40601     this.el = Roo.get(el);
40602     if(!this.el && config && config.autoCreate){
40603         if(typeof config.autoCreate == "object"){
40604             if(!config.autoCreate.id){
40605                 config.autoCreate.id = config.id||el;
40606             }
40607             this.el = Roo.DomHelper.append(document.body,
40608                         config.autoCreate, true);
40609         }else{
40610             var elcfg =  {
40611                 tag: "div",
40612                 cls: (config.cls || '') +
40613                     (config.background ? ' bg-' + config.background : '') +
40614                     " roo-layout-inactive-content",
40615                 id: config.id||el
40616             };
40617             if (config.iframe) {
40618                 elcfg.cn = [
40619                     {
40620                         tag : 'iframe',
40621                         style : 'border: 0px',
40622                         src : 'about:blank'
40623                     }
40624                 ];
40625             }
40626               
40627             if (config.html) {
40628                 elcfg.html = config.html;
40629                 
40630             }
40631                         
40632             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40633             if (config.iframe) {
40634                 this.iframeEl = this.el.select('iframe',true).first();
40635             }
40636             
40637         }
40638     } 
40639     this.closable = false;
40640     this.loaded = false;
40641     this.active = false;
40642    
40643       
40644     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40645         
40646         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40647         
40648         this.wrapEl = this.el; //this.el.wrap();
40649         var ti = [];
40650         if (config.toolbar.items) {
40651             ti = config.toolbar.items ;
40652             delete config.toolbar.items ;
40653         }
40654         
40655         var nitems = [];
40656         this.toolbar.render(this.wrapEl, 'before');
40657         for(var i =0;i < ti.length;i++) {
40658           //  Roo.log(['add child', items[i]]);
40659             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40660         }
40661         this.toolbar.items = nitems;
40662         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40663         delete config.toolbar;
40664         
40665     }
40666     /*
40667     // xtype created footer. - not sure if will work as we normally have to render first..
40668     if (this.footer && !this.footer.el && this.footer.xtype) {
40669         if (!this.wrapEl) {
40670             this.wrapEl = this.el.wrap();
40671         }
40672     
40673         this.footer.container = this.wrapEl.createChild();
40674          
40675         this.footer = Roo.factory(this.footer, Roo);
40676         
40677     }
40678     */
40679     
40680      if(typeof config == "string"){
40681         this.title = config;
40682     }else{
40683         Roo.apply(this, config);
40684     }
40685     
40686     if(this.resizeEl){
40687         this.resizeEl = Roo.get(this.resizeEl, true);
40688     }else{
40689         this.resizeEl = this.el;
40690     }
40691     // handle view.xtype
40692     
40693  
40694     
40695     
40696     this.addEvents({
40697         /**
40698          * @event activate
40699          * Fires when this panel is activated. 
40700          * @param {Roo.ContentPanel} this
40701          */
40702         "activate" : true,
40703         /**
40704          * @event deactivate
40705          * Fires when this panel is activated. 
40706          * @param {Roo.ContentPanel} this
40707          */
40708         "deactivate" : true,
40709
40710         /**
40711          * @event resize
40712          * Fires when this panel is resized if fitToFrame is true.
40713          * @param {Roo.ContentPanel} this
40714          * @param {Number} width The width after any component adjustments
40715          * @param {Number} height The height after any component adjustments
40716          */
40717         "resize" : true,
40718         
40719          /**
40720          * @event render
40721          * Fires when this tab is created
40722          * @param {Roo.ContentPanel} this
40723          */
40724         "render" : true,
40725         
40726           /**
40727          * @event scroll
40728          * Fires when this content is scrolled
40729          * @param {Roo.ContentPanel} this
40730          * @param {Event} scrollEvent
40731          */
40732         "scroll" : true
40733         
40734         
40735         
40736     });
40737     
40738
40739     
40740     
40741     if(this.autoScroll && !this.iframe){
40742         this.resizeEl.setStyle("overflow", "auto");
40743         this.resizeEl.on('scroll', this.onScroll, this);
40744     } else {
40745         // fix randome scrolling
40746         //this.el.on('scroll', function() {
40747         //    Roo.log('fix random scolling');
40748         //    this.scrollTo('top',0); 
40749         //});
40750     }
40751     content = content || this.content;
40752     if(content){
40753         this.setContent(content);
40754     }
40755     if(config && config.url){
40756         this.setUrl(this.url, this.params, this.loadOnce);
40757     }
40758     
40759     
40760     
40761     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40762     
40763     if (this.view && typeof(this.view.xtype) != 'undefined') {
40764         this.view.el = this.el.appendChild(document.createElement("div"));
40765         this.view = Roo.factory(this.view); 
40766         this.view.render  &&  this.view.render(false, '');  
40767     }
40768     
40769     
40770     this.fireEvent('render', this);
40771 };
40772
40773 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40774     
40775     cls : '',
40776     background : '',
40777     
40778     tabTip : '',
40779     
40780     iframe : false,
40781     iframeEl : false,
40782     
40783     /* Resize Element - use this to work out scroll etc. */
40784     resizeEl : false,
40785     
40786     setRegion : function(region){
40787         this.region = region;
40788         this.setActiveClass(region && !this.background);
40789     },
40790     
40791     
40792     setActiveClass: function(state)
40793     {
40794         if(state){
40795            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40796            this.el.setStyle('position','relative');
40797         }else{
40798            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40799            this.el.setStyle('position', 'absolute');
40800         } 
40801     },
40802     
40803     /**
40804      * Returns the toolbar for this Panel if one was configured. 
40805      * @return {Roo.Toolbar} 
40806      */
40807     getToolbar : function(){
40808         return this.toolbar;
40809     },
40810     
40811     setActiveState : function(active)
40812     {
40813         this.active = active;
40814         this.setActiveClass(active);
40815         if(!active){
40816             if(this.fireEvent("deactivate", this) === false){
40817                 return false;
40818             }
40819             return true;
40820         }
40821         this.fireEvent("activate", this);
40822         return true;
40823     },
40824     /**
40825      * Updates this panel's element (not for iframe)
40826      * @param {String} content The new content
40827      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40828     */
40829     setContent : function(content, loadScripts){
40830         if (this.iframe) {
40831             return;
40832         }
40833         
40834         this.el.update(content, loadScripts);
40835     },
40836
40837     ignoreResize : function(w, h){
40838         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40839             return true;
40840         }else{
40841             this.lastSize = {width: w, height: h};
40842             return false;
40843         }
40844     },
40845     /**
40846      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40847      * @return {Roo.UpdateManager} The UpdateManager
40848      */
40849     getUpdateManager : function(){
40850         if (this.iframe) {
40851             return false;
40852         }
40853         return this.el.getUpdateManager();
40854     },
40855      /**
40856      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40857      * Does not work with IFRAME contents
40858      * @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:
40859 <pre><code>
40860 panel.load({
40861     url: "your-url.php",
40862     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40863     callback: yourFunction,
40864     scope: yourObject, //(optional scope)
40865     discardUrl: false,
40866     nocache: false,
40867     text: "Loading...",
40868     timeout: 30,
40869     scripts: false
40870 });
40871 </code></pre>
40872      
40873      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40874      * 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.
40875      * @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}
40876      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40877      * @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.
40878      * @return {Roo.ContentPanel} this
40879      */
40880     load : function(){
40881         
40882         if (this.iframe) {
40883             return this;
40884         }
40885         
40886         var um = this.el.getUpdateManager();
40887         um.update.apply(um, arguments);
40888         return this;
40889     },
40890
40891
40892     /**
40893      * 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.
40894      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40895      * @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)
40896      * @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)
40897      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40898      */
40899     setUrl : function(url, params, loadOnce){
40900         if (this.iframe) {
40901             this.iframeEl.dom.src = url;
40902             return false;
40903         }
40904         
40905         if(this.refreshDelegate){
40906             this.removeListener("activate", this.refreshDelegate);
40907         }
40908         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40909         this.on("activate", this.refreshDelegate);
40910         return this.el.getUpdateManager();
40911     },
40912     
40913     _handleRefresh : function(url, params, loadOnce){
40914         if(!loadOnce || !this.loaded){
40915             var updater = this.el.getUpdateManager();
40916             updater.update(url, params, this._setLoaded.createDelegate(this));
40917         }
40918     },
40919     
40920     _setLoaded : function(){
40921         this.loaded = true;
40922     }, 
40923     
40924     /**
40925      * Returns this panel's id
40926      * @return {String} 
40927      */
40928     getId : function(){
40929         return this.el.id;
40930     },
40931     
40932     /** 
40933      * Returns this panel's element - used by regiosn to add.
40934      * @return {Roo.Element} 
40935      */
40936     getEl : function(){
40937         return this.wrapEl || this.el;
40938     },
40939     
40940    
40941     
40942     adjustForComponents : function(width, height)
40943     {
40944         //Roo.log('adjustForComponents ');
40945         if(this.resizeEl != this.el){
40946             width -= this.el.getFrameWidth('lr');
40947             height -= this.el.getFrameWidth('tb');
40948         }
40949         if(this.toolbar){
40950             var te = this.toolbar.getEl();
40951             te.setWidth(width);
40952             height -= te.getHeight();
40953         }
40954         if(this.footer){
40955             var te = this.footer.getEl();
40956             te.setWidth(width);
40957             height -= te.getHeight();
40958         }
40959         
40960         
40961         if(this.adjustments){
40962             width += this.adjustments[0];
40963             height += this.adjustments[1];
40964         }
40965         return {"width": width, "height": height};
40966     },
40967     
40968     setSize : function(width, height){
40969         if(this.fitToFrame && !this.ignoreResize(width, height)){
40970             if(this.fitContainer && this.resizeEl != this.el){
40971                 this.el.setSize(width, height);
40972             }
40973             var size = this.adjustForComponents(width, height);
40974             if (this.iframe) {
40975                 this.iframeEl.setSize(width,height);
40976             }
40977             
40978             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40979             this.fireEvent('resize', this, size.width, size.height);
40980             
40981             
40982         }
40983     },
40984     
40985     /**
40986      * Returns this panel's title
40987      * @return {String} 
40988      */
40989     getTitle : function(){
40990         
40991         if (typeof(this.title) != 'object') {
40992             return this.title;
40993         }
40994         
40995         var t = '';
40996         for (var k in this.title) {
40997             if (!this.title.hasOwnProperty(k)) {
40998                 continue;
40999             }
41000             
41001             if (k.indexOf('-') >= 0) {
41002                 var s = k.split('-');
41003                 for (var i = 0; i<s.length; i++) {
41004                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41005                 }
41006             } else {
41007                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41008             }
41009         }
41010         return t;
41011     },
41012     
41013     /**
41014      * Set this panel's title
41015      * @param {String} title
41016      */
41017     setTitle : function(title){
41018         this.title = title;
41019         if(this.region){
41020             this.region.updatePanelTitle(this, title);
41021         }
41022     },
41023     
41024     /**
41025      * Returns true is this panel was configured to be closable
41026      * @return {Boolean} 
41027      */
41028     isClosable : function(){
41029         return this.closable;
41030     },
41031     
41032     beforeSlide : function(){
41033         this.el.clip();
41034         this.resizeEl.clip();
41035     },
41036     
41037     afterSlide : function(){
41038         this.el.unclip();
41039         this.resizeEl.unclip();
41040     },
41041     
41042     /**
41043      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41044      *   Will fail silently if the {@link #setUrl} method has not been called.
41045      *   This does not activate the panel, just updates its content.
41046      */
41047     refresh : function(){
41048         if(this.refreshDelegate){
41049            this.loaded = false;
41050            this.refreshDelegate();
41051         }
41052     },
41053     
41054     /**
41055      * Destroys this panel
41056      */
41057     destroy : function(){
41058         this.el.removeAllListeners();
41059         var tempEl = document.createElement("span");
41060         tempEl.appendChild(this.el.dom);
41061         tempEl.innerHTML = "";
41062         this.el.remove();
41063         this.el = null;
41064     },
41065     
41066     /**
41067      * form - if the content panel contains a form - this is a reference to it.
41068      * @type {Roo.form.Form}
41069      */
41070     form : false,
41071     /**
41072      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41073      *    This contains a reference to it.
41074      * @type {Roo.View}
41075      */
41076     view : false,
41077     
41078       /**
41079      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41080      * <pre><code>
41081
41082 layout.addxtype({
41083        xtype : 'Form',
41084        items: [ .... ]
41085    }
41086 );
41087
41088 </code></pre>
41089      * @param {Object} cfg Xtype definition of item to add.
41090      */
41091     
41092     
41093     getChildContainer: function () {
41094         return this.getEl();
41095     },
41096     
41097     
41098     onScroll : function(e)
41099     {
41100         this.fireEvent('scroll', this, e);
41101     }
41102     
41103     
41104     /*
41105         var  ret = new Roo.factory(cfg);
41106         return ret;
41107         
41108         
41109         // add form..
41110         if (cfg.xtype.match(/^Form$/)) {
41111             
41112             var el;
41113             //if (this.footer) {
41114             //    el = this.footer.container.insertSibling(false, 'before');
41115             //} else {
41116                 el = this.el.createChild();
41117             //}
41118
41119             this.form = new  Roo.form.Form(cfg);
41120             
41121             
41122             if ( this.form.allItems.length) {
41123                 this.form.render(el.dom);
41124             }
41125             return this.form;
41126         }
41127         // should only have one of theses..
41128         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41129             // views.. should not be just added - used named prop 'view''
41130             
41131             cfg.el = this.el.appendChild(document.createElement("div"));
41132             // factory?
41133             
41134             var ret = new Roo.factory(cfg);
41135              
41136              ret.render && ret.render(false, ''); // render blank..
41137             this.view = ret;
41138             return ret;
41139         }
41140         return false;
41141     }
41142     \*/
41143 });
41144  
41145 /**
41146  * @class Roo.bootstrap.panel.Grid
41147  * @extends Roo.bootstrap.panel.Content
41148  * @constructor
41149  * Create a new GridPanel.
41150  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41151  * @param {Object} config A the config object
41152   
41153  */
41154
41155
41156
41157 Roo.bootstrap.panel.Grid = function(config)
41158 {
41159     
41160       
41161     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41162         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41163
41164     config.el = this.wrapper;
41165     //this.el = this.wrapper;
41166     
41167       if (config.container) {
41168         // ctor'ed from a Border/panel.grid
41169         
41170         
41171         this.wrapper.setStyle("overflow", "hidden");
41172         this.wrapper.addClass('roo-grid-container');
41173
41174     }
41175     
41176     
41177     if(config.toolbar){
41178         var tool_el = this.wrapper.createChild();    
41179         this.toolbar = Roo.factory(config.toolbar);
41180         var ti = [];
41181         if (config.toolbar.items) {
41182             ti = config.toolbar.items ;
41183             delete config.toolbar.items ;
41184         }
41185         
41186         var nitems = [];
41187         this.toolbar.render(tool_el);
41188         for(var i =0;i < ti.length;i++) {
41189           //  Roo.log(['add child', items[i]]);
41190             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41191         }
41192         this.toolbar.items = nitems;
41193         
41194         delete config.toolbar;
41195     }
41196     
41197     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41198     config.grid.scrollBody = true;;
41199     config.grid.monitorWindowResize = false; // turn off autosizing
41200     config.grid.autoHeight = false;
41201     config.grid.autoWidth = false;
41202     
41203     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41204     
41205     if (config.background) {
41206         // render grid on panel activation (if panel background)
41207         this.on('activate', function(gp) {
41208             if (!gp.grid.rendered) {
41209                 gp.grid.render(this.wrapper);
41210                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41211             }
41212         });
41213             
41214     } else {
41215         this.grid.render(this.wrapper);
41216         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41217
41218     }
41219     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41220     // ??? needed ??? config.el = this.wrapper;
41221     
41222     
41223     
41224   
41225     // xtype created footer. - not sure if will work as we normally have to render first..
41226     if (this.footer && !this.footer.el && this.footer.xtype) {
41227         
41228         var ctr = this.grid.getView().getFooterPanel(true);
41229         this.footer.dataSource = this.grid.dataSource;
41230         this.footer = Roo.factory(this.footer, Roo);
41231         this.footer.render(ctr);
41232         
41233     }
41234     
41235     
41236     
41237     
41238      
41239 };
41240
41241 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41242     getId : function(){
41243         return this.grid.id;
41244     },
41245     
41246     /**
41247      * Returns the grid for this panel
41248      * @return {Roo.bootstrap.Table} 
41249      */
41250     getGrid : function(){
41251         return this.grid;    
41252     },
41253     
41254     setSize : function(width, height){
41255         if(!this.ignoreResize(width, height)){
41256             var grid = this.grid;
41257             var size = this.adjustForComponents(width, height);
41258             // tfoot is not a footer?
41259           
41260             
41261             var gridel = grid.getGridEl();
41262             gridel.setSize(size.width, size.height);
41263             
41264             var tbd = grid.getGridEl().select('tbody', true).first();
41265             var thd = grid.getGridEl().select('thead',true).first();
41266             var tbf= grid.getGridEl().select('tfoot', true).first();
41267
41268             if (tbf) {
41269                 size.height -= tbf.getHeight();
41270             }
41271             if (thd) {
41272                 size.height -= thd.getHeight();
41273             }
41274             
41275             tbd.setSize(size.width, size.height );
41276             // this is for the account management tab -seems to work there.
41277             var thd = grid.getGridEl().select('thead',true).first();
41278             //if (tbd) {
41279             //    tbd.setSize(size.width, size.height - thd.getHeight());
41280             //}
41281              
41282             grid.autoSize();
41283         }
41284     },
41285      
41286     
41287     
41288     beforeSlide : function(){
41289         this.grid.getView().scroller.clip();
41290     },
41291     
41292     afterSlide : function(){
41293         this.grid.getView().scroller.unclip();
41294     },
41295     
41296     destroy : function(){
41297         this.grid.destroy();
41298         delete this.grid;
41299         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41300     }
41301 });
41302
41303 /**
41304  * @class Roo.bootstrap.panel.Nest
41305  * @extends Roo.bootstrap.panel.Content
41306  * @constructor
41307  * Create a new Panel, that can contain a layout.Border.
41308  * 
41309  * 
41310  * @param {Roo.BorderLayout} layout The layout for this panel
41311  * @param {String/Object} config A string to set only the title or a config object
41312  */
41313 Roo.bootstrap.panel.Nest = function(config)
41314 {
41315     // construct with only one argument..
41316     /* FIXME - implement nicer consturctors
41317     if (layout.layout) {
41318         config = layout;
41319         layout = config.layout;
41320         delete config.layout;
41321     }
41322     if (layout.xtype && !layout.getEl) {
41323         // then layout needs constructing..
41324         layout = Roo.factory(layout, Roo);
41325     }
41326     */
41327     
41328     config.el =  config.layout.getEl();
41329     
41330     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41331     
41332     config.layout.monitorWindowResize = false; // turn off autosizing
41333     this.layout = config.layout;
41334     this.layout.getEl().addClass("roo-layout-nested-layout");
41335     this.layout.parent = this;
41336     
41337     
41338     
41339     
41340 };
41341
41342 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41343
41344     setSize : function(width, height){
41345         if(!this.ignoreResize(width, height)){
41346             var size = this.adjustForComponents(width, height);
41347             var el = this.layout.getEl();
41348             if (size.height < 1) {
41349                 el.setWidth(size.width);   
41350             } else {
41351                 el.setSize(size.width, size.height);
41352             }
41353             var touch = el.dom.offsetWidth;
41354             this.layout.layout();
41355             // ie requires a double layout on the first pass
41356             if(Roo.isIE && !this.initialized){
41357                 this.initialized = true;
41358                 this.layout.layout();
41359             }
41360         }
41361     },
41362     
41363     // activate all subpanels if not currently active..
41364     
41365     setActiveState : function(active){
41366         this.active = active;
41367         this.setActiveClass(active);
41368         
41369         if(!active){
41370             this.fireEvent("deactivate", this);
41371             return;
41372         }
41373         
41374         this.fireEvent("activate", this);
41375         // not sure if this should happen before or after..
41376         if (!this.layout) {
41377             return; // should not happen..
41378         }
41379         var reg = false;
41380         for (var r in this.layout.regions) {
41381             reg = this.layout.getRegion(r);
41382             if (reg.getActivePanel()) {
41383                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41384                 reg.setActivePanel(reg.getActivePanel());
41385                 continue;
41386             }
41387             if (!reg.panels.length) {
41388                 continue;
41389             }
41390             reg.showPanel(reg.getPanel(0));
41391         }
41392         
41393         
41394         
41395         
41396     },
41397     
41398     /**
41399      * Returns the nested BorderLayout for this panel
41400      * @return {Roo.BorderLayout} 
41401      */
41402     getLayout : function(){
41403         return this.layout;
41404     },
41405     
41406      /**
41407      * Adds a xtype elements to the layout of the nested panel
41408      * <pre><code>
41409
41410 panel.addxtype({
41411        xtype : 'ContentPanel',
41412        region: 'west',
41413        items: [ .... ]
41414    }
41415 );
41416
41417 panel.addxtype({
41418         xtype : 'NestedLayoutPanel',
41419         region: 'west',
41420         layout: {
41421            center: { },
41422            west: { }   
41423         },
41424         items : [ ... list of content panels or nested layout panels.. ]
41425    }
41426 );
41427 </code></pre>
41428      * @param {Object} cfg Xtype definition of item to add.
41429      */
41430     addxtype : function(cfg) {
41431         return this.layout.addxtype(cfg);
41432     
41433     }
41434 });/*
41435  * Based on:
41436  * Ext JS Library 1.1.1
41437  * Copyright(c) 2006-2007, Ext JS, LLC.
41438  *
41439  * Originally Released Under LGPL - original licence link has changed is not relivant.
41440  *
41441  * Fork - LGPL
41442  * <script type="text/javascript">
41443  */
41444 /**
41445  * @class Roo.TabPanel
41446  * @extends Roo.util.Observable
41447  * A lightweight tab container.
41448  * <br><br>
41449  * Usage:
41450  * <pre><code>
41451 // basic tabs 1, built from existing content
41452 var tabs = new Roo.TabPanel("tabs1");
41453 tabs.addTab("script", "View Script");
41454 tabs.addTab("markup", "View Markup");
41455 tabs.activate("script");
41456
41457 // more advanced tabs, built from javascript
41458 var jtabs = new Roo.TabPanel("jtabs");
41459 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41460
41461 // set up the UpdateManager
41462 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41463 var updater = tab2.getUpdateManager();
41464 updater.setDefaultUrl("ajax1.htm");
41465 tab2.on('activate', updater.refresh, updater, true);
41466
41467 // Use setUrl for Ajax loading
41468 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41469 tab3.setUrl("ajax2.htm", null, true);
41470
41471 // Disabled tab
41472 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41473 tab4.disable();
41474
41475 jtabs.activate("jtabs-1");
41476  * </code></pre>
41477  * @constructor
41478  * Create a new TabPanel.
41479  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41480  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41481  */
41482 Roo.bootstrap.panel.Tabs = function(config){
41483     /**
41484     * The container element for this TabPanel.
41485     * @type Roo.Element
41486     */
41487     this.el = Roo.get(config.el);
41488     delete config.el;
41489     if(config){
41490         if(typeof config == "boolean"){
41491             this.tabPosition = config ? "bottom" : "top";
41492         }else{
41493             Roo.apply(this, config);
41494         }
41495     }
41496     
41497     if(this.tabPosition == "bottom"){
41498         // if tabs are at the bottom = create the body first.
41499         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41500         this.el.addClass("roo-tabs-bottom");
41501     }
41502     // next create the tabs holders
41503     
41504     if (this.tabPosition == "west"){
41505         
41506         var reg = this.region; // fake it..
41507         while (reg) {
41508             if (!reg.mgr.parent) {
41509                 break;
41510             }
41511             reg = reg.mgr.parent.region;
41512         }
41513         Roo.log("got nest?");
41514         Roo.log(reg);
41515         if (reg.mgr.getRegion('west')) {
41516             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41517             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41518             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41519             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41520             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41521         
41522             
41523         }
41524         
41525         
41526     } else {
41527      
41528         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41529         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41530         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41531         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41532     }
41533     
41534     
41535     if(Roo.isIE){
41536         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41537     }
41538     
41539     // finally - if tabs are at the top, then create the body last..
41540     if(this.tabPosition != "bottom"){
41541         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41542          * @type Roo.Element
41543          */
41544         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41545         this.el.addClass("roo-tabs-top");
41546     }
41547     this.items = [];
41548
41549     this.bodyEl.setStyle("position", "relative");
41550
41551     this.active = null;
41552     this.activateDelegate = this.activate.createDelegate(this);
41553
41554     this.addEvents({
41555         /**
41556          * @event tabchange
41557          * Fires when the active tab changes
41558          * @param {Roo.TabPanel} this
41559          * @param {Roo.TabPanelItem} activePanel The new active tab
41560          */
41561         "tabchange": true,
41562         /**
41563          * @event beforetabchange
41564          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41565          * @param {Roo.TabPanel} this
41566          * @param {Object} e Set cancel to true on this object to cancel the tab change
41567          * @param {Roo.TabPanelItem} tab The tab being changed to
41568          */
41569         "beforetabchange" : true
41570     });
41571
41572     Roo.EventManager.onWindowResize(this.onResize, this);
41573     this.cpad = this.el.getPadding("lr");
41574     this.hiddenCount = 0;
41575
41576
41577     // toolbar on the tabbar support...
41578     if (this.toolbar) {
41579         alert("no toolbar support yet");
41580         this.toolbar  = false;
41581         /*
41582         var tcfg = this.toolbar;
41583         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41584         this.toolbar = new Roo.Toolbar(tcfg);
41585         if (Roo.isSafari) {
41586             var tbl = tcfg.container.child('table', true);
41587             tbl.setAttribute('width', '100%');
41588         }
41589         */
41590         
41591     }
41592    
41593
41594
41595     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41596 };
41597
41598 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41599     /*
41600      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41601      */
41602     tabPosition : "top",
41603     /*
41604      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41605      */
41606     currentTabWidth : 0,
41607     /*
41608      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41609      */
41610     minTabWidth : 40,
41611     /*
41612      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41613      */
41614     maxTabWidth : 250,
41615     /*
41616      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41617      */
41618     preferredTabWidth : 175,
41619     /*
41620      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41621      */
41622     resizeTabs : false,
41623     /*
41624      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41625      */
41626     monitorResize : true,
41627     /*
41628      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41629      */
41630     toolbar : false,  // set by caller..
41631     
41632     region : false, /// set by caller
41633     
41634     disableTooltips : true, // not used yet...
41635
41636     /**
41637      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41638      * @param {String} id The id of the div to use <b>or create</b>
41639      * @param {String} text The text for the tab
41640      * @param {String} content (optional) Content to put in the TabPanelItem body
41641      * @param {Boolean} closable (optional) True to create a close icon on the tab
41642      * @return {Roo.TabPanelItem} The created TabPanelItem
41643      */
41644     addTab : function(id, text, content, closable, tpl)
41645     {
41646         var item = new Roo.bootstrap.panel.TabItem({
41647             panel: this,
41648             id : id,
41649             text : text,
41650             closable : closable,
41651             tpl : tpl
41652         });
41653         this.addTabItem(item);
41654         if(content){
41655             item.setContent(content);
41656         }
41657         return item;
41658     },
41659
41660     /**
41661      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41662      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41663      * @return {Roo.TabPanelItem}
41664      */
41665     getTab : function(id){
41666         return this.items[id];
41667     },
41668
41669     /**
41670      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41671      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41672      */
41673     hideTab : function(id){
41674         var t = this.items[id];
41675         if(!t.isHidden()){
41676            t.setHidden(true);
41677            this.hiddenCount++;
41678            this.autoSizeTabs();
41679         }
41680     },
41681
41682     /**
41683      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41684      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41685      */
41686     unhideTab : function(id){
41687         var t = this.items[id];
41688         if(t.isHidden()){
41689            t.setHidden(false);
41690            this.hiddenCount--;
41691            this.autoSizeTabs();
41692         }
41693     },
41694
41695     /**
41696      * Adds an existing {@link Roo.TabPanelItem}.
41697      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41698      */
41699     addTabItem : function(item)
41700     {
41701         this.items[item.id] = item;
41702         this.items.push(item);
41703         this.autoSizeTabs();
41704       //  if(this.resizeTabs){
41705     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41706   //         this.autoSizeTabs();
41707 //        }else{
41708 //            item.autoSize();
41709        // }
41710     },
41711
41712     /**
41713      * Removes a {@link Roo.TabPanelItem}.
41714      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41715      */
41716     removeTab : function(id){
41717         var items = this.items;
41718         var tab = items[id];
41719         if(!tab) { return; }
41720         var index = items.indexOf(tab);
41721         if(this.active == tab && items.length > 1){
41722             var newTab = this.getNextAvailable(index);
41723             if(newTab) {
41724                 newTab.activate();
41725             }
41726         }
41727         this.stripEl.dom.removeChild(tab.pnode.dom);
41728         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41729             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41730         }
41731         items.splice(index, 1);
41732         delete this.items[tab.id];
41733         tab.fireEvent("close", tab);
41734         tab.purgeListeners();
41735         this.autoSizeTabs();
41736     },
41737
41738     getNextAvailable : function(start){
41739         var items = this.items;
41740         var index = start;
41741         // look for a next tab that will slide over to
41742         // replace the one being removed
41743         while(index < items.length){
41744             var item = items[++index];
41745             if(item && !item.isHidden()){
41746                 return item;
41747             }
41748         }
41749         // if one isn't found select the previous tab (on the left)
41750         index = start;
41751         while(index >= 0){
41752             var item = items[--index];
41753             if(item && !item.isHidden()){
41754                 return item;
41755             }
41756         }
41757         return null;
41758     },
41759
41760     /**
41761      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41762      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41763      */
41764     disableTab : function(id){
41765         var tab = this.items[id];
41766         if(tab && this.active != tab){
41767             tab.disable();
41768         }
41769     },
41770
41771     /**
41772      * Enables a {@link Roo.TabPanelItem} that is disabled.
41773      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41774      */
41775     enableTab : function(id){
41776         var tab = this.items[id];
41777         tab.enable();
41778     },
41779
41780     /**
41781      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41782      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41783      * @return {Roo.TabPanelItem} The TabPanelItem.
41784      */
41785     activate : function(id)
41786     {
41787         //Roo.log('activite:'  + id);
41788         
41789         var tab = this.items[id];
41790         if(!tab){
41791             return null;
41792         }
41793         if(tab == this.active || tab.disabled){
41794             return tab;
41795         }
41796         var e = {};
41797         this.fireEvent("beforetabchange", this, e, tab);
41798         if(e.cancel !== true && !tab.disabled){
41799             if(this.active){
41800                 this.active.hide();
41801             }
41802             this.active = this.items[id];
41803             this.active.show();
41804             this.fireEvent("tabchange", this, this.active);
41805         }
41806         return tab;
41807     },
41808
41809     /**
41810      * Gets the active {@link Roo.TabPanelItem}.
41811      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41812      */
41813     getActiveTab : function(){
41814         return this.active;
41815     },
41816
41817     /**
41818      * Updates the tab body element to fit the height of the container element
41819      * for overflow scrolling
41820      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41821      */
41822     syncHeight : function(targetHeight){
41823         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41824         var bm = this.bodyEl.getMargins();
41825         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41826         this.bodyEl.setHeight(newHeight);
41827         return newHeight;
41828     },
41829
41830     onResize : function(){
41831         if(this.monitorResize){
41832             this.autoSizeTabs();
41833         }
41834     },
41835
41836     /**
41837      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41838      */
41839     beginUpdate : function(){
41840         this.updating = true;
41841     },
41842
41843     /**
41844      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41845      */
41846     endUpdate : function(){
41847         this.updating = false;
41848         this.autoSizeTabs();
41849     },
41850
41851     /**
41852      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41853      */
41854     autoSizeTabs : function()
41855     {
41856         var count = this.items.length;
41857         var vcount = count - this.hiddenCount;
41858         
41859         if (vcount < 2) {
41860             this.stripEl.hide();
41861         } else {
41862             this.stripEl.show();
41863         }
41864         
41865         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41866             return;
41867         }
41868         
41869         
41870         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41871         var availWidth = Math.floor(w / vcount);
41872         var b = this.stripBody;
41873         if(b.getWidth() > w){
41874             var tabs = this.items;
41875             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41876             if(availWidth < this.minTabWidth){
41877                 /*if(!this.sleft){    // incomplete scrolling code
41878                     this.createScrollButtons();
41879                 }
41880                 this.showScroll();
41881                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41882             }
41883         }else{
41884             if(this.currentTabWidth < this.preferredTabWidth){
41885                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41886             }
41887         }
41888     },
41889
41890     /**
41891      * Returns the number of tabs in this TabPanel.
41892      * @return {Number}
41893      */
41894      getCount : function(){
41895          return this.items.length;
41896      },
41897
41898     /**
41899      * Resizes all the tabs to the passed width
41900      * @param {Number} The new width
41901      */
41902     setTabWidth : function(width){
41903         this.currentTabWidth = width;
41904         for(var i = 0, len = this.items.length; i < len; i++) {
41905                 if(!this.items[i].isHidden()) {
41906                 this.items[i].setWidth(width);
41907             }
41908         }
41909     },
41910
41911     /**
41912      * Destroys this TabPanel
41913      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41914      */
41915     destroy : function(removeEl){
41916         Roo.EventManager.removeResizeListener(this.onResize, this);
41917         for(var i = 0, len = this.items.length; i < len; i++){
41918             this.items[i].purgeListeners();
41919         }
41920         if(removeEl === true){
41921             this.el.update("");
41922             this.el.remove();
41923         }
41924     },
41925     
41926     createStrip : function(container)
41927     {
41928         var strip = document.createElement("nav");
41929         strip.className = Roo.bootstrap.version == 4 ?
41930             "navbar-light bg-light" : 
41931             "navbar navbar-default"; //"x-tabs-wrap";
41932         container.appendChild(strip);
41933         return strip;
41934     },
41935     
41936     createStripList : function(strip)
41937     {
41938         // div wrapper for retard IE
41939         // returns the "tr" element.
41940         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41941         //'<div class="x-tabs-strip-wrap">'+
41942           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41943           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41944         return strip.firstChild; //.firstChild.firstChild.firstChild;
41945     },
41946     createBody : function(container)
41947     {
41948         var body = document.createElement("div");
41949         Roo.id(body, "tab-body");
41950         //Roo.fly(body).addClass("x-tabs-body");
41951         Roo.fly(body).addClass("tab-content");
41952         container.appendChild(body);
41953         return body;
41954     },
41955     createItemBody :function(bodyEl, id){
41956         var body = Roo.getDom(id);
41957         if(!body){
41958             body = document.createElement("div");
41959             body.id = id;
41960         }
41961         //Roo.fly(body).addClass("x-tabs-item-body");
41962         Roo.fly(body).addClass("tab-pane");
41963          bodyEl.insertBefore(body, bodyEl.firstChild);
41964         return body;
41965     },
41966     /** @private */
41967     createStripElements :  function(stripEl, text, closable, tpl)
41968     {
41969         var td = document.createElement("li"); // was td..
41970         td.className = 'nav-item';
41971         
41972         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41973         
41974         
41975         stripEl.appendChild(td);
41976         /*if(closable){
41977             td.className = "x-tabs-closable";
41978             if(!this.closeTpl){
41979                 this.closeTpl = new Roo.Template(
41980                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41981                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41982                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41983                 );
41984             }
41985             var el = this.closeTpl.overwrite(td, {"text": text});
41986             var close = el.getElementsByTagName("div")[0];
41987             var inner = el.getElementsByTagName("em")[0];
41988             return {"el": el, "close": close, "inner": inner};
41989         } else {
41990         */
41991         // not sure what this is..
41992 //            if(!this.tabTpl){
41993                 //this.tabTpl = new Roo.Template(
41994                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41995                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41996                 //);
41997 //                this.tabTpl = new Roo.Template(
41998 //                   '<a href="#">' +
41999 //                   '<span unselectable="on"' +
42000 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42001 //                            ' >{text}</span></a>'
42002 //                );
42003 //                
42004 //            }
42005
42006
42007             var template = tpl || this.tabTpl || false;
42008             
42009             if(!template){
42010                 template =  new Roo.Template(
42011                         Roo.bootstrap.version == 4 ? 
42012                             (
42013                                 '<a class="nav-link" href="#" unselectable="on"' +
42014                                      (this.disableTooltips ? '' : ' title="{text}"') +
42015                                      ' >{text}</a>'
42016                             ) : (
42017                                 '<a class="nav-link" href="#">' +
42018                                 '<span unselectable="on"' +
42019                                          (this.disableTooltips ? '' : ' title="{text}"') +
42020                                     ' >{text}</span></a>'
42021                             )
42022                 );
42023             }
42024             
42025             switch (typeof(template)) {
42026                 case 'object' :
42027                     break;
42028                 case 'string' :
42029                     template = new Roo.Template(template);
42030                     break;
42031                 default :
42032                     break;
42033             }
42034             
42035             var el = template.overwrite(td, {"text": text});
42036             
42037             var inner = el.getElementsByTagName("span")[0];
42038             
42039             return {"el": el, "inner": inner};
42040             
42041     }
42042         
42043     
42044 });
42045
42046 /**
42047  * @class Roo.TabPanelItem
42048  * @extends Roo.util.Observable
42049  * Represents an individual item (tab plus body) in a TabPanel.
42050  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42051  * @param {String} id The id of this TabPanelItem
42052  * @param {String} text The text for the tab of this TabPanelItem
42053  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42054  */
42055 Roo.bootstrap.panel.TabItem = function(config){
42056     /**
42057      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42058      * @type Roo.TabPanel
42059      */
42060     this.tabPanel = config.panel;
42061     /**
42062      * The id for this TabPanelItem
42063      * @type String
42064      */
42065     this.id = config.id;
42066     /** @private */
42067     this.disabled = false;
42068     /** @private */
42069     this.text = config.text;
42070     /** @private */
42071     this.loaded = false;
42072     this.closable = config.closable;
42073
42074     /**
42075      * The body element for this TabPanelItem.
42076      * @type Roo.Element
42077      */
42078     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42079     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42080     this.bodyEl.setStyle("display", "block");
42081     this.bodyEl.setStyle("zoom", "1");
42082     //this.hideAction();
42083
42084     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42085     /** @private */
42086     this.el = Roo.get(els.el);
42087     this.inner = Roo.get(els.inner, true);
42088      this.textEl = Roo.bootstrap.version == 4 ?
42089         this.el : Roo.get(this.el.dom.firstChild, true);
42090
42091     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42092     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42093
42094     
42095 //    this.el.on("mousedown", this.onTabMouseDown, this);
42096     this.el.on("click", this.onTabClick, this);
42097     /** @private */
42098     if(config.closable){
42099         var c = Roo.get(els.close, true);
42100         c.dom.title = this.closeText;
42101         c.addClassOnOver("close-over");
42102         c.on("click", this.closeClick, this);
42103      }
42104
42105     this.addEvents({
42106          /**
42107          * @event activate
42108          * Fires when this tab becomes the active tab.
42109          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42110          * @param {Roo.TabPanelItem} this
42111          */
42112         "activate": true,
42113         /**
42114          * @event beforeclose
42115          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42116          * @param {Roo.TabPanelItem} this
42117          * @param {Object} e Set cancel to true on this object to cancel the close.
42118          */
42119         "beforeclose": true,
42120         /**
42121          * @event close
42122          * Fires when this tab is closed.
42123          * @param {Roo.TabPanelItem} this
42124          */
42125          "close": true,
42126         /**
42127          * @event deactivate
42128          * Fires when this tab is no longer the active tab.
42129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42130          * @param {Roo.TabPanelItem} this
42131          */
42132          "deactivate" : true
42133     });
42134     this.hidden = false;
42135
42136     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42137 };
42138
42139 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42140            {
42141     purgeListeners : function(){
42142        Roo.util.Observable.prototype.purgeListeners.call(this);
42143        this.el.removeAllListeners();
42144     },
42145     /**
42146      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42147      */
42148     show : function(){
42149         this.status_node.addClass("active");
42150         this.showAction();
42151         if(Roo.isOpera){
42152             this.tabPanel.stripWrap.repaint();
42153         }
42154         this.fireEvent("activate", this.tabPanel, this);
42155     },
42156
42157     /**
42158      * Returns true if this tab is the active tab.
42159      * @return {Boolean}
42160      */
42161     isActive : function(){
42162         return this.tabPanel.getActiveTab() == this;
42163     },
42164
42165     /**
42166      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42167      */
42168     hide : function(){
42169         this.status_node.removeClass("active");
42170         this.hideAction();
42171         this.fireEvent("deactivate", this.tabPanel, this);
42172     },
42173
42174     hideAction : function(){
42175         this.bodyEl.hide();
42176         this.bodyEl.setStyle("position", "absolute");
42177         this.bodyEl.setLeft("-20000px");
42178         this.bodyEl.setTop("-20000px");
42179     },
42180
42181     showAction : function(){
42182         this.bodyEl.setStyle("position", "relative");
42183         this.bodyEl.setTop("");
42184         this.bodyEl.setLeft("");
42185         this.bodyEl.show();
42186     },
42187
42188     /**
42189      * Set the tooltip for the tab.
42190      * @param {String} tooltip The tab's tooltip
42191      */
42192     setTooltip : function(text){
42193         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42194             this.textEl.dom.qtip = text;
42195             this.textEl.dom.removeAttribute('title');
42196         }else{
42197             this.textEl.dom.title = text;
42198         }
42199     },
42200
42201     onTabClick : function(e){
42202         e.preventDefault();
42203         this.tabPanel.activate(this.id);
42204     },
42205
42206     onTabMouseDown : function(e){
42207         e.preventDefault();
42208         this.tabPanel.activate(this.id);
42209     },
42210 /*
42211     getWidth : function(){
42212         return this.inner.getWidth();
42213     },
42214
42215     setWidth : function(width){
42216         var iwidth = width - this.linode.getPadding("lr");
42217         this.inner.setWidth(iwidth);
42218         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42219         this.linode.setWidth(width);
42220     },
42221 */
42222     /**
42223      * Show or hide the tab
42224      * @param {Boolean} hidden True to hide or false to show.
42225      */
42226     setHidden : function(hidden){
42227         this.hidden = hidden;
42228         this.linode.setStyle("display", hidden ? "none" : "");
42229     },
42230
42231     /**
42232      * Returns true if this tab is "hidden"
42233      * @return {Boolean}
42234      */
42235     isHidden : function(){
42236         return this.hidden;
42237     },
42238
42239     /**
42240      * Returns the text for this tab
42241      * @return {String}
42242      */
42243     getText : function(){
42244         return this.text;
42245     },
42246     /*
42247     autoSize : function(){
42248         //this.el.beginMeasure();
42249         this.textEl.setWidth(1);
42250         /*
42251          *  #2804 [new] Tabs in Roojs
42252          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42253          */
42254         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42255         //this.el.endMeasure();
42256     //},
42257
42258     /**
42259      * Sets the text for the tab (Note: this also sets the tooltip text)
42260      * @param {String} text The tab's text and tooltip
42261      */
42262     setText : function(text){
42263         this.text = text;
42264         this.textEl.update(text);
42265         this.setTooltip(text);
42266         //if(!this.tabPanel.resizeTabs){
42267         //    this.autoSize();
42268         //}
42269     },
42270     /**
42271      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42272      */
42273     activate : function(){
42274         this.tabPanel.activate(this.id);
42275     },
42276
42277     /**
42278      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42279      */
42280     disable : function(){
42281         if(this.tabPanel.active != this){
42282             this.disabled = true;
42283             this.status_node.addClass("disabled");
42284         }
42285     },
42286
42287     /**
42288      * Enables this TabPanelItem if it was previously disabled.
42289      */
42290     enable : function(){
42291         this.disabled = false;
42292         this.status_node.removeClass("disabled");
42293     },
42294
42295     /**
42296      * Sets the content for this TabPanelItem.
42297      * @param {String} content The content
42298      * @param {Boolean} loadScripts true to look for and load scripts
42299      */
42300     setContent : function(content, loadScripts){
42301         this.bodyEl.update(content, loadScripts);
42302     },
42303
42304     /**
42305      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42306      * @return {Roo.UpdateManager} The UpdateManager
42307      */
42308     getUpdateManager : function(){
42309         return this.bodyEl.getUpdateManager();
42310     },
42311
42312     /**
42313      * Set a URL to be used to load the content for this TabPanelItem.
42314      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42315      * @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)
42316      * @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)
42317      * @return {Roo.UpdateManager} The UpdateManager
42318      */
42319     setUrl : function(url, params, loadOnce){
42320         if(this.refreshDelegate){
42321             this.un('activate', this.refreshDelegate);
42322         }
42323         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42324         this.on("activate", this.refreshDelegate);
42325         return this.bodyEl.getUpdateManager();
42326     },
42327
42328     /** @private */
42329     _handleRefresh : function(url, params, loadOnce){
42330         if(!loadOnce || !this.loaded){
42331             var updater = this.bodyEl.getUpdateManager();
42332             updater.update(url, params, this._setLoaded.createDelegate(this));
42333         }
42334     },
42335
42336     /**
42337      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42338      *   Will fail silently if the setUrl method has not been called.
42339      *   This does not activate the panel, just updates its content.
42340      */
42341     refresh : function(){
42342         if(this.refreshDelegate){
42343            this.loaded = false;
42344            this.refreshDelegate();
42345         }
42346     },
42347
42348     /** @private */
42349     _setLoaded : function(){
42350         this.loaded = true;
42351     },
42352
42353     /** @private */
42354     closeClick : function(e){
42355         var o = {};
42356         e.stopEvent();
42357         this.fireEvent("beforeclose", this, o);
42358         if(o.cancel !== true){
42359             this.tabPanel.removeTab(this.id);
42360         }
42361     },
42362     /**
42363      * The text displayed in the tooltip for the close icon.
42364      * @type String
42365      */
42366     closeText : "Close this tab"
42367 });
42368 /**
42369 *    This script refer to:
42370 *    Title: International Telephone Input
42371 *    Author: Jack O'Connor
42372 *    Code version:  v12.1.12
42373 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42374 **/
42375
42376 Roo.bootstrap.PhoneInputData = function() {
42377     var d = [
42378       [
42379         "Afghanistan (‫افغانستان‬‎)",
42380         "af",
42381         "93"
42382       ],
42383       [
42384         "Albania (Shqipëri)",
42385         "al",
42386         "355"
42387       ],
42388       [
42389         "Algeria (‫الجزائر‬‎)",
42390         "dz",
42391         "213"
42392       ],
42393       [
42394         "American Samoa",
42395         "as",
42396         "1684"
42397       ],
42398       [
42399         "Andorra",
42400         "ad",
42401         "376"
42402       ],
42403       [
42404         "Angola",
42405         "ao",
42406         "244"
42407       ],
42408       [
42409         "Anguilla",
42410         "ai",
42411         "1264"
42412       ],
42413       [
42414         "Antigua and Barbuda",
42415         "ag",
42416         "1268"
42417       ],
42418       [
42419         "Argentina",
42420         "ar",
42421         "54"
42422       ],
42423       [
42424         "Armenia (Հայաստան)",
42425         "am",
42426         "374"
42427       ],
42428       [
42429         "Aruba",
42430         "aw",
42431         "297"
42432       ],
42433       [
42434         "Australia",
42435         "au",
42436         "61",
42437         0
42438       ],
42439       [
42440         "Austria (Österreich)",
42441         "at",
42442         "43"
42443       ],
42444       [
42445         "Azerbaijan (Azərbaycan)",
42446         "az",
42447         "994"
42448       ],
42449       [
42450         "Bahamas",
42451         "bs",
42452         "1242"
42453       ],
42454       [
42455         "Bahrain (‫البحرين‬‎)",
42456         "bh",
42457         "973"
42458       ],
42459       [
42460         "Bangladesh (বাংলাদেশ)",
42461         "bd",
42462         "880"
42463       ],
42464       [
42465         "Barbados",
42466         "bb",
42467         "1246"
42468       ],
42469       [
42470         "Belarus (Беларусь)",
42471         "by",
42472         "375"
42473       ],
42474       [
42475         "Belgium (België)",
42476         "be",
42477         "32"
42478       ],
42479       [
42480         "Belize",
42481         "bz",
42482         "501"
42483       ],
42484       [
42485         "Benin (Bénin)",
42486         "bj",
42487         "229"
42488       ],
42489       [
42490         "Bermuda",
42491         "bm",
42492         "1441"
42493       ],
42494       [
42495         "Bhutan (འབྲུག)",
42496         "bt",
42497         "975"
42498       ],
42499       [
42500         "Bolivia",
42501         "bo",
42502         "591"
42503       ],
42504       [
42505         "Bosnia and Herzegovina (Босна и Херцеговина)",
42506         "ba",
42507         "387"
42508       ],
42509       [
42510         "Botswana",
42511         "bw",
42512         "267"
42513       ],
42514       [
42515         "Brazil (Brasil)",
42516         "br",
42517         "55"
42518       ],
42519       [
42520         "British Indian Ocean Territory",
42521         "io",
42522         "246"
42523       ],
42524       [
42525         "British Virgin Islands",
42526         "vg",
42527         "1284"
42528       ],
42529       [
42530         "Brunei",
42531         "bn",
42532         "673"
42533       ],
42534       [
42535         "Bulgaria (България)",
42536         "bg",
42537         "359"
42538       ],
42539       [
42540         "Burkina Faso",
42541         "bf",
42542         "226"
42543       ],
42544       [
42545         "Burundi (Uburundi)",
42546         "bi",
42547         "257"
42548       ],
42549       [
42550         "Cambodia (កម្ពុជា)",
42551         "kh",
42552         "855"
42553       ],
42554       [
42555         "Cameroon (Cameroun)",
42556         "cm",
42557         "237"
42558       ],
42559       [
42560         "Canada",
42561         "ca",
42562         "1",
42563         1,
42564         ["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"]
42565       ],
42566       [
42567         "Cape Verde (Kabu Verdi)",
42568         "cv",
42569         "238"
42570       ],
42571       [
42572         "Caribbean Netherlands",
42573         "bq",
42574         "599",
42575         1
42576       ],
42577       [
42578         "Cayman Islands",
42579         "ky",
42580         "1345"
42581       ],
42582       [
42583         "Central African Republic (République centrafricaine)",
42584         "cf",
42585         "236"
42586       ],
42587       [
42588         "Chad (Tchad)",
42589         "td",
42590         "235"
42591       ],
42592       [
42593         "Chile",
42594         "cl",
42595         "56"
42596       ],
42597       [
42598         "China (中国)",
42599         "cn",
42600         "86"
42601       ],
42602       [
42603         "Christmas Island",
42604         "cx",
42605         "61",
42606         2
42607       ],
42608       [
42609         "Cocos (Keeling) Islands",
42610         "cc",
42611         "61",
42612         1
42613       ],
42614       [
42615         "Colombia",
42616         "co",
42617         "57"
42618       ],
42619       [
42620         "Comoros (‫جزر القمر‬‎)",
42621         "km",
42622         "269"
42623       ],
42624       [
42625         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42626         "cd",
42627         "243"
42628       ],
42629       [
42630         "Congo (Republic) (Congo-Brazzaville)",
42631         "cg",
42632         "242"
42633       ],
42634       [
42635         "Cook Islands",
42636         "ck",
42637         "682"
42638       ],
42639       [
42640         "Costa Rica",
42641         "cr",
42642         "506"
42643       ],
42644       [
42645         "Côte d’Ivoire",
42646         "ci",
42647         "225"
42648       ],
42649       [
42650         "Croatia (Hrvatska)",
42651         "hr",
42652         "385"
42653       ],
42654       [
42655         "Cuba",
42656         "cu",
42657         "53"
42658       ],
42659       [
42660         "Curaçao",
42661         "cw",
42662         "599",
42663         0
42664       ],
42665       [
42666         "Cyprus (Κύπρος)",
42667         "cy",
42668         "357"
42669       ],
42670       [
42671         "Czech Republic (Česká republika)",
42672         "cz",
42673         "420"
42674       ],
42675       [
42676         "Denmark (Danmark)",
42677         "dk",
42678         "45"
42679       ],
42680       [
42681         "Djibouti",
42682         "dj",
42683         "253"
42684       ],
42685       [
42686         "Dominica",
42687         "dm",
42688         "1767"
42689       ],
42690       [
42691         "Dominican Republic (República Dominicana)",
42692         "do",
42693         "1",
42694         2,
42695         ["809", "829", "849"]
42696       ],
42697       [
42698         "Ecuador",
42699         "ec",
42700         "593"
42701       ],
42702       [
42703         "Egypt (‫مصر‬‎)",
42704         "eg",
42705         "20"
42706       ],
42707       [
42708         "El Salvador",
42709         "sv",
42710         "503"
42711       ],
42712       [
42713         "Equatorial Guinea (Guinea Ecuatorial)",
42714         "gq",
42715         "240"
42716       ],
42717       [
42718         "Eritrea",
42719         "er",
42720         "291"
42721       ],
42722       [
42723         "Estonia (Eesti)",
42724         "ee",
42725         "372"
42726       ],
42727       [
42728         "Ethiopia",
42729         "et",
42730         "251"
42731       ],
42732       [
42733         "Falkland Islands (Islas Malvinas)",
42734         "fk",
42735         "500"
42736       ],
42737       [
42738         "Faroe Islands (Føroyar)",
42739         "fo",
42740         "298"
42741       ],
42742       [
42743         "Fiji",
42744         "fj",
42745         "679"
42746       ],
42747       [
42748         "Finland (Suomi)",
42749         "fi",
42750         "358",
42751         0
42752       ],
42753       [
42754         "France",
42755         "fr",
42756         "33"
42757       ],
42758       [
42759         "French Guiana (Guyane française)",
42760         "gf",
42761         "594"
42762       ],
42763       [
42764         "French Polynesia (Polynésie française)",
42765         "pf",
42766         "689"
42767       ],
42768       [
42769         "Gabon",
42770         "ga",
42771         "241"
42772       ],
42773       [
42774         "Gambia",
42775         "gm",
42776         "220"
42777       ],
42778       [
42779         "Georgia (საქართველო)",
42780         "ge",
42781         "995"
42782       ],
42783       [
42784         "Germany (Deutschland)",
42785         "de",
42786         "49"
42787       ],
42788       [
42789         "Ghana (Gaana)",
42790         "gh",
42791         "233"
42792       ],
42793       [
42794         "Gibraltar",
42795         "gi",
42796         "350"
42797       ],
42798       [
42799         "Greece (Ελλάδα)",
42800         "gr",
42801         "30"
42802       ],
42803       [
42804         "Greenland (Kalaallit Nunaat)",
42805         "gl",
42806         "299"
42807       ],
42808       [
42809         "Grenada",
42810         "gd",
42811         "1473"
42812       ],
42813       [
42814         "Guadeloupe",
42815         "gp",
42816         "590",
42817         0
42818       ],
42819       [
42820         "Guam",
42821         "gu",
42822         "1671"
42823       ],
42824       [
42825         "Guatemala",
42826         "gt",
42827         "502"
42828       ],
42829       [
42830         "Guernsey",
42831         "gg",
42832         "44",
42833         1
42834       ],
42835       [
42836         "Guinea (Guinée)",
42837         "gn",
42838         "224"
42839       ],
42840       [
42841         "Guinea-Bissau (Guiné Bissau)",
42842         "gw",
42843         "245"
42844       ],
42845       [
42846         "Guyana",
42847         "gy",
42848         "592"
42849       ],
42850       [
42851         "Haiti",
42852         "ht",
42853         "509"
42854       ],
42855       [
42856         "Honduras",
42857         "hn",
42858         "504"
42859       ],
42860       [
42861         "Hong Kong (香港)",
42862         "hk",
42863         "852"
42864       ],
42865       [
42866         "Hungary (Magyarország)",
42867         "hu",
42868         "36"
42869       ],
42870       [
42871         "Iceland (Ísland)",
42872         "is",
42873         "354"
42874       ],
42875       [
42876         "India (भारत)",
42877         "in",
42878         "91"
42879       ],
42880       [
42881         "Indonesia",
42882         "id",
42883         "62"
42884       ],
42885       [
42886         "Iran (‫ایران‬‎)",
42887         "ir",
42888         "98"
42889       ],
42890       [
42891         "Iraq (‫العراق‬‎)",
42892         "iq",
42893         "964"
42894       ],
42895       [
42896         "Ireland",
42897         "ie",
42898         "353"
42899       ],
42900       [
42901         "Isle of Man",
42902         "im",
42903         "44",
42904         2
42905       ],
42906       [
42907         "Israel (‫ישראל‬‎)",
42908         "il",
42909         "972"
42910       ],
42911       [
42912         "Italy (Italia)",
42913         "it",
42914         "39",
42915         0
42916       ],
42917       [
42918         "Jamaica",
42919         "jm",
42920         "1876"
42921       ],
42922       [
42923         "Japan (日本)",
42924         "jp",
42925         "81"
42926       ],
42927       [
42928         "Jersey",
42929         "je",
42930         "44",
42931         3
42932       ],
42933       [
42934         "Jordan (‫الأردن‬‎)",
42935         "jo",
42936         "962"
42937       ],
42938       [
42939         "Kazakhstan (Казахстан)",
42940         "kz",
42941         "7",
42942         1
42943       ],
42944       [
42945         "Kenya",
42946         "ke",
42947         "254"
42948       ],
42949       [
42950         "Kiribati",
42951         "ki",
42952         "686"
42953       ],
42954       [
42955         "Kosovo",
42956         "xk",
42957         "383"
42958       ],
42959       [
42960         "Kuwait (‫الكويت‬‎)",
42961         "kw",
42962         "965"
42963       ],
42964       [
42965         "Kyrgyzstan (Кыргызстан)",
42966         "kg",
42967         "996"
42968       ],
42969       [
42970         "Laos (ລາວ)",
42971         "la",
42972         "856"
42973       ],
42974       [
42975         "Latvia (Latvija)",
42976         "lv",
42977         "371"
42978       ],
42979       [
42980         "Lebanon (‫لبنان‬‎)",
42981         "lb",
42982         "961"
42983       ],
42984       [
42985         "Lesotho",
42986         "ls",
42987         "266"
42988       ],
42989       [
42990         "Liberia",
42991         "lr",
42992         "231"
42993       ],
42994       [
42995         "Libya (‫ليبيا‬‎)",
42996         "ly",
42997         "218"
42998       ],
42999       [
43000         "Liechtenstein",
43001         "li",
43002         "423"
43003       ],
43004       [
43005         "Lithuania (Lietuva)",
43006         "lt",
43007         "370"
43008       ],
43009       [
43010         "Luxembourg",
43011         "lu",
43012         "352"
43013       ],
43014       [
43015         "Macau (澳門)",
43016         "mo",
43017         "853"
43018       ],
43019       [
43020         "Macedonia (FYROM) (Македонија)",
43021         "mk",
43022         "389"
43023       ],
43024       [
43025         "Madagascar (Madagasikara)",
43026         "mg",
43027         "261"
43028       ],
43029       [
43030         "Malawi",
43031         "mw",
43032         "265"
43033       ],
43034       [
43035         "Malaysia",
43036         "my",
43037         "60"
43038       ],
43039       [
43040         "Maldives",
43041         "mv",
43042         "960"
43043       ],
43044       [
43045         "Mali",
43046         "ml",
43047         "223"
43048       ],
43049       [
43050         "Malta",
43051         "mt",
43052         "356"
43053       ],
43054       [
43055         "Marshall Islands",
43056         "mh",
43057         "692"
43058       ],
43059       [
43060         "Martinique",
43061         "mq",
43062         "596"
43063       ],
43064       [
43065         "Mauritania (‫موريتانيا‬‎)",
43066         "mr",
43067         "222"
43068       ],
43069       [
43070         "Mauritius (Moris)",
43071         "mu",
43072         "230"
43073       ],
43074       [
43075         "Mayotte",
43076         "yt",
43077         "262",
43078         1
43079       ],
43080       [
43081         "Mexico (México)",
43082         "mx",
43083         "52"
43084       ],
43085       [
43086         "Micronesia",
43087         "fm",
43088         "691"
43089       ],
43090       [
43091         "Moldova (Republica Moldova)",
43092         "md",
43093         "373"
43094       ],
43095       [
43096         "Monaco",
43097         "mc",
43098         "377"
43099       ],
43100       [
43101         "Mongolia (Монгол)",
43102         "mn",
43103         "976"
43104       ],
43105       [
43106         "Montenegro (Crna Gora)",
43107         "me",
43108         "382"
43109       ],
43110       [
43111         "Montserrat",
43112         "ms",
43113         "1664"
43114       ],
43115       [
43116         "Morocco (‫المغرب‬‎)",
43117         "ma",
43118         "212",
43119         0
43120       ],
43121       [
43122         "Mozambique (Moçambique)",
43123         "mz",
43124         "258"
43125       ],
43126       [
43127         "Myanmar (Burma) (မြန်မာ)",
43128         "mm",
43129         "95"
43130       ],
43131       [
43132         "Namibia (Namibië)",
43133         "na",
43134         "264"
43135       ],
43136       [
43137         "Nauru",
43138         "nr",
43139         "674"
43140       ],
43141       [
43142         "Nepal (नेपाल)",
43143         "np",
43144         "977"
43145       ],
43146       [
43147         "Netherlands (Nederland)",
43148         "nl",
43149         "31"
43150       ],
43151       [
43152         "New Caledonia (Nouvelle-Calédonie)",
43153         "nc",
43154         "687"
43155       ],
43156       [
43157         "New Zealand",
43158         "nz",
43159         "64"
43160       ],
43161       [
43162         "Nicaragua",
43163         "ni",
43164         "505"
43165       ],
43166       [
43167         "Niger (Nijar)",
43168         "ne",
43169         "227"
43170       ],
43171       [
43172         "Nigeria",
43173         "ng",
43174         "234"
43175       ],
43176       [
43177         "Niue",
43178         "nu",
43179         "683"
43180       ],
43181       [
43182         "Norfolk Island",
43183         "nf",
43184         "672"
43185       ],
43186       [
43187         "North Korea (조선 민주주의 인민 공화국)",
43188         "kp",
43189         "850"
43190       ],
43191       [
43192         "Northern Mariana Islands",
43193         "mp",
43194         "1670"
43195       ],
43196       [
43197         "Norway (Norge)",
43198         "no",
43199         "47",
43200         0
43201       ],
43202       [
43203         "Oman (‫عُمان‬‎)",
43204         "om",
43205         "968"
43206       ],
43207       [
43208         "Pakistan (‫پاکستان‬‎)",
43209         "pk",
43210         "92"
43211       ],
43212       [
43213         "Palau",
43214         "pw",
43215         "680"
43216       ],
43217       [
43218         "Palestine (‫فلسطين‬‎)",
43219         "ps",
43220         "970"
43221       ],
43222       [
43223         "Panama (Panamá)",
43224         "pa",
43225         "507"
43226       ],
43227       [
43228         "Papua New Guinea",
43229         "pg",
43230         "675"
43231       ],
43232       [
43233         "Paraguay",
43234         "py",
43235         "595"
43236       ],
43237       [
43238         "Peru (Perú)",
43239         "pe",
43240         "51"
43241       ],
43242       [
43243         "Philippines",
43244         "ph",
43245         "63"
43246       ],
43247       [
43248         "Poland (Polska)",
43249         "pl",
43250         "48"
43251       ],
43252       [
43253         "Portugal",
43254         "pt",
43255         "351"
43256       ],
43257       [
43258         "Puerto Rico",
43259         "pr",
43260         "1",
43261         3,
43262         ["787", "939"]
43263       ],
43264       [
43265         "Qatar (‫قطر‬‎)",
43266         "qa",
43267         "974"
43268       ],
43269       [
43270         "Réunion (La Réunion)",
43271         "re",
43272         "262",
43273         0
43274       ],
43275       [
43276         "Romania (România)",
43277         "ro",
43278         "40"
43279       ],
43280       [
43281         "Russia (Россия)",
43282         "ru",
43283         "7",
43284         0
43285       ],
43286       [
43287         "Rwanda",
43288         "rw",
43289         "250"
43290       ],
43291       [
43292         "Saint Barthélemy",
43293         "bl",
43294         "590",
43295         1
43296       ],
43297       [
43298         "Saint Helena",
43299         "sh",
43300         "290"
43301       ],
43302       [
43303         "Saint Kitts and Nevis",
43304         "kn",
43305         "1869"
43306       ],
43307       [
43308         "Saint Lucia",
43309         "lc",
43310         "1758"
43311       ],
43312       [
43313         "Saint Martin (Saint-Martin (partie française))",
43314         "mf",
43315         "590",
43316         2
43317       ],
43318       [
43319         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43320         "pm",
43321         "508"
43322       ],
43323       [
43324         "Saint Vincent and the Grenadines",
43325         "vc",
43326         "1784"
43327       ],
43328       [
43329         "Samoa",
43330         "ws",
43331         "685"
43332       ],
43333       [
43334         "San Marino",
43335         "sm",
43336         "378"
43337       ],
43338       [
43339         "São Tomé and Príncipe (São Tomé e Príncipe)",
43340         "st",
43341         "239"
43342       ],
43343       [
43344         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43345         "sa",
43346         "966"
43347       ],
43348       [
43349         "Senegal (Sénégal)",
43350         "sn",
43351         "221"
43352       ],
43353       [
43354         "Serbia (Србија)",
43355         "rs",
43356         "381"
43357       ],
43358       [
43359         "Seychelles",
43360         "sc",
43361         "248"
43362       ],
43363       [
43364         "Sierra Leone",
43365         "sl",
43366         "232"
43367       ],
43368       [
43369         "Singapore",
43370         "sg",
43371         "65"
43372       ],
43373       [
43374         "Sint Maarten",
43375         "sx",
43376         "1721"
43377       ],
43378       [
43379         "Slovakia (Slovensko)",
43380         "sk",
43381         "421"
43382       ],
43383       [
43384         "Slovenia (Slovenija)",
43385         "si",
43386         "386"
43387       ],
43388       [
43389         "Solomon Islands",
43390         "sb",
43391         "677"
43392       ],
43393       [
43394         "Somalia (Soomaaliya)",
43395         "so",
43396         "252"
43397       ],
43398       [
43399         "South Africa",
43400         "za",
43401         "27"
43402       ],
43403       [
43404         "South Korea (대한민국)",
43405         "kr",
43406         "82"
43407       ],
43408       [
43409         "South Sudan (‫جنوب السودان‬‎)",
43410         "ss",
43411         "211"
43412       ],
43413       [
43414         "Spain (España)",
43415         "es",
43416         "34"
43417       ],
43418       [
43419         "Sri Lanka (ශ්‍රී ලංකාව)",
43420         "lk",
43421         "94"
43422       ],
43423       [
43424         "Sudan (‫السودان‬‎)",
43425         "sd",
43426         "249"
43427       ],
43428       [
43429         "Suriname",
43430         "sr",
43431         "597"
43432       ],
43433       [
43434         "Svalbard and Jan Mayen",
43435         "sj",
43436         "47",
43437         1
43438       ],
43439       [
43440         "Swaziland",
43441         "sz",
43442         "268"
43443       ],
43444       [
43445         "Sweden (Sverige)",
43446         "se",
43447         "46"
43448       ],
43449       [
43450         "Switzerland (Schweiz)",
43451         "ch",
43452         "41"
43453       ],
43454       [
43455         "Syria (‫سوريا‬‎)",
43456         "sy",
43457         "963"
43458       ],
43459       [
43460         "Taiwan (台灣)",
43461         "tw",
43462         "886"
43463       ],
43464       [
43465         "Tajikistan",
43466         "tj",
43467         "992"
43468       ],
43469       [
43470         "Tanzania",
43471         "tz",
43472         "255"
43473       ],
43474       [
43475         "Thailand (ไทย)",
43476         "th",
43477         "66"
43478       ],
43479       [
43480         "Timor-Leste",
43481         "tl",
43482         "670"
43483       ],
43484       [
43485         "Togo",
43486         "tg",
43487         "228"
43488       ],
43489       [
43490         "Tokelau",
43491         "tk",
43492         "690"
43493       ],
43494       [
43495         "Tonga",
43496         "to",
43497         "676"
43498       ],
43499       [
43500         "Trinidad and Tobago",
43501         "tt",
43502         "1868"
43503       ],
43504       [
43505         "Tunisia (‫تونس‬‎)",
43506         "tn",
43507         "216"
43508       ],
43509       [
43510         "Turkey (Türkiye)",
43511         "tr",
43512         "90"
43513       ],
43514       [
43515         "Turkmenistan",
43516         "tm",
43517         "993"
43518       ],
43519       [
43520         "Turks and Caicos Islands",
43521         "tc",
43522         "1649"
43523       ],
43524       [
43525         "Tuvalu",
43526         "tv",
43527         "688"
43528       ],
43529       [
43530         "U.S. Virgin Islands",
43531         "vi",
43532         "1340"
43533       ],
43534       [
43535         "Uganda",
43536         "ug",
43537         "256"
43538       ],
43539       [
43540         "Ukraine (Україна)",
43541         "ua",
43542         "380"
43543       ],
43544       [
43545         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43546         "ae",
43547         "971"
43548       ],
43549       [
43550         "United Kingdom",
43551         "gb",
43552         "44",
43553         0
43554       ],
43555       [
43556         "United States",
43557         "us",
43558         "1",
43559         0
43560       ],
43561       [
43562         "Uruguay",
43563         "uy",
43564         "598"
43565       ],
43566       [
43567         "Uzbekistan (Oʻzbekiston)",
43568         "uz",
43569         "998"
43570       ],
43571       [
43572         "Vanuatu",
43573         "vu",
43574         "678"
43575       ],
43576       [
43577         "Vatican City (Città del Vaticano)",
43578         "va",
43579         "39",
43580         1
43581       ],
43582       [
43583         "Venezuela",
43584         "ve",
43585         "58"
43586       ],
43587       [
43588         "Vietnam (Việt Nam)",
43589         "vn",
43590         "84"
43591       ],
43592       [
43593         "Wallis and Futuna (Wallis-et-Futuna)",
43594         "wf",
43595         "681"
43596       ],
43597       [
43598         "Western Sahara (‫الصحراء الغربية‬‎)",
43599         "eh",
43600         "212",
43601         1
43602       ],
43603       [
43604         "Yemen (‫اليمن‬‎)",
43605         "ye",
43606         "967"
43607       ],
43608       [
43609         "Zambia",
43610         "zm",
43611         "260"
43612       ],
43613       [
43614         "Zimbabwe",
43615         "zw",
43616         "263"
43617       ],
43618       [
43619         "Åland Islands",
43620         "ax",
43621         "358",
43622         1
43623       ]
43624   ];
43625   
43626   return d;
43627 }/**
43628 *    This script refer to:
43629 *    Title: International Telephone Input
43630 *    Author: Jack O'Connor
43631 *    Code version:  v12.1.12
43632 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43633 **/
43634
43635 /**
43636  * @class Roo.bootstrap.PhoneInput
43637  * @extends Roo.bootstrap.TriggerField
43638  * An input with International dial-code selection
43639  
43640  * @cfg {String} defaultDialCode default '+852'
43641  * @cfg {Array} preferedCountries default []
43642   
43643  * @constructor
43644  * Create a new PhoneInput.
43645  * @param {Object} config Configuration options
43646  */
43647
43648 Roo.bootstrap.PhoneInput = function(config) {
43649     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43650 };
43651
43652 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43653         /**
43654         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43655         */
43656         listWidth: undefined,
43657         
43658         selectedClass: 'active',
43659         
43660         invalidClass : "has-warning",
43661         
43662         validClass: 'has-success',
43663         
43664         allowed: '0123456789',
43665         
43666         max_length: 15,
43667         
43668         /**
43669          * @cfg {String} defaultDialCode The default dial code when initializing the input
43670          */
43671         defaultDialCode: '+852',
43672         
43673         /**
43674          * @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
43675          */
43676         preferedCountries: false,
43677         
43678         getAutoCreate : function()
43679         {
43680             var data = Roo.bootstrap.PhoneInputData();
43681             var align = this.labelAlign || this.parentLabelAlign();
43682             var id = Roo.id();
43683             
43684             this.allCountries = [];
43685             this.dialCodeMapping = [];
43686             
43687             for (var i = 0; i < data.length; i++) {
43688               var c = data[i];
43689               this.allCountries[i] = {
43690                 name: c[0],
43691                 iso2: c[1],
43692                 dialCode: c[2],
43693                 priority: c[3] || 0,
43694                 areaCodes: c[4] || null
43695               };
43696               this.dialCodeMapping[c[2]] = {
43697                   name: c[0],
43698                   iso2: c[1],
43699                   priority: c[3] || 0,
43700                   areaCodes: c[4] || null
43701               };
43702             }
43703             
43704             var cfg = {
43705                 cls: 'form-group',
43706                 cn: []
43707             };
43708             
43709             var input =  {
43710                 tag: 'input',
43711                 id : id,
43712                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43713                 maxlength: this.max_length,
43714                 cls : 'form-control tel-input',
43715                 autocomplete: 'new-password'
43716             };
43717             
43718             var hiddenInput = {
43719                 tag: 'input',
43720                 type: 'hidden',
43721                 cls: 'hidden-tel-input'
43722             };
43723             
43724             if (this.name) {
43725                 hiddenInput.name = this.name;
43726             }
43727             
43728             if (this.disabled) {
43729                 input.disabled = true;
43730             }
43731             
43732             var flag_container = {
43733                 tag: 'div',
43734                 cls: 'flag-box',
43735                 cn: [
43736                     {
43737                         tag: 'div',
43738                         cls: 'flag'
43739                     },
43740                     {
43741                         tag: 'div',
43742                         cls: 'caret'
43743                     }
43744                 ]
43745             };
43746             
43747             var box = {
43748                 tag: 'div',
43749                 cls: this.hasFeedback ? 'has-feedback' : '',
43750                 cn: [
43751                     hiddenInput,
43752                     input,
43753                     {
43754                         tag: 'input',
43755                         cls: 'dial-code-holder',
43756                         disabled: true
43757                     }
43758                 ]
43759             };
43760             
43761             var container = {
43762                 cls: 'roo-select2-container input-group',
43763                 cn: [
43764                     flag_container,
43765                     box
43766                 ]
43767             };
43768             
43769             if (this.fieldLabel.length) {
43770                 var indicator = {
43771                     tag: 'i',
43772                     tooltip: 'This field is required'
43773                 };
43774                 
43775                 var label = {
43776                     tag: 'label',
43777                     'for':  id,
43778                     cls: 'control-label',
43779                     cn: []
43780                 };
43781                 
43782                 var label_text = {
43783                     tag: 'span',
43784                     html: this.fieldLabel
43785                 };
43786                 
43787                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43788                 label.cn = [
43789                     indicator,
43790                     label_text
43791                 ];
43792                 
43793                 if(this.indicatorpos == 'right') {
43794                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43795                     label.cn = [
43796                         label_text,
43797                         indicator
43798                     ];
43799                 }
43800                 
43801                 if(align == 'left') {
43802                     container = {
43803                         tag: 'div',
43804                         cn: [
43805                             container
43806                         ]
43807                     };
43808                     
43809                     if(this.labelWidth > 12){
43810                         label.style = "width: " + this.labelWidth + 'px';
43811                     }
43812                     if(this.labelWidth < 13 && this.labelmd == 0){
43813                         this.labelmd = this.labelWidth;
43814                     }
43815                     if(this.labellg > 0){
43816                         label.cls += ' col-lg-' + this.labellg;
43817                         input.cls += ' col-lg-' + (12 - this.labellg);
43818                     }
43819                     if(this.labelmd > 0){
43820                         label.cls += ' col-md-' + this.labelmd;
43821                         container.cls += ' col-md-' + (12 - this.labelmd);
43822                     }
43823                     if(this.labelsm > 0){
43824                         label.cls += ' col-sm-' + this.labelsm;
43825                         container.cls += ' col-sm-' + (12 - this.labelsm);
43826                     }
43827                     if(this.labelxs > 0){
43828                         label.cls += ' col-xs-' + this.labelxs;
43829                         container.cls += ' col-xs-' + (12 - this.labelxs);
43830                     }
43831                 }
43832             }
43833             
43834             cfg.cn = [
43835                 label,
43836                 container
43837             ];
43838             
43839             var settings = this;
43840             
43841             ['xs','sm','md','lg'].map(function(size){
43842                 if (settings[size]) {
43843                     cfg.cls += ' col-' + size + '-' + settings[size];
43844                 }
43845             });
43846             
43847             this.store = new Roo.data.Store({
43848                 proxy : new Roo.data.MemoryProxy({}),
43849                 reader : new Roo.data.JsonReader({
43850                     fields : [
43851                         {
43852                             'name' : 'name',
43853                             'type' : 'string'
43854                         },
43855                         {
43856                             'name' : 'iso2',
43857                             'type' : 'string'
43858                         },
43859                         {
43860                             'name' : 'dialCode',
43861                             'type' : 'string'
43862                         },
43863                         {
43864                             'name' : 'priority',
43865                             'type' : 'string'
43866                         },
43867                         {
43868                             'name' : 'areaCodes',
43869                             'type' : 'string'
43870                         }
43871                     ]
43872                 })
43873             });
43874             
43875             if(!this.preferedCountries) {
43876                 this.preferedCountries = [
43877                     'hk',
43878                     'gb',
43879                     'us'
43880                 ];
43881             }
43882             
43883             var p = this.preferedCountries.reverse();
43884             
43885             if(p) {
43886                 for (var i = 0; i < p.length; i++) {
43887                     for (var j = 0; j < this.allCountries.length; j++) {
43888                         if(this.allCountries[j].iso2 == p[i]) {
43889                             var t = this.allCountries[j];
43890                             this.allCountries.splice(j,1);
43891                             this.allCountries.unshift(t);
43892                         }
43893                     } 
43894                 }
43895             }
43896             
43897             this.store.proxy.data = {
43898                 success: true,
43899                 data: this.allCountries
43900             };
43901             
43902             return cfg;
43903         },
43904         
43905         initEvents : function()
43906         {
43907             this.createList();
43908             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43909             
43910             this.indicator = this.indicatorEl();
43911             this.flag = this.flagEl();
43912             this.dialCodeHolder = this.dialCodeHolderEl();
43913             
43914             this.trigger = this.el.select('div.flag-box',true).first();
43915             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43916             
43917             var _this = this;
43918             
43919             (function(){
43920                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43921                 _this.list.setWidth(lw);
43922             }).defer(100);
43923             
43924             this.list.on('mouseover', this.onViewOver, this);
43925             this.list.on('mousemove', this.onViewMove, this);
43926             this.inputEl().on("keyup", this.onKeyUp, this);
43927             this.inputEl().on("keypress", this.onKeyPress, this);
43928             
43929             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43930
43931             this.view = new Roo.View(this.list, this.tpl, {
43932                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43933             });
43934             
43935             this.view.on('click', this.onViewClick, this);
43936             this.setValue(this.defaultDialCode);
43937         },
43938         
43939         onTriggerClick : function(e)
43940         {
43941             Roo.log('trigger click');
43942             if(this.disabled){
43943                 return;
43944             }
43945             
43946             if(this.isExpanded()){
43947                 this.collapse();
43948                 this.hasFocus = false;
43949             }else {
43950                 this.store.load({});
43951                 this.hasFocus = true;
43952                 this.expand();
43953             }
43954         },
43955         
43956         isExpanded : function()
43957         {
43958             return this.list.isVisible();
43959         },
43960         
43961         collapse : function()
43962         {
43963             if(!this.isExpanded()){
43964                 return;
43965             }
43966             this.list.hide();
43967             Roo.get(document).un('mousedown', this.collapseIf, this);
43968             Roo.get(document).un('mousewheel', this.collapseIf, this);
43969             this.fireEvent('collapse', this);
43970             this.validate();
43971         },
43972         
43973         expand : function()
43974         {
43975             Roo.log('expand');
43976
43977             if(this.isExpanded() || !this.hasFocus){
43978                 return;
43979             }
43980             
43981             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43982             this.list.setWidth(lw);
43983             
43984             this.list.show();
43985             this.restrictHeight();
43986             
43987             Roo.get(document).on('mousedown', this.collapseIf, this);
43988             Roo.get(document).on('mousewheel', this.collapseIf, this);
43989             
43990             this.fireEvent('expand', this);
43991         },
43992         
43993         restrictHeight : function()
43994         {
43995             this.list.alignTo(this.inputEl(), this.listAlign);
43996             this.list.alignTo(this.inputEl(), this.listAlign);
43997         },
43998         
43999         onViewOver : function(e, t)
44000         {
44001             if(this.inKeyMode){
44002                 return;
44003             }
44004             var item = this.view.findItemFromChild(t);
44005             
44006             if(item){
44007                 var index = this.view.indexOf(item);
44008                 this.select(index, false);
44009             }
44010         },
44011
44012         // private
44013         onViewClick : function(view, doFocus, el, e)
44014         {
44015             var index = this.view.getSelectedIndexes()[0];
44016             
44017             var r = this.store.getAt(index);
44018             
44019             if(r){
44020                 this.onSelect(r, index);
44021             }
44022             if(doFocus !== false && !this.blockFocus){
44023                 this.inputEl().focus();
44024             }
44025         },
44026         
44027         onViewMove : function(e, t)
44028         {
44029             this.inKeyMode = false;
44030         },
44031         
44032         select : function(index, scrollIntoView)
44033         {
44034             this.selectedIndex = index;
44035             this.view.select(index);
44036             if(scrollIntoView !== false){
44037                 var el = this.view.getNode(index);
44038                 if(el){
44039                     this.list.scrollChildIntoView(el, false);
44040                 }
44041             }
44042         },
44043         
44044         createList : function()
44045         {
44046             this.list = Roo.get(document.body).createChild({
44047                 tag: 'ul',
44048                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44049                 style: 'display:none'
44050             });
44051             
44052             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44053         },
44054         
44055         collapseIf : function(e)
44056         {
44057             var in_combo  = e.within(this.el);
44058             var in_list =  e.within(this.list);
44059             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44060             
44061             if (in_combo || in_list || is_list) {
44062                 return;
44063             }
44064             this.collapse();
44065         },
44066         
44067         onSelect : function(record, index)
44068         {
44069             if(this.fireEvent('beforeselect', this, record, index) !== false){
44070                 
44071                 this.setFlagClass(record.data.iso2);
44072                 this.setDialCode(record.data.dialCode);
44073                 this.hasFocus = false;
44074                 this.collapse();
44075                 this.fireEvent('select', this, record, index);
44076             }
44077         },
44078         
44079         flagEl : function()
44080         {
44081             var flag = this.el.select('div.flag',true).first();
44082             if(!flag){
44083                 return false;
44084             }
44085             return flag;
44086         },
44087         
44088         dialCodeHolderEl : function()
44089         {
44090             var d = this.el.select('input.dial-code-holder',true).first();
44091             if(!d){
44092                 return false;
44093             }
44094             return d;
44095         },
44096         
44097         setDialCode : function(v)
44098         {
44099             this.dialCodeHolder.dom.value = '+'+v;
44100         },
44101         
44102         setFlagClass : function(n)
44103         {
44104             this.flag.dom.className = 'flag '+n;
44105         },
44106         
44107         getValue : function()
44108         {
44109             var v = this.inputEl().getValue();
44110             if(this.dialCodeHolder) {
44111                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44112             }
44113             return v;
44114         },
44115         
44116         setValue : function(v)
44117         {
44118             var d = this.getDialCode(v);
44119             
44120             //invalid dial code
44121             if(v.length == 0 || !d || d.length == 0) {
44122                 if(this.rendered){
44123                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44124                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44125                 }
44126                 return;
44127             }
44128             
44129             //valid dial code
44130             this.setFlagClass(this.dialCodeMapping[d].iso2);
44131             this.setDialCode(d);
44132             this.inputEl().dom.value = v.replace('+'+d,'');
44133             this.hiddenEl().dom.value = this.getValue();
44134             
44135             this.validate();
44136         },
44137         
44138         getDialCode : function(v)
44139         {
44140             v = v ||  '';
44141             
44142             if (v.length == 0) {
44143                 return this.dialCodeHolder.dom.value;
44144             }
44145             
44146             var dialCode = "";
44147             if (v.charAt(0) != "+") {
44148                 return false;
44149             }
44150             var numericChars = "";
44151             for (var i = 1; i < v.length; i++) {
44152               var c = v.charAt(i);
44153               if (!isNaN(c)) {
44154                 numericChars += c;
44155                 if (this.dialCodeMapping[numericChars]) {
44156                   dialCode = v.substr(1, i);
44157                 }
44158                 if (numericChars.length == 4) {
44159                   break;
44160                 }
44161               }
44162             }
44163             return dialCode;
44164         },
44165         
44166         reset : function()
44167         {
44168             this.setValue(this.defaultDialCode);
44169             this.validate();
44170         },
44171         
44172         hiddenEl : function()
44173         {
44174             return this.el.select('input.hidden-tel-input',true).first();
44175         },
44176         
44177         // after setting val
44178         onKeyUp : function(e){
44179             this.setValue(this.getValue());
44180         },
44181         
44182         onKeyPress : function(e){
44183             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44184                 e.stopEvent();
44185             }
44186         }
44187         
44188 });
44189 /**
44190  * @class Roo.bootstrap.MoneyField
44191  * @extends Roo.bootstrap.ComboBox
44192  * Bootstrap MoneyField class
44193  * 
44194  * @constructor
44195  * Create a new MoneyField.
44196  * @param {Object} config Configuration options
44197  */
44198
44199 Roo.bootstrap.MoneyField = function(config) {
44200     
44201     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44202     
44203 };
44204
44205 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44206     
44207     /**
44208      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44209      */
44210     allowDecimals : true,
44211     /**
44212      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44213      */
44214     decimalSeparator : ".",
44215     /**
44216      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44217      */
44218     decimalPrecision : 0,
44219     /**
44220      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44221      */
44222     allowNegative : true,
44223     /**
44224      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44225      */
44226     allowZero: true,
44227     /**
44228      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44229      */
44230     minValue : Number.NEGATIVE_INFINITY,
44231     /**
44232      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44233      */
44234     maxValue : Number.MAX_VALUE,
44235     /**
44236      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44237      */
44238     minText : "The minimum value for this field is {0}",
44239     /**
44240      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44241      */
44242     maxText : "The maximum value for this field is {0}",
44243     /**
44244      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44245      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44246      */
44247     nanText : "{0} is not a valid number",
44248     /**
44249      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44250      */
44251     castInt : true,
44252     /**
44253      * @cfg {String} defaults currency of the MoneyField
44254      * value should be in lkey
44255      */
44256     defaultCurrency : false,
44257     /**
44258      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44259      */
44260     thousandsDelimiter : false,
44261     /**
44262      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44263      */
44264     max_length: false,
44265     
44266     inputlg : 9,
44267     inputmd : 9,
44268     inputsm : 9,
44269     inputxs : 6,
44270     
44271     store : false,
44272     
44273     getAutoCreate : function()
44274     {
44275         var align = this.labelAlign || this.parentLabelAlign();
44276         
44277         var id = Roo.id();
44278
44279         var cfg = {
44280             cls: 'form-group',
44281             cn: []
44282         };
44283
44284         var input =  {
44285             tag: 'input',
44286             id : id,
44287             cls : 'form-control roo-money-amount-input',
44288             autocomplete: 'new-password'
44289         };
44290         
44291         var hiddenInput = {
44292             tag: 'input',
44293             type: 'hidden',
44294             id: Roo.id(),
44295             cls: 'hidden-number-input'
44296         };
44297         
44298         if(this.max_length) {
44299             input.maxlength = this.max_length; 
44300         }
44301         
44302         if (this.name) {
44303             hiddenInput.name = this.name;
44304         }
44305
44306         if (this.disabled) {
44307             input.disabled = true;
44308         }
44309
44310         var clg = 12 - this.inputlg;
44311         var cmd = 12 - this.inputmd;
44312         var csm = 12 - this.inputsm;
44313         var cxs = 12 - this.inputxs;
44314         
44315         var container = {
44316             tag : 'div',
44317             cls : 'row roo-money-field',
44318             cn : [
44319                 {
44320                     tag : 'div',
44321                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44322                     cn : [
44323                         {
44324                             tag : 'div',
44325                             cls: 'roo-select2-container input-group',
44326                             cn: [
44327                                 {
44328                                     tag : 'input',
44329                                     cls : 'form-control roo-money-currency-input',
44330                                     autocomplete: 'new-password',
44331                                     readOnly : 1,
44332                                     name : this.currencyName
44333                                 },
44334                                 {
44335                                     tag :'span',
44336                                     cls : 'input-group-addon',
44337                                     cn : [
44338                                         {
44339                                             tag: 'span',
44340                                             cls: 'caret'
44341                                         }
44342                                     ]
44343                                 }
44344                             ]
44345                         }
44346                     ]
44347                 },
44348                 {
44349                     tag : 'div',
44350                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44351                     cn : [
44352                         {
44353                             tag: 'div',
44354                             cls: this.hasFeedback ? 'has-feedback' : '',
44355                             cn: [
44356                                 input
44357                             ]
44358                         }
44359                     ]
44360                 }
44361             ]
44362             
44363         };
44364         
44365         if (this.fieldLabel.length) {
44366             var indicator = {
44367                 tag: 'i',
44368                 tooltip: 'This field is required'
44369             };
44370
44371             var label = {
44372                 tag: 'label',
44373                 'for':  id,
44374                 cls: 'control-label',
44375                 cn: []
44376             };
44377
44378             var label_text = {
44379                 tag: 'span',
44380                 html: this.fieldLabel
44381             };
44382
44383             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44384             label.cn = [
44385                 indicator,
44386                 label_text
44387             ];
44388
44389             if(this.indicatorpos == 'right') {
44390                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44391                 label.cn = [
44392                     label_text,
44393                     indicator
44394                 ];
44395             }
44396
44397             if(align == 'left') {
44398                 container = {
44399                     tag: 'div',
44400                     cn: [
44401                         container
44402                     ]
44403                 };
44404
44405                 if(this.labelWidth > 12){
44406                     label.style = "width: " + this.labelWidth + 'px';
44407                 }
44408                 if(this.labelWidth < 13 && this.labelmd == 0){
44409                     this.labelmd = this.labelWidth;
44410                 }
44411                 if(this.labellg > 0){
44412                     label.cls += ' col-lg-' + this.labellg;
44413                     input.cls += ' col-lg-' + (12 - this.labellg);
44414                 }
44415                 if(this.labelmd > 0){
44416                     label.cls += ' col-md-' + this.labelmd;
44417                     container.cls += ' col-md-' + (12 - this.labelmd);
44418                 }
44419                 if(this.labelsm > 0){
44420                     label.cls += ' col-sm-' + this.labelsm;
44421                     container.cls += ' col-sm-' + (12 - this.labelsm);
44422                 }
44423                 if(this.labelxs > 0){
44424                     label.cls += ' col-xs-' + this.labelxs;
44425                     container.cls += ' col-xs-' + (12 - this.labelxs);
44426                 }
44427             }
44428         }
44429
44430         cfg.cn = [
44431             label,
44432             container,
44433             hiddenInput
44434         ];
44435         
44436         var settings = this;
44437
44438         ['xs','sm','md','lg'].map(function(size){
44439             if (settings[size]) {
44440                 cfg.cls += ' col-' + size + '-' + settings[size];
44441             }
44442         });
44443         
44444         return cfg;
44445     },
44446     
44447     initEvents : function()
44448     {
44449         this.indicator = this.indicatorEl();
44450         
44451         this.initCurrencyEvent();
44452         
44453         this.initNumberEvent();
44454     },
44455     
44456     initCurrencyEvent : function()
44457     {
44458         if (!this.store) {
44459             throw "can not find store for combo";
44460         }
44461         
44462         this.store = Roo.factory(this.store, Roo.data);
44463         this.store.parent = this;
44464         
44465         this.createList();
44466         
44467         this.triggerEl = this.el.select('.input-group-addon', true).first();
44468         
44469         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44470         
44471         var _this = this;
44472         
44473         (function(){
44474             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44475             _this.list.setWidth(lw);
44476         }).defer(100);
44477         
44478         this.list.on('mouseover', this.onViewOver, this);
44479         this.list.on('mousemove', this.onViewMove, this);
44480         this.list.on('scroll', this.onViewScroll, this);
44481         
44482         if(!this.tpl){
44483             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44484         }
44485         
44486         this.view = new Roo.View(this.list, this.tpl, {
44487             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44488         });
44489         
44490         this.view.on('click', this.onViewClick, this);
44491         
44492         this.store.on('beforeload', this.onBeforeLoad, this);
44493         this.store.on('load', this.onLoad, this);
44494         this.store.on('loadexception', this.onLoadException, this);
44495         
44496         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44497             "up" : function(e){
44498                 this.inKeyMode = true;
44499                 this.selectPrev();
44500             },
44501
44502             "down" : function(e){
44503                 if(!this.isExpanded()){
44504                     this.onTriggerClick();
44505                 }else{
44506                     this.inKeyMode = true;
44507                     this.selectNext();
44508                 }
44509             },
44510
44511             "enter" : function(e){
44512                 this.collapse();
44513                 
44514                 if(this.fireEvent("specialkey", this, e)){
44515                     this.onViewClick(false);
44516                 }
44517                 
44518                 return true;
44519             },
44520
44521             "esc" : function(e){
44522                 this.collapse();
44523             },
44524
44525             "tab" : function(e){
44526                 this.collapse();
44527                 
44528                 if(this.fireEvent("specialkey", this, e)){
44529                     this.onViewClick(false);
44530                 }
44531                 
44532                 return true;
44533             },
44534
44535             scope : this,
44536
44537             doRelay : function(foo, bar, hname){
44538                 if(hname == 'down' || this.scope.isExpanded()){
44539                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44540                 }
44541                 return true;
44542             },
44543
44544             forceKeyDown: true
44545         });
44546         
44547         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44548         
44549     },
44550     
44551     initNumberEvent : function(e)
44552     {
44553         this.inputEl().on("keydown" , this.fireKey,  this);
44554         this.inputEl().on("focus", this.onFocus,  this);
44555         this.inputEl().on("blur", this.onBlur,  this);
44556         
44557         this.inputEl().relayEvent('keyup', this);
44558         
44559         if(this.indicator){
44560             this.indicator.addClass('invisible');
44561         }
44562  
44563         this.originalValue = this.getValue();
44564         
44565         if(this.validationEvent == 'keyup'){
44566             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44567             this.inputEl().on('keyup', this.filterValidation, this);
44568         }
44569         else if(this.validationEvent !== false){
44570             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44571         }
44572         
44573         if(this.selectOnFocus){
44574             this.on("focus", this.preFocus, this);
44575             
44576         }
44577         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44578             this.inputEl().on("keypress", this.filterKeys, this);
44579         } else {
44580             this.inputEl().relayEvent('keypress', this);
44581         }
44582         
44583         var allowed = "0123456789";
44584         
44585         if(this.allowDecimals){
44586             allowed += this.decimalSeparator;
44587         }
44588         
44589         if(this.allowNegative){
44590             allowed += "-";
44591         }
44592         
44593         if(this.thousandsDelimiter) {
44594             allowed += ",";
44595         }
44596         
44597         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44598         
44599         var keyPress = function(e){
44600             
44601             var k = e.getKey();
44602             
44603             var c = e.getCharCode();
44604             
44605             if(
44606                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44607                     allowed.indexOf(String.fromCharCode(c)) === -1
44608             ){
44609                 e.stopEvent();
44610                 return;
44611             }
44612             
44613             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44614                 return;
44615             }
44616             
44617             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44618                 e.stopEvent();
44619             }
44620         };
44621         
44622         this.inputEl().on("keypress", keyPress, this);
44623         
44624     },
44625     
44626     onTriggerClick : function(e)
44627     {   
44628         if(this.disabled){
44629             return;
44630         }
44631         
44632         this.page = 0;
44633         this.loadNext = false;
44634         
44635         if(this.isExpanded()){
44636             this.collapse();
44637             return;
44638         }
44639         
44640         this.hasFocus = true;
44641         
44642         if(this.triggerAction == 'all') {
44643             this.doQuery(this.allQuery, true);
44644             return;
44645         }
44646         
44647         this.doQuery(this.getRawValue());
44648     },
44649     
44650     getCurrency : function()
44651     {   
44652         var v = this.currencyEl().getValue();
44653         
44654         return v;
44655     },
44656     
44657     restrictHeight : function()
44658     {
44659         this.list.alignTo(this.currencyEl(), this.listAlign);
44660         this.list.alignTo(this.currencyEl(), this.listAlign);
44661     },
44662     
44663     onViewClick : function(view, doFocus, el, e)
44664     {
44665         var index = this.view.getSelectedIndexes()[0];
44666         
44667         var r = this.store.getAt(index);
44668         
44669         if(r){
44670             this.onSelect(r, index);
44671         }
44672     },
44673     
44674     onSelect : function(record, index){
44675         
44676         if(this.fireEvent('beforeselect', this, record, index) !== false){
44677         
44678             this.setFromCurrencyData(index > -1 ? record.data : false);
44679             
44680             this.collapse();
44681             
44682             this.fireEvent('select', this, record, index);
44683         }
44684     },
44685     
44686     setFromCurrencyData : function(o)
44687     {
44688         var currency = '';
44689         
44690         this.lastCurrency = o;
44691         
44692         if (this.currencyField) {
44693             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44694         } else {
44695             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44696         }
44697         
44698         this.lastSelectionText = currency;
44699         
44700         //setting default currency
44701         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44702             this.setCurrency(this.defaultCurrency);
44703             return;
44704         }
44705         
44706         this.setCurrency(currency);
44707     },
44708     
44709     setFromData : function(o)
44710     {
44711         var c = {};
44712         
44713         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44714         
44715         this.setFromCurrencyData(c);
44716         
44717         var value = '';
44718         
44719         if (this.name) {
44720             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44721         } else {
44722             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44723         }
44724         
44725         this.setValue(value);
44726         
44727     },
44728     
44729     setCurrency : function(v)
44730     {   
44731         this.currencyValue = v;
44732         
44733         if(this.rendered){
44734             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44735             this.validate();
44736         }
44737     },
44738     
44739     setValue : function(v)
44740     {
44741         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44742         
44743         this.value = v;
44744         
44745         if(this.rendered){
44746             
44747             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44748             
44749             this.inputEl().dom.value = (v == '') ? '' :
44750                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44751             
44752             if(!this.allowZero && v === '0') {
44753                 this.hiddenEl().dom.value = '';
44754                 this.inputEl().dom.value = '';
44755             }
44756             
44757             this.validate();
44758         }
44759     },
44760     
44761     getRawValue : function()
44762     {
44763         var v = this.inputEl().getValue();
44764         
44765         return v;
44766     },
44767     
44768     getValue : function()
44769     {
44770         return this.fixPrecision(this.parseValue(this.getRawValue()));
44771     },
44772     
44773     parseValue : function(value)
44774     {
44775         if(this.thousandsDelimiter) {
44776             value += "";
44777             r = new RegExp(",", "g");
44778             value = value.replace(r, "");
44779         }
44780         
44781         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44782         return isNaN(value) ? '' : value;
44783         
44784     },
44785     
44786     fixPrecision : function(value)
44787     {
44788         if(this.thousandsDelimiter) {
44789             value += "";
44790             r = new RegExp(",", "g");
44791             value = value.replace(r, "");
44792         }
44793         
44794         var nan = isNaN(value);
44795         
44796         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44797             return nan ? '' : value;
44798         }
44799         return parseFloat(value).toFixed(this.decimalPrecision);
44800     },
44801     
44802     decimalPrecisionFcn : function(v)
44803     {
44804         return Math.floor(v);
44805     },
44806     
44807     validateValue : function(value)
44808     {
44809         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44810             return false;
44811         }
44812         
44813         var num = this.parseValue(value);
44814         
44815         if(isNaN(num)){
44816             this.markInvalid(String.format(this.nanText, value));
44817             return false;
44818         }
44819         
44820         if(num < this.minValue){
44821             this.markInvalid(String.format(this.minText, this.minValue));
44822             return false;
44823         }
44824         
44825         if(num > this.maxValue){
44826             this.markInvalid(String.format(this.maxText, this.maxValue));
44827             return false;
44828         }
44829         
44830         return true;
44831     },
44832     
44833     validate : function()
44834     {
44835         if(this.disabled || this.allowBlank){
44836             this.markValid();
44837             return true;
44838         }
44839         
44840         var currency = this.getCurrency();
44841         
44842         if(this.validateValue(this.getRawValue()) && currency.length){
44843             this.markValid();
44844             return true;
44845         }
44846         
44847         this.markInvalid();
44848         return false;
44849     },
44850     
44851     getName: function()
44852     {
44853         return this.name;
44854     },
44855     
44856     beforeBlur : function()
44857     {
44858         if(!this.castInt){
44859             return;
44860         }
44861         
44862         var v = this.parseValue(this.getRawValue());
44863         
44864         if(v || v == 0){
44865             this.setValue(v);
44866         }
44867     },
44868     
44869     onBlur : function()
44870     {
44871         this.beforeBlur();
44872         
44873         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44874             //this.el.removeClass(this.focusClass);
44875         }
44876         
44877         this.hasFocus = false;
44878         
44879         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44880             this.validate();
44881         }
44882         
44883         var v = this.getValue();
44884         
44885         if(String(v) !== String(this.startValue)){
44886             this.fireEvent('change', this, v, this.startValue);
44887         }
44888         
44889         this.fireEvent("blur", this);
44890     },
44891     
44892     inputEl : function()
44893     {
44894         return this.el.select('.roo-money-amount-input', true).first();
44895     },
44896     
44897     currencyEl : function()
44898     {
44899         return this.el.select('.roo-money-currency-input', true).first();
44900     },
44901     
44902     hiddenEl : function()
44903     {
44904         return this.el.select('input.hidden-number-input',true).first();
44905     }
44906     
44907 });/**
44908  * @class Roo.bootstrap.BezierSignature
44909  * @extends Roo.bootstrap.Component
44910  * Bootstrap BezierSignature class
44911  * This script refer to:
44912  *    Title: Signature Pad
44913  *    Author: szimek
44914  *    Availability: https://github.com/szimek/signature_pad
44915  *
44916  * @constructor
44917  * Create a new BezierSignature
44918  * @param {Object} config The config object
44919  */
44920
44921 Roo.bootstrap.BezierSignature = function(config){
44922     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44923     this.addEvents({
44924         "resize" : true
44925     });
44926 };
44927
44928 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44929 {
44930      
44931     curve_data: [],
44932     
44933     is_empty: true,
44934     
44935     mouse_btn_down: true,
44936     
44937     /**
44938      * @cfg {int} canvas height
44939      */
44940     canvas_height: '200px',
44941     
44942     /**
44943      * @cfg {float|function} Radius of a single dot.
44944      */ 
44945     dot_size: false,
44946     
44947     /**
44948      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44949      */
44950     min_width: 0.5,
44951     
44952     /**
44953      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44954      */
44955     max_width: 2.5,
44956     
44957     /**
44958      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44959      */
44960     throttle: 16,
44961     
44962     /**
44963      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44964      */
44965     min_distance: 5,
44966     
44967     /**
44968      * @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.
44969      */
44970     bg_color: 'rgba(0, 0, 0, 0)',
44971     
44972     /**
44973      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44974      */
44975     dot_color: 'black',
44976     
44977     /**
44978      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44979      */ 
44980     velocity_filter_weight: 0.7,
44981     
44982     /**
44983      * @cfg {function} Callback when stroke begin. 
44984      */
44985     onBegin: false,
44986     
44987     /**
44988      * @cfg {function} Callback when stroke end.
44989      */
44990     onEnd: false,
44991     
44992     getAutoCreate : function()
44993     {
44994         var cls = 'roo-signature column';
44995         
44996         if(this.cls){
44997             cls += ' ' + this.cls;
44998         }
44999         
45000         var col_sizes = [
45001             'lg',
45002             'md',
45003             'sm',
45004             'xs'
45005         ];
45006         
45007         for(var i = 0; i < col_sizes.length; i++) {
45008             if(this[col_sizes[i]]) {
45009                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45010             }
45011         }
45012         
45013         var cfg = {
45014             tag: 'div',
45015             cls: cls,
45016             cn: [
45017                 {
45018                     tag: 'div',
45019                     cls: 'roo-signature-body',
45020                     cn: [
45021                         {
45022                             tag: 'canvas',
45023                             cls: 'roo-signature-body-canvas',
45024                             height: this.canvas_height,
45025                             width: this.canvas_width
45026                         }
45027                     ]
45028                 },
45029                 {
45030                     tag: 'input',
45031                     type: 'file',
45032                     style: 'display: none'
45033                 }
45034             ]
45035         };
45036         
45037         return cfg;
45038     },
45039     
45040     initEvents: function() 
45041     {
45042         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45043         
45044         var canvas = this.canvasEl();
45045         
45046         // mouse && touch event swapping...
45047         canvas.dom.style.touchAction = 'none';
45048         canvas.dom.style.msTouchAction = 'none';
45049         
45050         this.mouse_btn_down = false;
45051         canvas.on('mousedown', this._handleMouseDown, this);
45052         canvas.on('mousemove', this._handleMouseMove, this);
45053         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45054         
45055         if (window.PointerEvent) {
45056             canvas.on('pointerdown', this._handleMouseDown, this);
45057             canvas.on('pointermove', this._handleMouseMove, this);
45058             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45059         }
45060         
45061         if ('ontouchstart' in window) {
45062             canvas.on('touchstart', this._handleTouchStart, this);
45063             canvas.on('touchmove', this._handleTouchMove, this);
45064             canvas.on('touchend', this._handleTouchEnd, this);
45065         }
45066         
45067         Roo.EventManager.onWindowResize(this.resize, this, true);
45068         
45069         // file input event
45070         this.fileEl().on('change', this.uploadImage, this);
45071         
45072         this.clear();
45073         
45074         this.resize();
45075     },
45076     
45077     resize: function(){
45078         
45079         var canvas = this.canvasEl().dom;
45080         var ctx = this.canvasElCtx();
45081         var img_data = false;
45082         
45083         if(canvas.width > 0) {
45084             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45085         }
45086         // setting canvas width will clean img data
45087         canvas.width = 0;
45088         
45089         var style = window.getComputedStyle ? 
45090             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45091             
45092         var padding_left = parseInt(style.paddingLeft) || 0;
45093         var padding_right = parseInt(style.paddingRight) || 0;
45094         
45095         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45096         
45097         if(img_data) {
45098             ctx.putImageData(img_data, 0, 0);
45099         }
45100     },
45101     
45102     _handleMouseDown: function(e)
45103     {
45104         if (e.browserEvent.which === 1) {
45105             this.mouse_btn_down = true;
45106             this.strokeBegin(e);
45107         }
45108     },
45109     
45110     _handleMouseMove: function (e)
45111     {
45112         if (this.mouse_btn_down) {
45113             this.strokeMoveUpdate(e);
45114         }
45115     },
45116     
45117     _handleMouseUp: function (e)
45118     {
45119         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45120             this.mouse_btn_down = false;
45121             this.strokeEnd(e);
45122         }
45123     },
45124     
45125     _handleTouchStart: function (e) {
45126         
45127         e.preventDefault();
45128         if (e.browserEvent.targetTouches.length === 1) {
45129             // var touch = e.browserEvent.changedTouches[0];
45130             // this.strokeBegin(touch);
45131             
45132              this.strokeBegin(e); // assume e catching the correct xy...
45133         }
45134     },
45135     
45136     _handleTouchMove: function (e) {
45137         e.preventDefault();
45138         // var touch = event.targetTouches[0];
45139         // _this._strokeMoveUpdate(touch);
45140         this.strokeMoveUpdate(e);
45141     },
45142     
45143     _handleTouchEnd: function (e) {
45144         var wasCanvasTouched = e.target === this.canvasEl().dom;
45145         if (wasCanvasTouched) {
45146             e.preventDefault();
45147             // var touch = event.changedTouches[0];
45148             // _this._strokeEnd(touch);
45149             this.strokeEnd(e);
45150         }
45151     },
45152     
45153     reset: function () {
45154         this._lastPoints = [];
45155         this._lastVelocity = 0;
45156         this._lastWidth = (this.min_width + this.max_width) / 2;
45157         this.canvasElCtx().fillStyle = this.dot_color;
45158     },
45159     
45160     strokeMoveUpdate: function(e)
45161     {
45162         this.strokeUpdate(e);
45163         
45164         if (this.throttle) {
45165             this.throttleStroke(this.strokeUpdate, this.throttle);
45166         }
45167         else {
45168             this.strokeUpdate(e);
45169         }
45170     },
45171     
45172     strokeBegin: function(e)
45173     {
45174         var newPointGroup = {
45175             color: this.dot_color,
45176             points: []
45177         };
45178         
45179         if (typeof this.onBegin === 'function') {
45180             this.onBegin(e);
45181         }
45182         
45183         this.curve_data.push(newPointGroup);
45184         this.reset();
45185         this.strokeUpdate(e);
45186     },
45187     
45188     strokeUpdate: function(e)
45189     {
45190         var rect = this.canvasEl().dom.getBoundingClientRect();
45191         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45192         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45193         var lastPoints = lastPointGroup.points;
45194         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45195         var isLastPointTooClose = lastPoint
45196             ? point.distanceTo(lastPoint) <= this.min_distance
45197             : false;
45198         var color = lastPointGroup.color;
45199         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45200             var curve = this.addPoint(point);
45201             if (!lastPoint) {
45202                 this.drawDot({color: color, point: point});
45203             }
45204             else if (curve) {
45205                 this.drawCurve({color: color, curve: curve});
45206             }
45207             lastPoints.push({
45208                 time: point.time,
45209                 x: point.x,
45210                 y: point.y
45211             });
45212         }
45213     },
45214     
45215     strokeEnd: function(e)
45216     {
45217         this.strokeUpdate(e);
45218         if (typeof this.onEnd === 'function') {
45219             this.onEnd(e);
45220         }
45221     },
45222     
45223     addPoint:  function (point) {
45224         var _lastPoints = this._lastPoints;
45225         _lastPoints.push(point);
45226         if (_lastPoints.length > 2) {
45227             if (_lastPoints.length === 3) {
45228                 _lastPoints.unshift(_lastPoints[0]);
45229             }
45230             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45231             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45232             _lastPoints.shift();
45233             return curve;
45234         }
45235         return null;
45236     },
45237     
45238     calculateCurveWidths: function (startPoint, endPoint) {
45239         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45240             (1 - this.velocity_filter_weight) * this._lastVelocity;
45241
45242         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45243         var widths = {
45244             end: newWidth,
45245             start: this._lastWidth
45246         };
45247         
45248         this._lastVelocity = velocity;
45249         this._lastWidth = newWidth;
45250         return widths;
45251     },
45252     
45253     drawDot: function (_a) {
45254         var color = _a.color, point = _a.point;
45255         var ctx = this.canvasElCtx();
45256         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45257         ctx.beginPath();
45258         this.drawCurveSegment(point.x, point.y, width);
45259         ctx.closePath();
45260         ctx.fillStyle = color;
45261         ctx.fill();
45262     },
45263     
45264     drawCurve: function (_a) {
45265         var color = _a.color, curve = _a.curve;
45266         var ctx = this.canvasElCtx();
45267         var widthDelta = curve.endWidth - curve.startWidth;
45268         var drawSteps = Math.floor(curve.length()) * 2;
45269         ctx.beginPath();
45270         ctx.fillStyle = color;
45271         for (var i = 0; i < drawSteps; i += 1) {
45272         var t = i / drawSteps;
45273         var tt = t * t;
45274         var ttt = tt * t;
45275         var u = 1 - t;
45276         var uu = u * u;
45277         var uuu = uu * u;
45278         var x = uuu * curve.startPoint.x;
45279         x += 3 * uu * t * curve.control1.x;
45280         x += 3 * u * tt * curve.control2.x;
45281         x += ttt * curve.endPoint.x;
45282         var y = uuu * curve.startPoint.y;
45283         y += 3 * uu * t * curve.control1.y;
45284         y += 3 * u * tt * curve.control2.y;
45285         y += ttt * curve.endPoint.y;
45286         var width = curve.startWidth + ttt * widthDelta;
45287         this.drawCurveSegment(x, y, width);
45288         }
45289         ctx.closePath();
45290         ctx.fill();
45291     },
45292     
45293     drawCurveSegment: function (x, y, width) {
45294         var ctx = this.canvasElCtx();
45295         ctx.moveTo(x, y);
45296         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45297         this.is_empty = false;
45298     },
45299     
45300     clear: function()
45301     {
45302         var ctx = this.canvasElCtx();
45303         var canvas = this.canvasEl().dom;
45304         ctx.fillStyle = this.bg_color;
45305         ctx.clearRect(0, 0, canvas.width, canvas.height);
45306         ctx.fillRect(0, 0, canvas.width, canvas.height);
45307         this.curve_data = [];
45308         this.reset();
45309         this.is_empty = true;
45310     },
45311     
45312     fileEl: function()
45313     {
45314         return  this.el.select('input',true).first();
45315     },
45316     
45317     canvasEl: function()
45318     {
45319         return this.el.select('canvas',true).first();
45320     },
45321     
45322     canvasElCtx: function()
45323     {
45324         return this.el.select('canvas',true).first().dom.getContext('2d');
45325     },
45326     
45327     getImage: function(type)
45328     {
45329         if(this.is_empty) {
45330             return false;
45331         }
45332         
45333         // encryption ?
45334         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45335     },
45336     
45337     drawFromImage: function(img_src)
45338     {
45339         var img = new Image();
45340         
45341         img.onload = function(){
45342             this.canvasElCtx().drawImage(img, 0, 0);
45343         }.bind(this);
45344         
45345         img.src = img_src;
45346         
45347         this.is_empty = false;
45348     },
45349     
45350     selectImage: function()
45351     {
45352         this.fileEl().dom.click();
45353     },
45354     
45355     uploadImage: function(e)
45356     {
45357         var reader = new FileReader();
45358         
45359         reader.onload = function(e){
45360             var img = new Image();
45361             img.onload = function(){
45362                 this.reset();
45363                 this.canvasElCtx().drawImage(img, 0, 0);
45364             }.bind(this);
45365             img.src = e.target.result;
45366         }.bind(this);
45367         
45368         reader.readAsDataURL(e.target.files[0]);
45369     },
45370     
45371     // Bezier Point Constructor
45372     Point: (function () {
45373         function Point(x, y, time) {
45374             this.x = x;
45375             this.y = y;
45376             this.time = time || Date.now();
45377         }
45378         Point.prototype.distanceTo = function (start) {
45379             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45380         };
45381         Point.prototype.equals = function (other) {
45382             return this.x === other.x && this.y === other.y && this.time === other.time;
45383         };
45384         Point.prototype.velocityFrom = function (start) {
45385             return this.time !== start.time
45386             ? this.distanceTo(start) / (this.time - start.time)
45387             : 0;
45388         };
45389         return Point;
45390     }()),
45391     
45392     
45393     // Bezier Constructor
45394     Bezier: (function () {
45395         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45396             this.startPoint = startPoint;
45397             this.control2 = control2;
45398             this.control1 = control1;
45399             this.endPoint = endPoint;
45400             this.startWidth = startWidth;
45401             this.endWidth = endWidth;
45402         }
45403         Bezier.fromPoints = function (points, widths, scope) {
45404             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45405             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45406             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45407         };
45408         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45409             var dx1 = s1.x - s2.x;
45410             var dy1 = s1.y - s2.y;
45411             var dx2 = s2.x - s3.x;
45412             var dy2 = s2.y - s3.y;
45413             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45414             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45415             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45416             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45417             var dxm = m1.x - m2.x;
45418             var dym = m1.y - m2.y;
45419             var k = l2 / (l1 + l2);
45420             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45421             var tx = s2.x - cm.x;
45422             var ty = s2.y - cm.y;
45423             return {
45424                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45425                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45426             };
45427         };
45428         Bezier.prototype.length = function () {
45429             var steps = 10;
45430             var length = 0;
45431             var px;
45432             var py;
45433             for (var i = 0; i <= steps; i += 1) {
45434                 var t = i / steps;
45435                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45436                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45437                 if (i > 0) {
45438                     var xdiff = cx - px;
45439                     var ydiff = cy - py;
45440                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45441                 }
45442                 px = cx;
45443                 py = cy;
45444             }
45445             return length;
45446         };
45447         Bezier.prototype.point = function (t, start, c1, c2, end) {
45448             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45449             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45450             + (3.0 * c2 * (1.0 - t) * t * t)
45451             + (end * t * t * t);
45452         };
45453         return Bezier;
45454     }()),
45455     
45456     throttleStroke: function(fn, wait) {
45457       if (wait === void 0) { wait = 250; }
45458       var previous = 0;
45459       var timeout = null;
45460       var result;
45461       var storedContext;
45462       var storedArgs;
45463       var later = function () {
45464           previous = Date.now();
45465           timeout = null;
45466           result = fn.apply(storedContext, storedArgs);
45467           if (!timeout) {
45468               storedContext = null;
45469               storedArgs = [];
45470           }
45471       };
45472       return function wrapper() {
45473           var args = [];
45474           for (var _i = 0; _i < arguments.length; _i++) {
45475               args[_i] = arguments[_i];
45476           }
45477           var now = Date.now();
45478           var remaining = wait - (now - previous);
45479           storedContext = this;
45480           storedArgs = args;
45481           if (remaining <= 0 || remaining > wait) {
45482               if (timeout) {
45483                   clearTimeout(timeout);
45484                   timeout = null;
45485               }
45486               previous = now;
45487               result = fn.apply(storedContext, storedArgs);
45488               if (!timeout) {
45489                   storedContext = null;
45490                   storedArgs = [];
45491               }
45492           }
45493           else if (!timeout) {
45494               timeout = window.setTimeout(later, remaining);
45495           }
45496           return result;
45497       };
45498   }
45499   
45500 });
45501
45502  
45503
45504